1
16
17 package warn
18
19 import (
20 "bytes"
21 "fmt"
22 "strings"
23 "testing"
24
25 "github.com/bazelbuild/buildtools/build"
26 "github.com/bazelbuild/buildtools/testutils"
27 )
28
29 const (
30 scopeBuild = build.TypeBuild
31 scopeBzl = build.TypeBzl
32 scopeWorkspace = build.TypeWorkspace
33 scopeDefault = build.TypeDefault
34 scopeModule = build.TypeModule
35 scopeEverywhere = scopeBuild | scopeBzl | scopeWorkspace | scopeDefault | scopeModule
36 scopeBazel = scopeBuild | scopeBzl | scopeWorkspace | scopeModule
37 scopeDeclarative = scopeBuild | scopeWorkspace | scopeModule
38 )
39
40
41
42 var testFileReader *FileReader
43
44
45
46 var testPackage string = "test/package"
47
48
49 var fileReaderRequests []string
50
51 func setUpFileReader(data map[string]string) (cleanup func()) {
52 readFile := func(filename string) ([]byte, error) {
53 fileReaderRequests = append(fileReaderRequests, filename)
54 if contents, ok := data[filename]; ok {
55 return []byte(contents), nil
56 }
57 return nil, fmt.Errorf("file not found")
58 }
59 testFileReader = NewFileReader(readFile)
60 fileReaderRequests = nil
61
62 return func() {
63
64 testFileReader = nil
65 fileReaderRequests = nil
66 }
67 }
68
69 func setUpTestPackage(name string) (cleanup func()) {
70 oldName := testPackage
71 testPackage = name
72
73 return func() {
74
75 testPackage = oldName
76 }
77 }
78
79 func getFilename(fileType build.FileType) string {
80 switch fileType {
81 case build.TypeBuild:
82 return "BUILD"
83 case build.TypeWorkspace:
84 return "WORKSPACE"
85 case build.TypeBzl:
86 return "test_file.bzl"
87 case build.TypeModule:
88 return "MODULE.bazel"
89 default:
90 return "test_file.strlrk"
91 }
92 }
93
94 func getFileForTest(input string, fileType build.FileType) *build.File {
95 input = strings.TrimLeft(input, "\n")
96 filename := getFilename(fileType)
97 file, err := build.Parse(testPackage+"/"+filename, []byte(input))
98 if err != nil {
99 panic(fmt.Sprintf("%v", err))
100 }
101 file.Pkg = testPackage
102 file.Label = filename
103 file.WorkspaceRoot = "/home/users/foo/bar"
104 return file
105 }
106
107 func getFindings(category, input string, fileType build.FileType) []*Finding {
108 file := getFileForTest(input, fileType)
109 return FileWarnings(file, []string{category}, nil, ModeWarn, testFileReader)
110 }
111
112 func compareFindings(t *testing.T, category, input string, expected []string, scope, fileType build.FileType) {
113
114 if scope&fileType == 0 {
115 expected = []string{}
116 }
117
118 findings := getFindings(category, input, fileType)
119
120
121 if len(expected) != len(findings) {
122 t.Errorf("Input: %s", input)
123 t.Errorf("number of matches: %d, want %d", len(findings), len(expected))
124 for _, e := range expected {
125 t.Errorf("expected: %s", e)
126 }
127 for _, f := range findings {
128 t.Errorf("got: %d: %s", f.Start.Line, f.Message)
129 }
130 return
131 }
132 for i := range findings {
133 msg := fmt.Sprintf(":%d: %s", findings[i].Start.Line, findings[i].Message)
134 if !strings.Contains(msg, expected[i]) {
135 t.Errorf("Input: %s", input)
136 t.Errorf("got: `%s`,\nwant: `%s`", msg, expected[i])
137 }
138 }
139 }
140
141
142 func checkFix(t *testing.T, category, input, expected string, scope, fileType build.FileType) {
143
144 if scope&fileType == 0 {
145 expected = input
146 }
147
148 file := getFileForTest(input, fileType)
149 goldenFile := getFileForTest(expected, fileType)
150
151 FixWarnings(file, []string{category}, false, testFileReader)
152 have := build.Format(file)
153 want := build.Format(goldenFile)
154 if !bytes.Equal(have, want) {
155 t.Errorf("fixed a test (type %s) incorrectly:\ninput:\n%s\ndiff (-expected, +ours)\n",
156 fileType, input)
157 testutils.Tdiff(t, want, have)
158 }
159 }
160
161
162
163 func checkNoFix(t *testing.T, category, input string, fileType build.FileType) {
164 file := getFileForTest(input, fileType)
165 formatted := build.Format(file)
166
167
168 FileWarnings(file, []string{category}, nil, ModeWarn, testFileReader)
169 fixed := build.FormatWithoutRewriting(file)
170
171 if !bytes.Equal(formatted, fixed) {
172 t.Errorf("Modified a file (type %s) while getting warnings:\ninput:\n%s\ndiff (-before, +after)\n",
173 fileType, input)
174 testutils.Tdiff(t, formatted, fixed)
175 }
176 }
177
178 func checkFindings(t *testing.T, category, input string, expected []string, scope build.FileType) {
179
180 checkFindingsAndFix(t, category, input, input, expected, scope)
181 }
182
183 func checkFindingsAndFix(t *testing.T, category, input, output string, expected []string, scope build.FileType) {
184 fileTypes := []build.FileType{
185 build.TypeDefault,
186 build.TypeBuild,
187 build.TypeWorkspace,
188 build.TypeBzl,
189 build.TypeModule,
190 }
191
192 for _, fileType := range fileTypes {
193 compareFindings(t, category, input, expected, scope, fileType)
194 checkFix(t, category, input, output, scope, fileType)
195 checkFix(t, category, output, output, scope, fileType)
196 checkNoFix(t, category, input, fileType)
197 }
198 }
199
200 func TestCalculateDifference(t *testing.T) {
201 tests := []struct {
202 before string
203 after string
204 start int
205 end int
206 replacement string
207 }{
208 {
209 before: "asdf",
210 after: "asxydf",
211 start: 2,
212 end: 2,
213 replacement: "xy",
214 },
215 {
216 before: "asxydf",
217 after: "asdf",
218 start: 2,
219 end: 4,
220 replacement: "",
221 },
222 {
223 before: "asxydf",
224 after: "asztdf",
225 start: 2,
226 end: 4,
227 replacement: "zt",
228 },
229 {
230 before: "",
231 after: "foobar",
232 start: 0,
233 end: 0,
234 replacement: "foobar",
235 },
236 {
237 before: "foobar",
238 after: "",
239 start: 0,
240 end: 6,
241 replacement: "",
242 },
243 {
244 before: "qwerty",
245 after: "asdfgh",
246 start: 0,
247 end: 6,
248 replacement: "asdfgh",
249 },
250 {
251 before: "aa",
252 after: "aaaa",
253 start: 2,
254 end: 2,
255 replacement: "aa",
256 },
257 {
258 before: "aaaa",
259 after: "aa",
260 start: 2,
261 end: 4,
262 replacement: "",
263 },
264 {
265 before: "abc",
266 after: "abdbc",
267 start: 2,
268 end: 2,
269 replacement: "db",
270 },
271 {
272 before: "abdbc",
273 after: "abc",
274 start: 2,
275 end: 4,
276 replacement: "",
277 },
278 }
279
280 for _, tc := range tests {
281 before := []byte(tc.before)
282 after := []byte(tc.after)
283
284 start, end, replacement := calculateDifference(&before, &after)
285
286 if start != tc.start || end != tc.end || replacement != tc.replacement {
287 t.Errorf("Wrong difference for %q and %q: want %d, %d, %q, got %d, %d, %q",
288 tc.before, tc.after, tc.start, tc.end, tc.replacement, start, end, replacement)
289 }
290 }
291 }
292
293 func TestSuggestions(t *testing.T) {
294
295
296 contents := `foo()
297
298 attr.bar(name = "bar", cfg = "data")
299
300 attr.baz("baz", cfg = "data")
301 `
302 f, err := build.ParseBzl("file.bzl", []byte(contents))
303 if err != nil {
304 t.Fatalf("Parse error: %v", err)
305 }
306
307 findings := FileWarnings(f, []string{"attr-cfg"}, nil, ModeSuggest, testFileReader)
308 want := []struct {
309 start int
310 end int
311 replacement string
312 }{
313 {
314 start: 28,
315 end: 42,
316 replacement: "",
317 },
318 {
319 start: 59,
320 end: 73,
321 replacement: "",
322 },
323 }
324
325 if len(findings) != len(want) {
326 t.Errorf("Expected %d findings, got %d", len(want), len(findings))
327 }
328
329 for i, f := range findings {
330 w := want[i]
331 if f.Replacement == nil {
332 t.Errorf("No replacement for finding %d", i)
333 }
334 r := f.Replacement
335 if r.Start != w.start || r.End != w.end || r.Content != w.replacement {
336 t.Errorf("Wrong replacement #%d, want %d, %d, %q, got %d, %d, %q",
337 i, w.start, w.end, w.replacement, r.Start, r.End, r.Content)
338 }
339 }
340 }
341
342 func TestDisabledWarning(t *testing.T) {
343 contents := `foo()
344
345 # buildifier: disable=depset-iteration
346 for x in depset([1, 2, 3]):
347 print(x) # buildozer: disable=print
348
349 for y in "foobar": # buildozer: disable=string-iteration
350 # buildifier: disable=no-effect
351 y
352
353 # buildifier: disable=duplicated-name-2
354 cc_library(
355 name = "foo", # buildifier: disable=duplicated-name-1
356 )
357
358 # buildifier: disable=skylark-comment
359 # some comment mentioning skylark
360 `
361
362 f, err := build.ParseBzl("file.bzl", []byte(contents))
363 if err != nil {
364 t.Fatalf("Parse error: %v", err)
365 }
366
367 tests := []struct {
368 start int
369 end int
370 category string
371 }{
372 {
373 start: 3,
374 end: 5,
375 category: "depset-iteration",
376 },
377 {
378 start: 5,
379 end: 5,
380 category: "print",
381 },
382 {
383 start: 7,
384 end: 7,
385 category: "string-iteration",
386 },
387 {
388 start: 8,
389 end: 9,
390 category: "no-effect",
391 },
392 {
393 start: 13,
394 end: 13,
395 category: "duplicated-name-1",
396 },
397 {
398 start: 11,
399 end: 14,
400 category: "duplicated-name-2",
401 },
402 {
403 start: 16,
404 end: 17,
405 category: "skylark-comment",
406 },
407 }
408
409 linesCount := strings.Count(contents, "\n")
410
411 for _, tc := range tests {
412 for line := 1; line <= linesCount; line++ {
413 disabled := DisabledWarning(f, line, tc.category)
414 shouldBeDisabled := line >= tc.start && line <= tc.end
415 if disabled != shouldBeDisabled {
416 t.Errorf("Wrong disabled status for the category %q, want %t, got %t", tc.category, shouldBeDisabled, disabled)
417 }
418 }
419 }
420 }
421
View as plain text