// Runs the real filter on a real Fcitx5 candidate list. // g++ -std=c++20 -Isrc tests/blockfilter_test.cpp -o /tmp/blockfilter_test // (add the flags from: pkg-config --cflags --libs Fcitx5Core) #include #include #include #include #include #include "blockfilter.h" #include "fcitx/candidatelist.h" #include "fcitx/text.h" using namespace fcitx; static int failures = 0; #define CHECK(cond) \ do { \ if (!(cond)) { \ std::printf("FAIL line %d: %s\n", __LINE__, #cond); \ failures++; \ } \ } while (0) static std::unique_ptr makeList(const std::vector &words, int pageSize) { auto list = std::make_unique(); list->setPageSize(pageSize); for (const auto &w : words) { list->append(Text(w)); } return list; } static std::vector shown(CommonCandidateList &list) { std::vector out; for (int i = 0; i < list.totalSize(); i++) { out.push_back(list.candidateFromAll(i).text().toString()); } return out; } int main() { const std::unordered_set blocked = {"我当时就", "你不能"}; auto isBlocked = [&](const std::string &w) { return blocked.contains(w); }; { // the filter is reachable through the interface the window uses auto list = makeList({"我", "我当时就", "你不能", "你"}, 5); CandidateList *asBase = list.get(); CHECK(asBase->toModifiable() != nullptr); CHECK(fcitx::m3ui::removeBlockedCandidates(*asBase, isBlocked) == 2); CHECK((shown(*list) == std::vector{"我", "你"})); } { // nothing blocked, nothing touched auto list = makeList({"a", "b", "c"}, 5); CHECK(fcitx::m3ui::removeBlockedCandidates(*list, isBlocked) == 0); CHECK(list->totalSize() == 3); } { // every candidate blocked leaves an empty but valid list auto list = makeList({"我当时就", "你不能"}, 5); CHECK(fcitx::m3ui::removeBlockedCandidates(*list, isBlocked) == 2); CHECK(list->totalSize() == 0); CHECK(list->size() == 0); } { // the same word more than once is removed everywhere auto list = makeList({"你不能", "好", "你不能", "你不能"}, 5); CHECK(fcitx::m3ui::removeBlockedCandidates(*list, isBlocked) == 3); CHECK((shown(*list) == std::vector{"好"})); } { // over several pages the page contents and labels stay consistent std::vector words; for (int i = 0; i < 12; i++) { words.push_back(i % 4 == 1 ? "你不能" : "词" + std::to_string(i)); } auto list = makeList(words, 5); const int before = list->totalSize(); const int removed = fcitx::m3ui::removeBlockedCandidates(*list, isBlocked); CHECK(removed == 3 && list->totalSize() == before - removed); for (int i = 0; i < list->totalSize(); i++) { CHECK(list->candidateFromAll(i).text().toString() != "你不能"); } // the first page is full again, with candidates from the remaining ones CHECK(list->size() == 5); CHECK(list->totalPages() == (list->totalSize() + 4) / 5); CHECK(list->candidate(0).text().toString() == "词0"); } { // the highlight stays on the same candidate wherever the removal was for (int cursor = 0; cursor < 5; cursor++) { auto list = makeList({"a", "你不能", "b", "我当时就", "c"}, 5); list->setGlobalCursorIndex(cursor); const std::string before = list->candidateFromAll(cursor).text().toString(); fcitx::m3ui::removeBlockedCandidates(*list, isBlocked); const std::string after = list->candidateFromAll(list->globalCursorIndex()).text().toString(); if (before == "你不能" || before == "我当时就") { // the one under the cursor is gone, the cursor is still valid CHECK(list->globalCursorIndex() >= 0 && list->globalCursorIndex() < list->totalSize()); } else { CHECK(after == before); } } } { // removing the last candidate while the cursor is on it auto list = makeList({"a", "b", "你不能"}, 5); list->setGlobalCursorIndex(2); fcitx::m3ui::removeBlockedCandidates(*list, isBlocked); CHECK(list->globalCursorIndex() == 1); } { // a list the cursor does not apply to is still filtered DisplayOnlyCandidateList list; list.setLayoutHint(CandidateLayoutHint::Vertical); CHECK(list.toModifiable() == nullptr); // cannot be modified, left alone CHECK(fcitx::m3ui::removeBlockedCandidates(list, isBlocked) == 0); } std::printf(failures ? "%d check(s) failed\n" : "blockfilter: all checks passed\n", failures); return failures ? 1 : 0; }