src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/edit/filter/compile_test.go (about) 1 package filter_test 2 3 import ( 4 "testing" 5 6 "src.elv.sh/pkg/edit/filter" 7 "src.elv.sh/pkg/parse" 8 ) 9 10 func TestCompile(t *testing.T) { 11 test(t, 12 That("empty filter matches anything"). 13 Filter("").Matches("foo", "bar", " ", ""), 14 15 That("bareword matches any string containing it"). 16 Filter("foo").Matches("foobar", "afoo").DoesNotMatch("", "faoo"), 17 That("bareword is case-insensitive is filter is all lower case"). 18 Filter("foo").Matches("FOO", "Foo", "FOObar").DoesNotMatch("", "faoo"), 19 That("bareword is case-sensitive is filter is not all lower case"). 20 Filter("Foo").Matches("Foobar").DoesNotMatch("foo", "FOO"), 21 22 That("double quoted string works like bareword"). 23 Filter(`"foo"`).Matches("FOO", "Foo", "FOObar").DoesNotMatch("", "faoo"), 24 25 That("single quoted string works like bareword"). 26 Filter(`'foo'`).Matches("FOO", "Foo", "FOObar").DoesNotMatch("", "faoo"), 27 28 That("space-separated words work like an AND filter"). 29 Filter("foo bar"). 30 Matches("foobar", "bar foo", "foo lorem ipsum bar"). 31 DoesNotMatch("foo", "bar", ""), 32 33 That("quoted string can be used when string contains spaces"). 34 Filter(`"foo bar"`). 35 Matches("__foo bar xyz"). 36 DoesNotMatch("foobar"), 37 38 That("AND filter matches if all components match"). 39 Filter("[and foo bar]").Matches("foobar", "bar foo").DoesNotMatch("foo"), 40 That("OR filter matches if any component matches"). 41 Filter("[or foo bar]").Matches("foo", "bar", "foobar").DoesNotMatch(""), 42 That("RE filter uses component as regular expression to match"). 43 Filter("[re f..]").Matches("foo", "f..").DoesNotMatch("fo", ""), 44 45 // Invalid queries 46 That("empty list is invalid"). 47 Filter("[]").DoesNotCompile("empty subfilter"), 48 That("starting list with non-literal is invalid"). 49 Filter("[[foo] bar]"). 50 DoesNotCompile("non-literal subfilter head not supported"), 51 That("RE filter with no argument is invalid"). 52 Filter("[re]"). 53 DoesNotCompile("re subfilter with no argument not supported"), 54 That("RE filter with two or more arguments is invalid"). 55 Filter("[re foo bar]"). 56 DoesNotCompile("re subfilter with two or more arguments not supported"), 57 That("RE filter with invalid regular expression is invalid"). 58 Filter("[re '[']"). 59 DoesNotCompile("error parsing regexp: missing closing ]: `[`"), 60 That("invalid syntax results in parse error"). 61 Filter("[and").DoesNotParse("parse error: [filter]:1:5: should be ']'"), 62 63 // Unsupported for now, but may be in future 64 That("options are not supported yet"). 65 Filter("foo &k=v").DoesNotCompile("option not supported"), 66 That("compound expressions are not supported yet"). 67 Filter(`a"foo"`).DoesNotCompile("compound expression not supported"), 68 That("indexing expressions are not supported yet"). 69 Filter("foo[0]").DoesNotCompile("indexing expression not supported"), 70 That("variable references are not supported yet"). 71 Filter("$a"). 72 DoesNotCompile("primary expression of type Variable not supported"), 73 That("variable references in RE subfilter are not supported yet"). 74 Filter("[re $a]"). 75 DoesNotCompile("re subfilter with primary expression of type Variable not supported"), 76 That("variable references in AND subfilter are not supported yet"). 77 Filter("[and $a]"). 78 DoesNotCompile("primary expression of type Variable not supported"), 79 That("variable references in OR subfilter are not supported yet"). 80 Filter("[or $a]"). 81 DoesNotCompile("primary expression of type Variable not supported"), 82 That("other subqueries are not supported yet"). 83 Filter("[other foo bar]"). 84 DoesNotCompile("head other not supported"), 85 ) 86 } 87 88 func test(t *testing.T, tests ...testCase) { 89 for _, test := range tests { 90 t.Run(test.name, func(t *testing.T) { 91 q, err := filter.Compile(test.filter) 92 if errType := getErrorType(err); errType != test.errorType { 93 t.Errorf("%q should have %s, but has %s", 94 test.filter, test.errorType, errType) 95 } 96 if err != nil { 97 if err.Error() != test.errorMessage { 98 t.Errorf("%q should have error message %q, but is %q", 99 test.filter, test.errorMessage, err) 100 } 101 return 102 } 103 for _, s := range test.matches { 104 ok := q.Match(s) 105 if !ok { 106 t.Errorf("%q should match %q, but doesn't", test.filter, s) 107 } 108 } 109 for _, s := range test.doesntMatch { 110 ok := q.Match(s) 111 if ok { 112 t.Errorf("%q shouldn't match %q, but does", test.filter, s) 113 } 114 } 115 }) 116 } 117 } 118 119 type testCase struct { 120 name string 121 filter string 122 matches []string 123 doesntMatch []string 124 errorType errorType 125 errorMessage string 126 } 127 128 func That(name string) testCase { 129 return testCase{name: name} 130 } 131 132 func (t testCase) Filter(q string) testCase { 133 t.filter = q 134 return t 135 } 136 137 func (t testCase) DoesNotParse(message string) testCase { 138 t.errorType = parseError 139 t.errorMessage = message 140 return t 141 } 142 143 func (t testCase) DoesNotCompile(message string) testCase { 144 t.errorType = compileError 145 t.errorMessage = message 146 return t 147 } 148 149 func (t testCase) Matches(s ...string) testCase { 150 t.matches = s 151 return t 152 } 153 154 func (t testCase) DoesNotMatch(s ...string) testCase { 155 t.doesntMatch = s 156 return t 157 } 158 159 type errorType uint 160 161 const ( 162 noError errorType = iota 163 parseError 164 compileError 165 ) 166 167 func getErrorType(err error) errorType { 168 if err == nil { 169 return noError 170 } else if parse.UnpackErrors(err) != nil { 171 return parseError 172 } else { 173 return compileError 174 } 175 } 176 177 func (et errorType) String() string { 178 switch et { 179 case noError: 180 return "no error" 181 case parseError: 182 return "parse error" 183 case compileError: 184 return "compile error" 185 default: 186 panic("unreachable") 187 } 188 }