1
16
17 package edit
18
19 import (
20 "io/ioutil"
21 "os"
22 "path/filepath"
23 "reflect"
24 "strings"
25 "testing"
26
27 "github.com/bazelbuild/buildtools/build"
28 )
29
30 var removeCommentTests = []struct {
31 args []string
32 buildFile string
33 expected string
34 }{
35 {[]string{},
36 `# comment
37 foo(
38 name = "foo",
39 )`,
40 `foo(
41 name = "foo",
42 )`,
43 },
44 {[]string{
45 "name",
46 },
47 `foo(
48 # comment
49 name = "foo",
50 )`,
51 `foo(
52 name = "foo",
53 )`,
54 },
55 {[]string{
56 "name",
57 },
58 `foo(
59 name = "foo" # comment,
60 )`,
61 `foo(
62 name = "foo",
63 )`,
64 },
65 {[]string{
66 "deps", "bar",
67 },
68 `foo(
69 name = "foo",
70 deps = [
71 # comment
72 "bar",
73 "baz",
74 ],
75 )`,
76 `foo(
77 name = "foo",
78 deps = [
79 "bar",
80 "baz",
81 ],
82 )`,
83 },
84 {[]string{
85 "deps", "bar",
86 },
87 `foo(
88 name = "foo",
89 deps = [
90 "bar", # comment
91 "baz",
92 ],
93 )`,
94 `foo(
95 name = "foo",
96 deps = [
97 "bar",
98 "baz",
99 ],
100 )`,
101 },
102 }
103
104 func TestCmdRemoveComment(t *testing.T) {
105 for i, tt := range removeCommentTests {
106 bld, err := build.Parse("BUILD", []byte(tt.buildFile))
107 if err != nil {
108 t.Error(err)
109 continue
110 }
111 rl := bld.Rules("foo")[0]
112 env := CmdEnvironment{
113 File: bld,
114 Rule: rl,
115 Args: tt.args,
116 }
117 bld, _ = cmdRemoveComment(NewOpts(), env)
118 got := strings.TrimSpace(string(build.Format(bld)))
119 if got != tt.expected {
120 t.Errorf("cmdRemoveComment(%d):\ngot:\n%s\nexpected:\n%s", i, got, tt.expected)
121 }
122 }
123 }
124
125 type targetExpressionToBuildFilesTestCase struct {
126 rootDir, target string
127 buildFiles []string
128 }
129
130 func setupTestTmpWorkspace(t *testing.T, buildFileName string) (tmp string) {
131 tmp, err := ioutil.TempDir("", "")
132 if err != nil {
133 t.Fatal(err)
134 }
135
136
137 tmp, err = filepath.EvalSymlinks(tmp)
138 if err != nil {
139 t.Fatal(err)
140 }
141
142 if err := os.MkdirAll(filepath.Join(tmp, "a", "b"), 0755); err != nil {
143 t.Fatal(err)
144 }
145 if err := os.MkdirAll(filepath.Join(tmp, "a", "c"), 0755); err != nil {
146 t.Fatal(err)
147 }
148 if err := ioutil.WriteFile(filepath.Join(tmp, "WORKSPACE"), nil, 0755); err != nil {
149 t.Fatal(err)
150 }
151 if err := ioutil.WriteFile(filepath.Join(tmp, buildFileName), nil, 0755); err != nil {
152 t.Fatal(err)
153 }
154 if err := ioutil.WriteFile(filepath.Join(tmp, "a", buildFileName), nil, 0755); err != nil {
155 t.Fatal(err)
156 }
157 if err := ioutil.WriteFile(filepath.Join(tmp, "a", "b", buildFileName), nil, 0755); err != nil {
158 t.Fatal(err)
159 }
160 if err := ioutil.WriteFile(filepath.Join(tmp, "a", "c", buildFileName), nil, 0755); err != nil {
161 t.Fatal(err)
162 }
163 return
164 }
165
166 func runTestTargetExpressionToBuildFiles(t *testing.T, buildFileName string) {
167 tmp := setupTestTmpWorkspace(t, buildFileName)
168 defer os.RemoveAll(tmp)
169
170 for _, tc := range []targetExpressionToBuildFilesTestCase{
171 {tmp, "//", []string{filepath.Join(tmp, buildFileName)}},
172 {tmp, "//:foo", []string{filepath.Join(tmp, buildFileName)}},
173 {tmp, "//a", []string{filepath.Join(tmp, "a", buildFileName)}},
174 {tmp, "//a:foo", []string{filepath.Join(tmp, "a", buildFileName)}},
175 {tmp, "//a/b", []string{filepath.Join(tmp, "a", "b", buildFileName)}},
176 {tmp, "//a/b:foo", []string{filepath.Join(tmp, "a", "b", buildFileName)}},
177 {tmp, "//...", []string{filepath.Join(tmp, buildFileName), filepath.Join(tmp, "a", buildFileName), filepath.Join(tmp, "a", "b", buildFileName), filepath.Join(tmp, "a", "c", buildFileName)}},
178 {tmp, "//a/...", []string{filepath.Join(tmp, "a", buildFileName), filepath.Join(tmp, "a", "b", buildFileName), filepath.Join(tmp, "a", "c", buildFileName)}},
179 {tmp, "//a/b/...", []string{filepath.Join(tmp, "a", "b", buildFileName)}},
180 {tmp, "//a/c/...", []string{filepath.Join(tmp, "a", "c", buildFileName)}},
181 {tmp, "//a/c/...:foo", []string{filepath.Join(tmp, "a", "c", buildFileName)}},
182 {"", "...:foo", []string{filepath.Join(tmp, buildFileName), filepath.Join(tmp, "a", buildFileName), filepath.Join(tmp, "a", "b", buildFileName), filepath.Join(tmp, "a", "c", buildFileName)}},
183 } {
184 if tc.rootDir == "" {
185 cwd, err := os.Getwd()
186 if err != nil {
187 t.Fatal(err)
188 }
189
190 if err := os.Chdir(tmp); err != nil {
191 t.Fatal(err)
192 }
193 defer os.Chdir(cwd)
194 }
195
196 buildFiles := targetExpressionToBuildFiles(tc.rootDir, tc.target)
197 expectedBuildFilesMap := make(map[string]bool)
198 buildFilesMap := make(map[string]bool)
199 for _, buildFile := range buildFiles {
200 buildFilesMap[buildFile] = true
201 }
202 for _, buildFile := range tc.buildFiles {
203 expectedBuildFilesMap[buildFile] = true
204 }
205 if !reflect.DeepEqual(expectedBuildFilesMap, buildFilesMap) {
206 t.Errorf("TargetExpressionToBuildFiles(%q, %q) = %q want %q", tc.rootDir, tc.target, buildFiles, tc.buildFiles)
207 }
208 }
209 }
210
211 func TestTargetExpressionToBuildFiles(t *testing.T) {
212 for _, buildFileName := range BuildFileNames {
213 runTestTargetExpressionToBuildFiles(t, buildFileName)
214 }
215 }
216
217 func runTestAppendCommands(t *testing.T, buildFileName string) {
218 tmp := setupTestTmpWorkspace(t, buildFileName)
219 defer os.RemoveAll(tmp)
220
221 for _, tc := range []targetExpressionToBuildFilesTestCase{
222 {tmp, ".:__pkg__", []string{"./" + buildFileName}},
223 {tmp, "a" + ":__pkg__", []string{"a/" + buildFileName}},
224 {"", "a" + ":__pkg__", []string{"a/" + buildFileName}},
225 } {
226 if tc.rootDir == "" {
227 cwd, err := os.Getwd()
228 if err != nil {
229 t.Fatal(err)
230 }
231
232 if err := os.Chdir(tmp); err != nil {
233 t.Fatal(err)
234 }
235 defer os.Chdir(cwd)
236 }
237
238 commandsByFile := make(map[string][]commandsForTarget)
239 opts := NewOpts()
240 opts.RootDir = tc.rootDir
241 appendCommands(opts, commandsByFile, tc.buildFiles)
242 if len(commandsByFile) != 1 {
243 t.Errorf("Expect one target after appendCommands")
244 }
245 for _, value := range commandsByFile {
246 if value[0].target != tc.target {
247 t.Errorf("appendCommands for buildfile %s yielded target %s, expected %s", tc.buildFiles, value[0].target, tc.target)
248 }
249 }
250 }
251 }
252
253 func TestAppendCommands(t *testing.T) {
254 for _, buildFileName := range BuildFileNames {
255 runTestAppendCommands(t, buildFileName)
256 }
257 }
258
259 var dictListAddTests = []struct {
260 args []string
261 buildFile string
262 expected string
263 }{
264 {[]string{
265 "attr", "key1", "value1",
266 },
267 `foo(
268 name = "foo",
269 )`,
270 `foo(
271 name = "foo",
272 attr = {"key1": ["value1"]},
273 )`,
274 },
275 {[]string{
276 "attr", "key1", "value2",
277 },
278 `foo(
279 name = "foo",
280 attr = {"key1": ["value1"]},
281 )`,
282 `foo(
283 name = "foo",
284 attr = {"key1": [
285 "value1",
286 "value2",
287 ]},
288 )`,
289 },
290 {[]string{
291 "attr", "key1", "value1", "value2",
292 },
293 `foo(
294 name = "foo",
295 )`,
296 `foo(
297 name = "foo",
298 attr = {"key1": [
299 "value1",
300 "value2",
301 ]},
302 )`,
303 },
304 {[]string{
305 "attr", "key2", "value2",
306 },
307 `foo(
308 name = "foo",
309 attr = {"key1": ["value1"]},
310 )`,
311 `foo(
312 name = "foo",
313 attr = {
314 "key1": ["value1"],
315 "key2": ["value2"],
316 },
317 )`,
318 },
319 {[]string{
320 "attr", "key1", "value1",
321 },
322 `foo(
323 name = "foo",
324 attr = {"key1": ["value1"]},
325 )`,
326 `foo(
327 name = "foo",
328 attr = {"key1": ["value1"]},
329 )`,
330 },
331 }
332
333 func TestCmdDictListAdd(t *testing.T) {
334 for i, tt := range dictListAddTests {
335 bld, err := build.Parse("BUILD", []byte(tt.buildFile))
336 if err != nil {
337 t.Error(err)
338 continue
339 }
340 rl := bld.Rules("foo")[0]
341 env := CmdEnvironment{
342 File: bld,
343 Rule: rl,
344 Args: tt.args,
345 }
346 bld, _ = cmdDictListAdd(NewOpts(), env)
347 got := strings.TrimSpace(string(build.Format(bld)))
348 if got != tt.expected {
349 t.Errorf("cmdDictListAdd(%d):\ngot:\n%s\nexpected:\n%s", i, got, tt.expected)
350 }
351 }
352 }
353
354 var substituteLoadsTests = []struct {
355 args []string
356 buildFile string
357 expected string
358 }{
359 {[]string{
360 "^(.*)$", "${1}",
361 },
362 `load("//foo:foo.bzl", "foo")`,
363 `load("//foo:foo.bzl", "foo")`,
364 },
365 {[]string{
366 "^@rules_foo//foo:defs.bzl$", "//build/rules/foo:defs.bzl",
367 },
368 `load("@rules_bar//bar:defs.bzl", "bar")`,
369 `load("@rules_bar//bar:defs.bzl", "bar")`,
370 },
371 {[]string{
372 "^@rules_foo//foo:defs.bzl$", "//build/rules/foo:defs.bzl",
373 },
374 `load("@rules_foo//foo:defs.bzl", "foo", "foo2")
375 load("@rules_bar//bar:defs.bzl", "bar")`,
376 `load("@rules_bar//bar:defs.bzl", "bar")
377 load("//build/rules/foo:defs.bzl", "foo", "foo2")`,
378 },
379 {[]string{
380 ":foo.bzl$", ":defs.bzl",
381 },
382 `load("//foo:foo.bzl", "foo")`,
383 `load("//foo:defs.bzl", "foo")`,
384 },
385 {[]string{
386
387 "^@([^/]*)//([^:].*)$", "//third_party/build_defs/${1}/${2}",
388 },
389 `load("@rules_foo//foo:defs.bzl", "foo", "foo2")
390 load("@rules_bar//bar:defs.bzl", "bar")
391 load("@rules_bar//:defs.bzl", legacy_bar = "bar")`,
392 `load("@rules_bar//:defs.bzl", legacy_bar = "bar")
393 load("//third_party/build_defs/rules_bar/bar:defs.bzl", "bar")
394 load("//third_party/build_defs/rules_foo/foo:defs.bzl", "foo", "foo2")`,
395 },
396 }
397
398 func TestCmdSubstituteLoad(t *testing.T) {
399 for i, tt := range substituteLoadsTests {
400 bld, err := build.Parse("BUILD", []byte(tt.buildFile))
401 if err != nil {
402 t.Error(err)
403 continue
404 }
405 env := CmdEnvironment{
406 File: bld,
407 Args: tt.args,
408 }
409 bld, _ = cmdSubstituteLoad(NewOpts(), env)
410 got := strings.TrimSpace(string(build.Format(bld)))
411 if got != tt.expected {
412 t.Errorf("cmdSubstituteLoad(%d):\ngot:\n%s\nexpected:\n%s", i, got, tt.expected)
413 }
414 }
415 }
416
417 func TestCmdDictAddSet_missingColon(t *testing.T) {
418 for _, tc := range []struct {
419 name string
420 fun func(*Options, CmdEnvironment) (*build.File, error)
421 }{
422 {"dict_add", cmdDictAdd},
423 {"dict_set", cmdDictSet},
424 } {
425 t.Run(tc.name, func(t *testing.T) {
426 bld, err := build.Parse("BUILD", []byte("rule()"))
427 if err != nil {
428 t.Fatal(err)
429 }
430 env := CmdEnvironment{
431 File: bld,
432 Rule: bld.RuleAt(1),
433 Args: []string{"attr", "invalid"},
434 }
435 _, err = tc.fun(NewOpts(), env)
436 if err == nil {
437 t.Error("succeeded, want error")
438 }
439 })
440 }
441 }
442
443 func TestCmdSetSelect(t *testing.T) {
444 for i, tc := range []struct {
445 name string
446 args []string
447 buildFile string
448 expected string
449 }{
450 {
451 name: "select_statement_doesn't_exist",
452 args: []string{
453 "args",
454 ":use_ci_timeouts", "-test.timeout=123s",
455 ":use_ci_timeouts", "-test.anotherFlag=flagValue",
456 "//conditions:default", "-test.timeout=789s",
457 },
458 buildFile: `foo(
459 name = "foo",
460 )`,
461 expected: `foo(
462 name = "foo",
463 args = select({
464 ":use_ci_timeouts": [
465 "-test.timeout=123s",
466 "-test.anotherFlag=flagValue",
467 ],
468 "//conditions:default": ["-test.timeout=789s"],
469 }),
470 )`},
471 {
472 name: "select_statement_exists",
473 args: []string{
474 "args",
475 ":use_ci_timeouts", "-test.timeout=543s",
476 "//conditions:default", "-test.timeout=876s",
477 },
478 buildFile: `foo(
479 name = "foo",
480 args = select({
481 ":use_ci_timeouts": [
482 "-test.timeout=123s",
483 "-test.anotherFlag=flagValue",
484 ],
485 "//conditions:default": ["-test.timeout=789s"],
486 }),
487 )`,
488 expected: `foo(
489 name = "foo",
490 args = select({
491 ":use_ci_timeouts": ["-test.timeout=543s"],
492 "//conditions:default": ["-test.timeout=876s"],
493 }),
494 )`},
495 {
496 name: "attr_exists_but_not_select",
497 args: []string{
498 "args",
499 ":use_ci_timeouts", "-test.timeout=543s",
500 "//conditions:default", "-test.timeout=876s",
501 },
502 buildFile: `foo(
503 name = "foo",
504 args = ["-test.timeout=123s"],
505 )`,
506 expected: `foo(
507 name = "foo",
508 args = select({
509 ":use_ci_timeouts": ["-test.timeout=543s"],
510 "//conditions:default": ["-test.timeout=876s"],
511 }),
512 )`},
513 } {
514 t.Run(tc.name, func(t *testing.T) {
515 bld, err := build.Parse("BUILD", []byte(tc.buildFile))
516 if err != nil {
517 t.Error(err)
518 }
519 rl := bld.Rules("foo")[0]
520 env := CmdEnvironment{
521 File: bld,
522 Rule: rl,
523 Args: tc.args,
524 }
525 bld, _ = cmdSetSelect(NewOpts(), env)
526 got := strings.TrimSpace(string(build.Format(bld)))
527 if got != tc.expected {
528 t.Errorf("cmdSetSelect(%d):\ngot:\n%s\nexpected:\n%s", i, got, tc.expected)
529 }
530 })
531 }
532 }
533
View as plain text