#include "fzf.h" #include #include #include typedef enum { ScoreMatch = 16, ScoreGapStart = -3, ScoreGapExtension = -1, BonusBoundary = ScoreMatch / 2, BonusNonWord = ScoreMatch / 2, BonusCamel123 = BonusBoundary + ScoreGapExtension, BonusConsecutive = -(ScoreGapStart + ScoreGapExtension), BonusFirstCharMultiplier = 2, } score_t; #define call_alg(alg, case, txt, pat, assert_block) \ { \ fzf_position_t *pos = fzf_pos_array(0); \ fzf_result_t res = alg(case, false, txt, pat, pos, NULL); \ assert_block; \ fzf_free_positions(pos); \ } \ { \ fzf_position_t *pos = fzf_pos_array(0); \ fzf_slab_t *slab = fzf_make_default_slab(); \ fzf_result_t res = alg(case, false, txt, pat, pos, slab); \ assert_block; \ fzf_free_positions(pos); \ fzf_free_slab(slab); \ } static int8_t max_i8(int8_t a, int8_t b) { return a > b ? a : b; } #define MATCH_WRAPPER(nn, og) \ fzf_result_t nn(bool case_sensitive, bool normalize, const char *text, \ const char *pattern, fzf_position_t *pos, \ fzf_slab_t *slab) { \ fzf_string_t input = {.data = text, .size = strlen(text)}; \ fzf_string_t pattern_wrap = {.data = pattern, .size = strlen(pattern)}; \ return og(case_sensitive, normalize, &input, &pattern_wrap, pos, slab); \ } MATCH_WRAPPER(fuzzy_match_v2, fzf_fuzzy_match_v2); MATCH_WRAPPER(fuzzy_match_v1, fzf_fuzzy_match_v1); MATCH_WRAPPER(exact_match_naive, fzf_exact_match_naive); MATCH_WRAPPER(prefix_match, fzf_prefix_match); MATCH_WRAPPER(suffix_match, fzf_suffix_match); MATCH_WRAPPER(equal_match, fzf_equal_match); // TODO(conni2461): Implement normalize and test it here TEST(FuzzyMatchV2, case1) { call_alg(fuzzy_match_v2, true, "So Danco Samba", "So", { ASSERT_EQ(0, res.start); ASSERT_EQ(2, res.end); ASSERT_EQ(56, res.score); ASSERT_EQ(2, pos->size); ASSERT_EQ(1, pos->data[0]); ASSERT_EQ(0, pos->data[1]); }); } TEST(FuzzyMatchV2, case2) { call_alg(fuzzy_match_v2, false, "So Danco Samba", "sodc", { ASSERT_EQ(0, res.start); ASSERT_EQ(7, res.end); ASSERT_EQ(89, res.score); ASSERT_EQ(4, pos->size); ASSERT_EQ(6, pos->data[0]); ASSERT_EQ(3, pos->data[1]); ASSERT_EQ(1, pos->data[2]); ASSERT_EQ(0, pos->data[3]); }); } TEST(FuzzyMatchV2, case3) { call_alg(fuzzy_match_v2, false, "Danco", "danco", { ASSERT_EQ(0, res.start); ASSERT_EQ(5, res.end); ASSERT_EQ(128, res.score); ASSERT_EQ(5, pos->size); ASSERT_EQ(4, pos->data[0]); ASSERT_EQ(3, pos->data[1]); ASSERT_EQ(2, pos->data[2]); ASSERT_EQ(1, pos->data[3]); ASSERT_EQ(0, pos->data[4]); }); } TEST(FuzzyMatchV2, case4) { call_alg(fuzzy_match_v2, false, "fooBarbaz1", "obz", { ASSERT_EQ(2, res.start); ASSERT_EQ(9, res.end); int expected_score = ScoreMatch * 3 + BonusCamel123 + ScoreGapStart + ScoreGapExtension * 3; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case5) { call_alg(fuzzy_match_v2, false, "foo bar baz", "fbb", { ASSERT_EQ(0, res.start); ASSERT_EQ(9, res.end); int expected_score = ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusBoundary * 2 + 2 * ScoreGapStart + 4 * ScoreGapExtension; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case6) { call_alg(fuzzy_match_v2, false, "/AutomatorDocument.icns", "rdoc", { ASSERT_EQ(9, res.start); ASSERT_EQ(13, res.end); int expected_score = ScoreMatch * 4 + BonusCamel123 + BonusConsecutive * 2; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case7) { call_alg(fuzzy_match_v2, false, "/man1/zshcompctl.1", "zshc", { ASSERT_EQ(6, res.start); ASSERT_EQ(10, res.end); int expected_score = ScoreMatch * 4 + BonusBoundary * BonusFirstCharMultiplier + BonusBoundary * 3; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case8) { call_alg(fuzzy_match_v2, false, "/.oh-my-zsh/cache", "zshc", { ASSERT_EQ(8, res.start); ASSERT_EQ(13, res.end); int expected_score = ScoreMatch * 4 + BonusBoundary * BonusFirstCharMultiplier + BonusBoundary * 3 + ScoreGapStart; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case9) { call_alg(fuzzy_match_v2, false, "ab0123 456", "12356", { ASSERT_EQ(3, res.start); ASSERT_EQ(10, res.end); int expected_score = ScoreMatch * 5 + BonusConsecutive * 3 + ScoreGapStart + ScoreGapExtension; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case10) { call_alg(fuzzy_match_v2, false, "abc123 456", "12356", { ASSERT_EQ(3, res.start); ASSERT_EQ(10, res.end); int expected_score = ScoreMatch * 5 + BonusCamel123 * BonusFirstCharMultiplier + BonusCamel123 * 2 + BonusConsecutive + ScoreGapStart + ScoreGapExtension; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case11) { call_alg(fuzzy_match_v2, false, "foo/bar/baz", "fbb", { ASSERT_EQ(0, res.start); ASSERT_EQ(9, res.end); int expected_score = ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusBoundary * 2 + 2 * ScoreGapStart + 4 * ScoreGapExtension; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case12) { call_alg(fuzzy_match_v2, false, "fooBarBaz", "fbb", { ASSERT_EQ(0, res.start); ASSERT_EQ(7, res.end); int expected_score = ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusCamel123 * 2 + 2 * ScoreGapStart + 2 * ScoreGapExtension; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case13) { call_alg(fuzzy_match_v2, false, "foo barbaz", "fbb", { ASSERT_EQ(0, res.start); ASSERT_EQ(8, res.end); int expected_score = ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusBoundary + ScoreGapStart * 2 + ScoreGapExtension * 3; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case14) { call_alg(fuzzy_match_v2, false, "fooBar Baz", "foob", { ASSERT_EQ(0, res.start); ASSERT_EQ(4, res.end); int expected_score = ScoreMatch * 4 + BonusBoundary * BonusFirstCharMultiplier + BonusBoundary * 3; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case15) { call_alg(fuzzy_match_v2, false, "xFoo-Bar Baz", "foo-b", { ASSERT_EQ(1, res.start); ASSERT_EQ(6, res.end); int expected_score = ScoreMatch * 5 + BonusCamel123 * BonusFirstCharMultiplier + BonusCamel123 * 2 + BonusNonWord + BonusBoundary; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case16) { call_alg(fuzzy_match_v2, true, "fooBarbaz", "oBz", { ASSERT_EQ(2, res.start); ASSERT_EQ(9, res.end); int expected_score = ScoreMatch * 3 + BonusCamel123 + ScoreGapStart + ScoreGapExtension * 3; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case17) { call_alg(fuzzy_match_v2, true, "Foo/Bar/Baz", "FBB", { ASSERT_EQ(0, res.start); ASSERT_EQ(9, res.end); int expected_score = ScoreMatch * 3 + BonusBoundary * (BonusFirstCharMultiplier + 2) + ScoreGapStart * 2 + ScoreGapExtension * 4; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case18) { call_alg(fuzzy_match_v2, true, "FooBarBaz", "FBB", { ASSERT_EQ(0, res.start); ASSERT_EQ(7, res.end); int expected_score = ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier + BonusCamel123 * 2 + ScoreGapStart * 2 + ScoreGapExtension * 2; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case19) { call_alg(fuzzy_match_v2, true, "FooBar Baz", "FooB", { ASSERT_EQ(0, res.start); ASSERT_EQ(4, res.end); int expected_score = ScoreMatch * 4 + BonusBoundary * BonusFirstCharMultiplier + BonusBoundary * 2 + max_i8(BonusCamel123, BonusBoundary); ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case20) { call_alg(fuzzy_match_v2, true, "foo-bar", "o-ba", { ASSERT_EQ(2, res.start); ASSERT_EQ(6, res.end); int expected_score = ScoreMatch * 4 + BonusBoundary * 3; ASSERT_EQ(expected_score, res.score); }); } TEST(FuzzyMatchV2, case21) { call_alg(fuzzy_match_v2, true, "fooBarbaz", "oBZ", { ASSERT_EQ(-1, res.start); ASSERT_EQ(-1, res.end); ASSERT_EQ(0, res.score); }); } TEST(FuzzyMatchV2, case22) { call_alg(fuzzy_match_v2, true, "Foo Bar Baz", "fbb", { ASSERT_EQ(-1, res.start); ASSERT_EQ(-1, res.end); ASSERT_EQ(0, res.score); }); } TEST(FuzzyMatchV2, case23) { call_alg(fuzzy_match_v2, true, "fooBarbaz", "fooBarbazz", { ASSERT_EQ(-1, res.start); ASSERT_EQ(-1, res.end); ASSERT_EQ(0, res.score); }); } TEST(FuzzyMatchV1, case1) { call_alg(fuzzy_match_v1, true, "So Danco Samba", "So", { ASSERT_EQ(0, res.start); ASSERT_EQ(2, res.end); ASSERT_EQ(56, res.score); ASSERT_EQ(2, pos->size); ASSERT_EQ(0, pos->data[0]); ASSERT_EQ(1, pos->data[1]); }); } TEST(FuzzyMatchV1, case2) { call_alg(fuzzy_match_v1, false, "So Danco Samba", "sodc", { ASSERT_EQ(0, res.start); ASSERT_EQ(7, res.end); ASSERT_EQ(89, res.score); ASSERT_EQ(4, pos->size); ASSERT_EQ(0, pos->data[0]); ASSERT_EQ(1, pos->data[1]); ASSERT_EQ(3, pos->data[2]); ASSERT_EQ(6, pos->data[3]); }); } TEST(FuzzyMatchV1, case3) { call_alg(fuzzy_match_v1, false, "Danco", "danco", { ASSERT_EQ(0, res.start); ASSERT_EQ(5, res.end); ASSERT_EQ(128, res.score); ASSERT_EQ(5, pos->size); ASSERT_EQ(0, pos->data[0]); ASSERT_EQ(1, pos->data[1]); ASSERT_EQ(2, pos->data[2]); ASSERT_EQ(3, pos->data[3]); ASSERT_EQ(4, pos->data[4]); }); } TEST(ExactMatch, case1) { call_alg(exact_match_naive, true, "So Danco Samba", "So", { ASSERT_EQ(0, res.start); ASSERT_EQ(2, res.end); ASSERT_EQ(56, res.score); }); } TEST(ExactMatch, case2) { call_alg(exact_match_naive, false, "So Danco Samba", "sodc", { ASSERT_EQ(-1, res.start); ASSERT_EQ(-1, res.end); ASSERT_EQ(0, res.score); }); } TEST(ExactMatch, case3) { call_alg(exact_match_naive, false, "Danco", "danco", { ASSERT_EQ(0, res.start); ASSERT_EQ(5, res.end); ASSERT_EQ(128, res.score); }); } TEST(PrefixMatch, case1) { call_alg(prefix_match, true, "So Danco Samba", "So", { ASSERT_EQ(0, res.start); ASSERT_EQ(2, res.end); ASSERT_EQ(56, res.score); }); } TEST(PrefixMatch, case2) { call_alg(prefix_match, false, "So Danco Samba", "sodc", { ASSERT_EQ(-1, res.start); ASSERT_EQ(-1, res.end); ASSERT_EQ(0, res.score); }); } TEST(PrefixMatch, case3) { call_alg(prefix_match, false, "Danco", "danco", { ASSERT_EQ(0, res.start); ASSERT_EQ(5, res.end); ASSERT_EQ(128, res.score); }); } TEST(SuffixMatch, case1) { call_alg(suffix_match, true, "So Danco Samba", "So", { ASSERT_EQ(-1, res.start); ASSERT_EQ(-1, res.end); ASSERT_EQ(0, res.score); }); } TEST(SuffixMatch, case2) { call_alg(suffix_match, false, "So Danco Samba", "sodc", { ASSERT_EQ(-1, res.start); ASSERT_EQ(-1, res.end); ASSERT_EQ(0, res.score); }); } TEST(SuffixMatch, case3) { call_alg(suffix_match, false, "Danco", "danco", { ASSERT_EQ(0, res.start); ASSERT_EQ(5, res.end); ASSERT_EQ(128, res.score); }); } TEST(EqualMatch, case1) { call_alg(equal_match, true, "So Danco Samba", "So", { ASSERT_EQ(-1, res.start); ASSERT_EQ(-1, res.end); ASSERT_EQ(0, res.score); }); } TEST(EqualMatch, case2) { call_alg(equal_match, false, "So Danco Samba", "sodc", { ASSERT_EQ(-1, res.start); ASSERT_EQ(-1, res.end); ASSERT_EQ(0, res.score); }); } TEST(EqualMatch, case3) { call_alg(equal_match, false, "Danco", "danco", { ASSERT_EQ(0, res.start); ASSERT_EQ(5, res.end); ASSERT_EQ(128, res.score); }); } TEST(PatternParsing, empty) { fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "", true); ASSERT_EQ(0, pat->size); ASSERT_EQ(0, pat->cap); ASSERT_FALSE(pat->only_inv); fzf_free_pattern(pat); } TEST(PatternParsing, simple) { fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "lua", true); ASSERT_EQ(1, pat->size); ASSERT_EQ(1, pat->cap); ASSERT_FALSE(pat->only_inv); ASSERT_EQ(1, pat->ptr[0]->size); ASSERT_EQ(1, pat->ptr[0]->cap); ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn); ASSERT_EQ("lua", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); fzf_free_pattern(pat); } TEST(PatternParsing, withEscapedSpace) { fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "file\\ ", true); ASSERT_EQ(1, pat->size); ASSERT_EQ(1, pat->cap); ASSERT_FALSE(pat->only_inv); ASSERT_EQ(1, pat->ptr[0]->size); ASSERT_EQ(1, pat->ptr[0]->cap); ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn); ASSERT_EQ("file ", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); fzf_free_pattern(pat); } TEST(PatternParsing, withComplexEscapedSpace) { fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "file\\ with\\ space", true); ASSERT_EQ(1, pat->size); ASSERT_EQ(1, pat->cap); ASSERT_FALSE(pat->only_inv); ASSERT_EQ(1, pat->ptr[0]->size); ASSERT_EQ(1, pat->ptr[0]->cap); ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn); ASSERT_EQ("file with space", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); fzf_free_pattern(pat); } TEST(PatternParsing, withEscapedSpaceAndNormalSpace) { fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "file\\ new", true); ASSERT_EQ(2, pat->size); ASSERT_EQ(2, pat->cap); ASSERT_FALSE(pat->only_inv); ASSERT_EQ(1, pat->ptr[0]->size); ASSERT_EQ(1, pat->ptr[0]->cap); ASSERT_EQ(1, pat->ptr[1]->size); ASSERT_EQ(1, pat->ptr[1]->cap); ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn); ASSERT_EQ("file ", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[1]->ptr[0].fn); ASSERT_EQ("new", ((fzf_string_t *)(pat->ptr[1]->ptr[0].text))->data); ASSERT_FALSE(pat->ptr[1]->ptr[0].case_sensitive); fzf_free_pattern(pat); } TEST(PatternParsing, invert) { fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "!Lua", true); ASSERT_EQ(1, pat->size); ASSERT_EQ(1, pat->cap); ASSERT_TRUE(pat->only_inv); ASSERT_EQ(1, pat->ptr[0]->size); ASSERT_EQ(1, pat->ptr[0]->cap); ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[0]->ptr[0].fn); ASSERT_EQ("Lua", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); ASSERT_TRUE(pat->ptr[0]->ptr[0].case_sensitive); ASSERT_TRUE(pat->ptr[0]->ptr[0].inv); fzf_free_pattern(pat); } TEST(PatternParsing, invertMultiple) { fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "!fzf !test", true); ASSERT_EQ(2, pat->size); ASSERT_EQ(2, pat->cap); ASSERT_TRUE(pat->only_inv); ASSERT_EQ(1, pat->ptr[0]->size); ASSERT_EQ(1, pat->ptr[0]->cap); ASSERT_EQ(1, pat->ptr[1]->size); ASSERT_EQ(1, pat->ptr[1]->cap); ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[0]->ptr[0].fn); ASSERT_EQ("fzf", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); ASSERT_TRUE(pat->ptr[0]->ptr[0].inv); ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[1]->ptr[0].fn); ASSERT_EQ("test", ((fzf_string_t *)(pat->ptr[1]->ptr[0].text))->data); ASSERT_FALSE(pat->ptr[1]->ptr[0].case_sensitive); ASSERT_TRUE(pat->ptr[1]->ptr[0].inv); fzf_free_pattern(pat); } TEST(PatternParsing, smartCase) { fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "Lua", true); ASSERT_EQ(1, pat->size); ASSERT_EQ(1, pat->cap); ASSERT_FALSE(pat->only_inv); ASSERT_EQ(1, pat->ptr[0]->size); ASSERT_EQ(1, pat->ptr[0]->cap); ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn); ASSERT_EQ("Lua", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); ASSERT_TRUE(pat->ptr[0]->ptr[0].case_sensitive); fzf_free_pattern(pat); } TEST(PatternParsing, simpleOr) { fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "'src | ^Lua", true); ASSERT_EQ(1, pat->size); ASSERT_EQ(1, pat->cap); ASSERT_FALSE(pat->only_inv); ASSERT_EQ(2, pat->ptr[0]->size); ASSERT_EQ(2, pat->ptr[0]->cap); ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[0]->ptr[0].fn); ASSERT_EQ("src", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); ASSERT_EQ((void *)fzf_prefix_match, pat->ptr[0]->ptr[1].fn); ASSERT_EQ("Lua", ((fzf_string_t *)(pat->ptr[0]->ptr[1].text))->data); ASSERT_TRUE(pat->ptr[0]->ptr[1].case_sensitive); fzf_free_pattern(pat); } TEST(PatternParsing, complexAnd) { fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, ".lua$ 'previewer !'term !asdf", true); ASSERT_EQ(4, pat->size); ASSERT_EQ(4, pat->cap); ASSERT_FALSE(pat->only_inv); ASSERT_EQ(1, pat->ptr[0]->size); ASSERT_EQ(1, pat->ptr[0]->cap); ASSERT_EQ(1, pat->ptr[1]->size); ASSERT_EQ(1, pat->ptr[1]->cap); ASSERT_EQ(1, pat->ptr[2]->size); ASSERT_EQ(1, pat->ptr[2]->cap); ASSERT_EQ(1, pat->ptr[3]->size); ASSERT_EQ(1, pat->ptr[3]->cap); ASSERT_EQ((void *)fzf_suffix_match, pat->ptr[0]->ptr[0].fn); ASSERT_EQ(".lua", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data); ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive); ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[1]->ptr[0].fn); ASSERT_EQ("previewer", ((fzf_string_t *)(pat->ptr[1]->ptr[0].text))->data); ASSERT_EQ(0, pat->ptr[1]->ptr[0].case_sensitive); ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[2]->ptr[0].fn); ASSERT_EQ("term", ((fzf_string_t *)(pat->ptr[2]->ptr[0].text))->data); ASSERT_FALSE(pat->ptr[2]->ptr[0].case_sensitive); ASSERT_TRUE(pat->ptr[2]->ptr[0].inv); ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[3]->ptr[0].fn); ASSERT_EQ("asdf", ((fzf_string_t *)(pat->ptr[3]->ptr[0].text))->data); ASSERT_FALSE(pat->ptr[3]->ptr[0].case_sensitive); ASSERT_TRUE(pat->ptr[3]->ptr[0].inv); fzf_free_pattern(pat); } static void score_wrapper(char *pattern, char **input, int *expected) { fzf_slab_t *slab = fzf_make_default_slab(); fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, pattern, true); for (size_t i = 0; input[i] != NULL; ++i) { ASSERT_EQ(expected[i], fzf_get_score(input[i], pat, slab)); } fzf_free_pattern(pat); fzf_free_slab(slab); } TEST(ScoreIntegration, simple) { char *input[] = {"fzf", "main.c", "src/fzf", "fz/noooo", NULL}; int expected[] = {0, 1, 0, 1}; score_wrapper("!fzf", input, expected); } TEST(ScoreIntegration, invertAnd) { char *input[] = {"src/fzf.c", "README.md", "lua/asdf", "test/test.c", NULL}; int expected[] = {0, 1, 1, 0}; score_wrapper("!fzf !test", input, expected); } TEST(ScoreIntegration, withEscapedSpace) { char *input[] = {"file ", "file lua", "lua", NULL}; int expected[] = {0, 200, 0}; score_wrapper("file\\ lua", input, expected); } TEST(ScoreIntegration, onlyEscapedSpace) { char *input[] = {"file with space", "file lua", "lua", "src", "test", NULL}; int expected[] = {32, 32, 0, 0, 0}; score_wrapper("\\ ", input, expected); } TEST(ScoreIntegration, simpleOr) { char *input[] = {"src/fzf.h", "README.md", "build/fzf", "lua/fzf_lib.lua", "Lua/fzf_lib.lua", NULL}; int expected[] = {80, 0, 0, 0, 80}; score_wrapper("'src | ^Lua", input, expected); } TEST(ScoreIntegration, complexTerm) { char *input[] = {"lua/random_previewer", "README.md", "previewers/utils.lua", "previewers/buffer.lua", "previewers/term.lua", NULL}; int expected[] = {0, 0, 328, 328, 0}; score_wrapper(".lua$ 'previewer !'term", input, expected); } static void pos_wrapper(char *pattern, char **input, int **expected) { fzf_slab_t *slab = fzf_make_default_slab(); fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, pattern, true); for (size_t i = 0; input[i] != NULL; ++i) { fzf_position_t *pos = fzf_get_positions(input[i], pat, slab); if (!pos) { ASSERT_EQ((void *)pos, expected[i]); continue; } // Verify that the size is correct if (expected[i]) { ASSERT_EQ(-1, expected[i][pos->size]); } else { ASSERT_EQ(0, pos->size); } ASSERT_EQ_MEM(expected[i], pos->data, pos->size * sizeof(pos->data[0])); fzf_free_positions(pos); } fzf_free_pattern(pat); fzf_free_slab(slab); } TEST(PosIntegration, simple) { char *input[] = {"src/fzf.c", "src/fzf.h", "lua/fzf_lib.lua", "lua/telescope/_extensions/fzf.lua", "README.md", NULL}; int match1[] = {6, 5, 4, -1}; int match2[] = {6, 5, 4, -1}; int match3[] = {6, 5, 4, -1}; int match4[] = {28, 27, 26, -1}; int *expected[] = {match1, match2, match3, match4, NULL}; pos_wrapper("fzf", input, expected); } TEST(PosIntegration, invert) { char *input[] = {"fzf", "main.c", "src/fzf", "fz/noooo", NULL}; int *expected[] = {NULL, NULL, NULL, NULL, NULL}; pos_wrapper("!fzf", input, expected); } TEST(PosIntegration, andWithSecondInvert) { char *input[] = {"src/fzf.c", "lua/fzf_lib.lua", "build/libfzf", NULL}; int match1[] = {6, 5, 4, -1}; int *expected[] = {match1, NULL, NULL}; pos_wrapper("fzf !lib", input, expected); } TEST(PosIntegration, andAllInvert) { char *input[] = {"src/fzf.c", "README.md", "lua/asdf", "test/test.c", NULL}; int *expected[] = {NULL, NULL, NULL, NULL}; pos_wrapper("!fzf !test", input, expected); } TEST(PosIntegration, withEscapedSpace) { char *input[] = {"file ", "file lua", "lua", NULL}; int match1[] = {7, 6, 5, 4, 3, 2, 1, 0, -1}; int *expected[] = {NULL, match1, NULL}; pos_wrapper("file\\ lua", input, expected); } TEST(PosIntegration, onlyEscapedSpace) { char *input[] = {"file with space", "lul lua", "lua", "src", "test", NULL}; int match1[] = {4, -1}; int match2[] = {3, -1}; int *expected[] = {match1, match2, NULL, NULL, NULL}; pos_wrapper("\\ ", input, expected); } TEST(PosIntegration, simpleOr) { char *input[] = {"src/fzf.h", "README.md", "build/fzf", "lua/fzf_lib.lua", "Lua/fzf_lib.lua", NULL}; int match1[] = {0, 1, 2, -1}; int match2[] = {0, 1, 2, -1}; int *expected[] = {match1, NULL, NULL, NULL, match2}; pos_wrapper("'src | ^Lua", input, expected); } TEST(PosIntegration, orMemLeak) { char *input[] = {"src/fzf.h", NULL}; int match1[] = {2, 1, 0, -1}; int *expected[] = {match1}; pos_wrapper("src | src", input, expected); } TEST(PosIntegration, complexTerm) { char *input[] = {"lua/random_previewer", "README.md", "previewers/utils.lua", "previewers/buffer.lua", "previewers/term.lua", NULL}; int match1[] = {16, 17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1}; int match2[] = {17, 18, 19, 20, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1}; int *expected[] = {NULL, NULL, match1, match2, NULL}; pos_wrapper(".lua$ 'previewer !'term", input, expected); } int main(int argc, char **argv) { exam_init(argc, argv); return exam_run(); }