fork of https://github.com/sourcegraph/zoekt
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 {"", &Const{Value: true}},
129 } {
130 got, err := Parse(c.in)
131 if (c.want == nil) != (err != nil) {
132 t.Errorf("Parse(%q): error %v, want %v", c.in, err, c.want)
133 } else if got != nil {
134 if !reflect.DeepEqual(got, c.want) {
135 t.Errorf("Parse(%s): got %v want %v", c.in, got, c.want)
136 }
137 }
138 }
139}
140
141func TestTokenize(t *testing.T) {
142 type testcase struct {
143 in string
144 typ int
145 text string
146 }
147
148 cases := []testcase{
149 {"file:bla", tokFile, "bla"},
150 {"file:bla ", tokFile, "bla"},
151 {"f:bla ", tokFile, "bla"},
152 {"(abc def) ", tokParenOpen, "("},
153 {"(abcdef)", tokText, "(abcdef)"},
154 {"(abc)(de)", tokText, "(abc)(de)"},
155 {"(ab(c)def) ", tokText, "(ab(c)def)"},
156 {"(ab\\ def) ", tokText, "(ab\\ def)"},
157 {") ", tokParenClose, ")"},
158 {"a(bc))", tokText, "a(bc)"},
159 {"abc) ", tokText, "abc"},
160 {"file:\"bla\"", tokFile, "bla"},
161 {"\"file:bla\"", tokText, "file:bla"},
162 {"\\", tokError, ""},
163 {"o\"r\" bla", tokText, "or"},
164 {"or bla", tokOr, "or"},
165 {"ar bla", tokText, "ar"},
166 }
167 for _, c := range cases {
168 tok, err := nextToken([]byte(c.in))
169 if err != nil {
170 tok = &token{Type: tokError}
171 }
172 if tok.Type != c.typ {
173 t.Errorf("%s: got type %d, want %d", c.in, tok.Type, c.typ)
174 continue
175 }
176
177 if string(tok.Text) != c.text {
178 t.Errorf("%s: got text %q, want %q", c.in, tok.Text, c.text)
179 }
180 }
181}