fork of https://github.com/sourcegraph/zoekt
0

Configure Feed

Select the types of activity you want to include in your feed.

1// Copyright 2016 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package query 16 17import ( 18 "log" 19 "reflect" 20 "regexp/syntax" 21 "testing" 22 23 "github.com/grafana/regexp" 24) 25 26func mustParseRE(s string) *syntax.Regexp { 27 r, err := syntax.Parse(s, regexpFlags) 28 if err != nil { 29 log.Panicf("parsing %q: %v", s, err) 30 } 31 return r 32} 33 34func TestParseQuery(t *testing.T) { 35 type testcase struct { 36 in string 37 want Q 38 } 39 40 for _, c := range []testcase{ 41 {`\bword\b`, &Regexp{Regexp: mustParseRE(`\bword\b`)}}, 42 {"fi\"le:bla\"", &Substring{Pattern: "file:bla"}}, 43 {"abc or def", NewOr(&Substring{Pattern: "abc"}, &Substring{Pattern: "def"})}, 44 {"(abc or def)", NewOr(&Substring{Pattern: "abc"}, &Substring{Pattern: "def"})}, 45 {"(ppp qqq or rrr sss)", NewOr( 46 NewAnd(&Substring{Pattern: "ppp"}, &Substring{Pattern: "qqq"}), 47 NewAnd(&Substring{Pattern: "rrr"}, &Substring{Pattern: "sss"}))}, 48 {"((x) ora b(z(d)))", NewAnd( 49 &Substring{Pattern: "x"}, 50 &Substring{Pattern: "ora"}, 51 &Substring{Pattern: "bzd"})}, 52 {"( )", &Const{Value: true}}, 53 {"(abc)(de)", &Substring{Pattern: "abcde"}}, 54 {"sub-pixel", &Substring{Pattern: "sub-pixel"}}, 55 {"abc", &Substring{Pattern: "abc"}}, 56 {"ABC", &Substring{Pattern: "ABC", CaseSensitive: true}}, 57 {"\"abc bcd\"", &Substring{Pattern: "abc bcd"}}, 58 {"abc bcd", NewAnd( 59 &Substring{Pattern: "abc"}, 60 &Substring{Pattern: "bcd"})}, 61 {"f:fs", &Substring{Pattern: "fs", FileName: true}}, 62 {"fs", &Substring{Pattern: "fs"}}, 63 {"-abc", &Not{&Substring{Pattern: "abc"}}}, 64 {"abccase:yes", &Substring{Pattern: "abccase:yes"}}, 65 {"file:abc", &Substring{Pattern: "abc", FileName: true}}, 66 {"branch:pqr", &Branch{Pattern: "pqr"}}, 67 {"((x|y) )", &Regexp{Regexp: mustParseRE("[xy]")}}, 68 {"archived:yes", RawConfig(RcOnlyArchived)}, 69 {"archived:no", RawConfig(RcNoArchived)}, 70 {"fork:yes", RawConfig(RcOnlyForks)}, 71 {"fork:no", RawConfig(RcNoForks)}, 72 {"public:yes", RawConfig(RcOnlyPublic)}, 73 {"public:no", RawConfig(RcOnlyPrivate)}, 74 {"file:helpers\\.go byte", NewAnd( 75 &Substring{Pattern: "helpers.go", FileName: true}, 76 &Substring{Pattern: "byte"})}, 77 {"(abc def)", NewAnd( 78 &Substring{Pattern: "abc"}, 79 &Substring{Pattern: "def"})}, 80 {"(abc def", nil}, 81 {"regex:abc[p-q]", &Regexp{Regexp: mustParseRE("abc[p-q]")}}, 82 {"aBc[p-q]", &Regexp{Regexp: mustParseRE("aBc[p-q]"), CaseSensitive: true}}, 83 {"aBc[p-q] case:auto", &Regexp{Regexp: mustParseRE("aBc[p-q]"), CaseSensitive: true}}, 84 {"repo:go", &Repo{regexp.MustCompile("go")}}, 85 {"repo:.*", &Repo{Regexp: regexp.MustCompile(".*")}}, 86 87 {"file:\"\"", &Const{true}}, 88 {"abc.*def", &Regexp{Regexp: mustParseRE("abc.*def")}}, 89 {"abc\\.\\*def", &Substring{Pattern: "abc.*def"}}, 90 {"(abc)", &Substring{Pattern: "abc"}}, 91 92 {"c:abc", &Substring{Pattern: "abc", Content: true}}, 93 {"content:abc", &Substring{Pattern: "abc", Content: true}}, 94 95 {"lang:c++", &Language{"C++"}}, 96 {"lang:cpp", &Language{"C++"}}, 97 {"sym:pqr", &Symbol{&Substring{Pattern: "pqr"}}}, 98 {"sym:Pqr", &Symbol{&Substring{Pattern: "Pqr", CaseSensitive: true}}}, 99 {"sym:.*", &Symbol{&Regexp{Regexp: mustParseRE(".*")}}}, 100 {"sym:a(b|d)e", &Symbol{&Regexp{Regexp: mustParseRE("a[bd]e")}}}, 101 102 // case 103 {"abc case:yes", &Substring{Pattern: "abc", CaseSensitive: true}}, 104 {"abc case:auto", &Substring{Pattern: "abc", CaseSensitive: false}}, 105 {"ABC case:auto", &Substring{Pattern: "ABC", CaseSensitive: true}}, 106 {"ABC case:\"auto\"", &Substring{Pattern: "ABC", CaseSensitive: true}}, 107 {"abc -f:def case:yes", NewAnd( 108 &Substring{Pattern: "abc", CaseSensitive: true}, 109 &Not{Child: &Substring{Pattern: "def", FileName: true, CaseSensitive: true}}, 110 )}, 111 112 // type 113 {"type:repo abc", &Type{Type: TypeRepo, Child: &Substring{Pattern: "abc"}}}, 114 {"type:file abc def", &Type{Type: TypeFileName, Child: NewAnd(&Substring{Pattern: "abc"}, &Substring{Pattern: "def"})}}, 115 {"(type:repo abc) def", NewAnd(&Type{Type: TypeRepo, Child: &Substring{Pattern: "abc"}}, &Substring{Pattern: "def"})}, 116 117 // errors. 118 {"--", nil}, 119 {"\"abc", nil}, 120 {"\"a\\", nil}, 121 {"case:foo", nil}, 122 123 {"sym:", nil}, 124 {"abc or", nil}, 125 {"or abc", nil}, 126 {"def or or abc", nil}, 127 128 // unbalanced parentheses 129 {"(", nil}, 130 {"((", nil}, 131 {"(((", nil}, 132 {")", nil}, 133 {"))", nil}, 134 {")))", nil}, 135 {"foo)", nil}, 136 {"foo))", nil}, 137 {"foo)))", nil}, 138 {"(foo", nil}, 139 {"((foo", nil}, 140 {"(((foo", nil}, 141 {"(foo))", nil}, 142 {"(((foo))", nil}, 143 144 {"", &Const{Value: true}}, 145 146 // whitespace 147 {" ( ) ", &Const{Value: true}}, 148 {" ( foo ) ", &Substring{Pattern: "foo"}}, 149 } { 150 got, err := Parse(c.in) 151 if (c.want == nil) != (err != nil) { 152 t.Errorf("Parse(%q): error %v, want %v", c.in, err, c.want) 153 } else if got != nil { 154 if !reflect.DeepEqual(got, c.want) { 155 t.Errorf("Parse(%s): got %v want %v", c.in, got, c.want) 156 } 157 } 158 } 159} 160 161func TestTokenize(t *testing.T) { 162 type testcase struct { 163 in string 164 typ int 165 text string 166 } 167 168 cases := []testcase{ 169 {"file:bla", tokFile, "bla"}, 170 {"file:bla ", tokFile, "bla"}, 171 {"f:bla ", tokFile, "bla"}, 172 {"(abc def) ", tokParenOpen, "("}, 173 {"(abcdef)", tokText, "(abcdef)"}, 174 {"(abc)(de)", tokText, "(abc)(de)"}, 175 {"(ab(c)def) ", tokText, "(ab(c)def)"}, 176 {"(ab\\ def) ", tokText, "(ab\\ def)"}, 177 {") ", tokParenClose, ")"}, 178 {"a(bc))", tokText, "a(bc)"}, 179 {"abc) ", tokText, "abc"}, 180 {"file:\"bla\"", tokFile, "bla"}, 181 {"\"file:bla\"", tokText, "file:bla"}, 182 {"\\", tokError, ""}, 183 {"o\"r\" bla", tokText, "or"}, 184 {"or bla", tokOr, "or"}, 185 {"ar bla", tokText, "ar"}, 186 } 187 for _, c := range cases { 188 tok, err := nextToken([]byte(c.in)) 189 if err != nil { 190 tok = &token{Type: tokError} 191 } 192 if tok.Type != c.typ { 193 t.Errorf("%s: got type %d, want %d", c.in, tok.Type, c.typ) 194 continue 195 } 196 197 if string(tok.Text) != c.text { 198 t.Errorf("%s: got text %q, want %q", c.in, tok.Text, c.text) 199 } 200 } 201} 202 203func TestMetaQueryParsing(t *testing.T) { 204 cases := []struct { 205 input string 206 field string 207 pattern string 208 err bool 209 }{ 210 { 211 input: "meta.visibility_level:20", 212 field: "visibility_level", 213 pattern: "20", 214 err: false, 215 }, 216 { 217 input: "meta.needle:ha.*stack", 218 field: "needle", 219 pattern: "ha.*stack", 220 err: false, 221 }, 222 { 223 input: "meta.public:true", 224 field: "public", 225 pattern: "true", 226 err: false, 227 }, 228 { 229 input: "meta.language:go", 230 field: "language", 231 pattern: "go", 232 err: false, 233 }, 234 { 235 input: "meta.invalid_field:(", 236 field: "invalid_field", 237 pattern: "(", 238 err: true, 239 }, 240 } 241 242 for _, c := range cases { 243 t.Run(c.input, func(t *testing.T) { 244 q, err := Parse(c.input) 245 if c.err { 246 if err == nil { 247 t.Errorf("expected error, got nil") 248 } 249 return 250 } 251 252 if err != nil { 253 t.Errorf("unexpected error: %v", err) 254 } 255 256 meta, ok := q.(*Meta) 257 if !ok || meta == nil { 258 t.Errorf("expected *Meta, got %T", q) 259 return 260 } 261 262 if meta.Field != c.field { 263 t.Errorf("expected field %q, got %q", c.field, meta.Field) 264 } 265 if meta.Value == nil || meta.Value.String() != c.pattern { 266 t.Errorf("expected pattern %q, got %v", c.pattern, meta.Value) 267 } 268 }) 269 } 270}