blob: 69f11152e8897200eb439fff53925e6aef9cec55 [file] [log] [blame]
Chris Parsons4f069892021-01-15 12:22:41 -05001// Copyright 2020 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 bazel
16
17import (
18 "fmt"
19 "reflect"
20 "testing"
21)
22
23func TestAqueryMultiArchGenrule(t *testing.T) {
24 // This input string is retrieved from a real build of bionic-related genrules.
25 const inputString = `
26{
27 "artifacts": [{
28 "id": 1,
29 "pathFragmentId": 1
30 }, {
31 "id": 2,
32 "pathFragmentId": 6
33 }, {
34 "id": 3,
35 "pathFragmentId": 8
36 }, {
37 "id": 4,
38 "pathFragmentId": 12
39 }, {
40 "id": 5,
41 "pathFragmentId": 19
42 }, {
43 "id": 6,
44 "pathFragmentId": 20
45 }, {
46 "id": 7,
47 "pathFragmentId": 21
48 }],
49 "actions": [{
50 "targetId": 1,
51 "actionKey": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
52 "mnemonic": "Genrule",
53 "configurationId": 1,
54 "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm.S"],
55 "environmentVariables": [{
56 "key": "PATH",
57 "value": "/bin:/usr/bin:/usr/local/bin"
58 }],
59 "inputDepSetIds": [1],
60 "outputIds": [4],
61 "primaryOutputId": 4
62 }, {
63 "targetId": 2,
64 "actionKey": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
65 "mnemonic": "Genrule",
66 "configurationId": 1,
67 "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86.S"],
68 "environmentVariables": [{
69 "key": "PATH",
70 "value": "/bin:/usr/bin:/usr/local/bin"
71 }],
72 "inputDepSetIds": [2],
73 "outputIds": [5],
74 "primaryOutputId": 5
75 }, {
76 "targetId": 3,
77 "actionKey": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
78 "mnemonic": "Genrule",
79 "configurationId": 1,
80 "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86_64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86_64.S"],
81 "environmentVariables": [{
82 "key": "PATH",
83 "value": "/bin:/usr/bin:/usr/local/bin"
84 }],
85 "inputDepSetIds": [3],
86 "outputIds": [6],
87 "primaryOutputId": 6
88 }, {
89 "targetId": 4,
90 "actionKey": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
91 "mnemonic": "Genrule",
92 "configurationId": 1,
93 "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm64.S"],
94 "environmentVariables": [{
95 "key": "PATH",
96 "value": "/bin:/usr/bin:/usr/local/bin"
97 }],
98 "inputDepSetIds": [4],
99 "outputIds": [7],
100 "primaryOutputId": 7
101 }],
102 "targets": [{
103 "id": 1,
104 "label": "@sourceroot//bionic/libc:syscalls-arm",
105 "ruleClassId": 1
106 }, {
107 "id": 2,
108 "label": "@sourceroot//bionic/libc:syscalls-x86",
109 "ruleClassId": 1
110 }, {
111 "id": 3,
112 "label": "@sourceroot//bionic/libc:syscalls-x86_64",
113 "ruleClassId": 1
114 }, {
115 "id": 4,
116 "label": "@sourceroot//bionic/libc:syscalls-arm64",
117 "ruleClassId": 1
118 }],
119 "depSetOfFiles": [{
120 "id": 1,
121 "directArtifactIds": [1, 2, 3]
122 }, {
123 "id": 2,
124 "directArtifactIds": [1, 2, 3]
125 }, {
126 "id": 3,
127 "directArtifactIds": [1, 2, 3]
128 }, {
129 "id": 4,
130 "directArtifactIds": [1, 2, 3]
131 }],
132 "configuration": [{
133 "id": 1,
134 "mnemonic": "k8-fastbuild",
135 "platformName": "k8",
136 "checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
137 }],
138 "ruleClasses": [{
139 "id": 1,
140 "name": "genrule"
141 }],
142 "pathFragments": [{
143 "id": 5,
144 "label": ".."
145 }, {
146 "id": 4,
147 "label": "sourceroot",
148 "parentId": 5
149 }, {
150 "id": 3,
151 "label": "bionic",
152 "parentId": 4
153 }, {
154 "id": 2,
155 "label": "libc",
156 "parentId": 3
157 }, {
158 "id": 1,
159 "label": "SYSCALLS.TXT",
160 "parentId": 2
161 }, {
162 "id": 7,
163 "label": "tools",
164 "parentId": 2
165 }, {
166 "id": 6,
167 "label": "gensyscalls.py",
168 "parentId": 7
169 }, {
170 "id": 11,
171 "label": "bazel_tools",
172 "parentId": 5
173 }, {
174 "id": 10,
175 "label": "tools",
176 "parentId": 11
177 }, {
178 "id": 9,
179 "label": "genrule",
180 "parentId": 10
181 }, {
182 "id": 8,
183 "label": "genrule-setup.sh",
184 "parentId": 9
185 }, {
186 "id": 18,
187 "label": "bazel-out"
188 }, {
189 "id": 17,
190 "label": "sourceroot",
191 "parentId": 18
192 }, {
193 "id": 16,
194 "label": "k8-fastbuild",
195 "parentId": 17
196 }, {
197 "id": 15,
198 "label": "bin",
199 "parentId": 16
200 }, {
201 "id": 14,
202 "label": "bionic",
203 "parentId": 15
204 }, {
205 "id": 13,
206 "label": "libc",
207 "parentId": 14
208 }, {
209 "id": 12,
210 "label": "syscalls-arm.S",
211 "parentId": 13
212 }, {
213 "id": 19,
214 "label": "syscalls-x86.S",
215 "parentId": 13
216 }, {
217 "id": 20,
218 "label": "syscalls-x86_64.S",
219 "parentId": 13
220 }, {
221 "id": 21,
222 "label": "syscalls-arm64.S",
223 "parentId": 13
224 }]
225}`
226 actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString))
227 expectedBuildStatements := []BuildStatement{}
228 for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
229 expectedBuildStatements = append(expectedBuildStatements,
230 BuildStatement{
231 Command: fmt.Sprintf(
232 "/bin/bash -c 'source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py %s ../sourceroot/bionic/libc/SYSCALLS.TXT > bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S'",
233 arch, arch),
234 OutputPaths: []string{
235 fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
236 },
237 InputPaths: []string{
238 "../sourceroot/bionic/libc/SYSCALLS.TXT",
239 "../sourceroot/bionic/libc/tools/gensyscalls.py",
240 "../bazel_tools/tools/genrule/genrule-setup.sh",
241 },
242 Env: []KeyValuePair{
243 KeyValuePair{Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
244 },
245 Mnemonic: "Genrule",
246 })
247 }
248 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
249}
250
251func TestInvalidOutputId(t *testing.T) {
252 const inputString = `
253{
254 "artifacts": [{
255 "id": 1,
256 "pathFragmentId": 1
257 }, {
258 "id": 2,
259 "pathFragmentId": 2
260 }],
261 "actions": [{
262 "targetId": 1,
263 "actionKey": "x",
264 "mnemonic": "x",
265 "arguments": ["touch", "foo"],
266 "inputDepSetIds": [1],
267 "outputIds": [3],
268 "primaryOutputId": 3
269 }],
270 "depSetOfFiles": [{
271 "id": 1,
272 "directArtifactIds": [1, 2]
273 }],
274 "pathFragments": [{
275 "id": 1,
276 "label": "one"
277 }, {
278 "id": 2,
279 "label": "two"
280 }]
281}`
282
283 _, err := AqueryBuildStatements([]byte(inputString))
284 assertError(t, err, "undefined outputId 3")
285}
286
287func TestInvalidInputDepsetId(t *testing.T) {
288 const inputString = `
289{
290 "artifacts": [{
291 "id": 1,
292 "pathFragmentId": 1
293 }, {
294 "id": 2,
295 "pathFragmentId": 2
296 }],
297 "actions": [{
298 "targetId": 1,
299 "actionKey": "x",
300 "mnemonic": "x",
301 "arguments": ["touch", "foo"],
302 "inputDepSetIds": [2],
303 "outputIds": [1],
304 "primaryOutputId": 1
305 }],
306 "depSetOfFiles": [{
307 "id": 1,
308 "directArtifactIds": [1, 2]
309 }],
310 "pathFragments": [{
311 "id": 1,
312 "label": "one"
313 }, {
314 "id": 2,
315 "label": "two"
316 }]
317}`
318
319 _, err := AqueryBuildStatements([]byte(inputString))
320 assertError(t, err, "undefined input depsetId 2")
321}
322
323func TestInvalidInputArtifactId(t *testing.T) {
324 const inputString = `
325{
326 "artifacts": [{
327 "id": 1,
328 "pathFragmentId": 1
329 }, {
330 "id": 2,
331 "pathFragmentId": 2
332 }],
333 "actions": [{
334 "targetId": 1,
335 "actionKey": "x",
336 "mnemonic": "x",
337 "arguments": ["touch", "foo"],
338 "inputDepSetIds": [1],
339 "outputIds": [1],
340 "primaryOutputId": 1
341 }],
342 "depSetOfFiles": [{
343 "id": 1,
344 "directArtifactIds": [1, 3]
345 }],
346 "pathFragments": [{
347 "id": 1,
348 "label": "one"
349 }, {
350 "id": 2,
351 "label": "two"
352 }]
353}`
354
355 _, err := AqueryBuildStatements([]byte(inputString))
356 assertError(t, err, "undefined input artifactId 3")
357}
358
359func TestInvalidPathFragmentId(t *testing.T) {
360 const inputString = `
361{
362 "artifacts": [{
363 "id": 1,
364 "pathFragmentId": 1
365 }, {
366 "id": 2,
367 "pathFragmentId": 2
368 }],
369 "actions": [{
370 "targetId": 1,
371 "actionKey": "x",
372 "mnemonic": "x",
373 "arguments": ["touch", "foo"],
374 "inputDepSetIds": [1],
375 "outputIds": [1],
376 "primaryOutputId": 1
377 }],
378 "depSetOfFiles": [{
379 "id": 1,
380 "directArtifactIds": [1, 2]
381 }],
382 "pathFragments": [{
383 "id": 1,
384 "label": "one"
385 }, {
386 "id": 2,
387 "label": "two",
388 "parentId": 3
389 }]
390}`
391
392 _, err := AqueryBuildStatements([]byte(inputString))
393 assertError(t, err, "undefined path fragment id 3")
394}
395
Liz Kammerde116852021-03-25 16:42:37 -0400396func TestDepfiles(t *testing.T) {
397 const inputString = `
398{
399 "artifacts": [{
400 "id": 1,
401 "pathFragmentId": 1
402 }, {
403 "id": 2,
404 "pathFragmentId": 2
405 }, {
406 "id": 3,
407 "pathFragmentId": 3
408 }],
409 "actions": [{
410 "targetId": 1,
411 "actionKey": "x",
412 "mnemonic": "x",
413 "arguments": ["touch", "foo"],
414 "inputDepSetIds": [1],
415 "outputIds": [2, 3],
416 "primaryOutputId": 2
417 }],
418 "depSetOfFiles": [{
419 "id": 1,
420 "directArtifactIds": [1, 2, 3]
421 }],
422 "pathFragments": [{
423 "id": 1,
424 "label": "one"
425 }, {
426 "id": 2,
427 "label": "two"
428 }, {
429 "id": 3,
430 "label": "two.d"
431 }]
432}`
433
434 actual, err := AqueryBuildStatements([]byte(inputString))
435 if err != nil {
436 t.Errorf("Unexpected error %q", err)
437 }
438 if expected := 1; len(actual) != expected {
439 t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
440 }
441
442 bs := actual[0]
443 expectedDepfile := "two.d"
444 if bs.Depfile == nil {
445 t.Errorf("Expected depfile %q, but there was none found", expectedDepfile)
446 } else if *bs.Depfile != expectedDepfile {
447 t.Errorf("Expected depfile %q, but got %q", expectedDepfile, *bs.Depfile)
448 }
449}
450
451func TestMultipleDepfiles(t *testing.T) {
452 const inputString = `
453{
454 "artifacts": [{
455 "id": 1,
456 "pathFragmentId": 1
457 }, {
458 "id": 2,
459 "pathFragmentId": 2
460 }, {
461 "id": 3,
462 "pathFragmentId": 3
463 }, {
464 "id": 4,
465 "pathFragmentId": 4
466 }],
467 "actions": [{
468 "targetId": 1,
469 "actionKey": "x",
470 "mnemonic": "x",
471 "arguments": ["touch", "foo"],
472 "inputDepSetIds": [1],
473 "outputIds": [2,3,4],
474 "primaryOutputId": 2
475 }],
476 "depSetOfFiles": [{
477 "id": 1,
478 "directArtifactIds": [1, 2, 3, 4]
479 }],
480 "pathFragments": [{
481 "id": 1,
482 "label": "one"
483 }, {
484 "id": 2,
485 "label": "two"
486 }, {
487 "id": 3,
488 "label": "two.d"
489 }, {
490 "id": 4,
491 "label": "other.d"
492 }]
493}`
494
495 _, err := AqueryBuildStatements([]byte(inputString))
496 assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
497}
498
Chris Parsons943f2432021-01-19 11:36:50 -0500499func TestTransitiveInputDepsets(t *testing.T) {
500 // The input aquery for this test comes from a proof-of-concept starlark rule which registers
501 // a single action with many inputs given via a deep depset.
502 const inputString = `
503{
504 "artifacts": [{
505 "id": 1,
506 "pathFragmentId": 1
507 }, {
508 "id": 2,
509 "pathFragmentId": 7
510 }, {
511 "id": 3,
512 "pathFragmentId": 8
513 }, {
514 "id": 4,
515 "pathFragmentId": 9
516 }, {
517 "id": 5,
518 "pathFragmentId": 10
519 }, {
520 "id": 6,
521 "pathFragmentId": 11
522 }, {
523 "id": 7,
524 "pathFragmentId": 12
525 }, {
526 "id": 8,
527 "pathFragmentId": 13
528 }, {
529 "id": 9,
530 "pathFragmentId": 14
531 }, {
532 "id": 10,
533 "pathFragmentId": 15
534 }, {
535 "id": 11,
536 "pathFragmentId": 16
537 }, {
538 "id": 12,
539 "pathFragmentId": 17
540 }, {
541 "id": 13,
542 "pathFragmentId": 18
543 }, {
544 "id": 14,
545 "pathFragmentId": 19
546 }, {
547 "id": 15,
548 "pathFragmentId": 20
549 }, {
550 "id": 16,
551 "pathFragmentId": 21
552 }, {
553 "id": 17,
554 "pathFragmentId": 22
555 }, {
556 "id": 18,
557 "pathFragmentId": 23
558 }, {
559 "id": 19,
560 "pathFragmentId": 24
561 }, {
562 "id": 20,
563 "pathFragmentId": 25
564 }, {
565 "id": 21,
566 "pathFragmentId": 26
567 }],
568 "actions": [{
569 "targetId": 1,
570 "actionKey": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
571 "mnemonic": "Action",
572 "configurationId": 1,
573 "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"],
574 "inputDepSetIds": [1],
575 "outputIds": [21],
576 "primaryOutputId": 21
577 }],
578 "depSetOfFiles": [{
579 "id": 3,
580 "directArtifactIds": [1, 2, 3, 4, 5]
581 }, {
582 "id": 4,
583 "directArtifactIds": [6, 7, 8, 9, 10]
584 }, {
585 "id": 2,
586 "transitiveDepSetIds": [3, 4],
587 "directArtifactIds": [11, 12, 13, 14, 15]
588 }, {
589 "id": 5,
590 "directArtifactIds": [16, 17, 18, 19]
591 }, {
592 "id": 1,
593 "transitiveDepSetIds": [2, 5],
594 "directArtifactIds": [20]
595 }],
596 "pathFragments": [{
597 "id": 6,
598 "label": "bazel-out"
599 }, {
600 "id": 5,
601 "label": "sourceroot",
602 "parentId": 6
603 }, {
604 "id": 4,
605 "label": "k8-fastbuild",
606 "parentId": 5
607 }, {
608 "id": 3,
609 "label": "bin",
610 "parentId": 4
611 }, {
612 "id": 2,
613 "label": "testpkg",
614 "parentId": 3
615 }, {
616 "id": 1,
617 "label": "test_1",
618 "parentId": 2
619 }, {
620 "id": 7,
621 "label": "test_2",
622 "parentId": 2
623 }, {
624 "id": 8,
625 "label": "test_3",
626 "parentId": 2
627 }, {
628 "id": 9,
629 "label": "test_4",
630 "parentId": 2
631 }, {
632 "id": 10,
633 "label": "test_5",
634 "parentId": 2
635 }, {
636 "id": 11,
637 "label": "test_6",
638 "parentId": 2
639 }, {
640 "id": 12,
641 "label": "test_7",
642 "parentId": 2
643 }, {
644 "id": 13,
645 "label": "test_8",
646 "parentId": 2
647 }, {
648 "id": 14,
649 "label": "test_9",
650 "parentId": 2
651 }, {
652 "id": 15,
653 "label": "test_10",
654 "parentId": 2
655 }, {
656 "id": 16,
657 "label": "test_11",
658 "parentId": 2
659 }, {
660 "id": 17,
661 "label": "test_12",
662 "parentId": 2
663 }, {
664 "id": 18,
665 "label": "test_13",
666 "parentId": 2
667 }, {
668 "id": 19,
669 "label": "test_14",
670 "parentId": 2
671 }, {
672 "id": 20,
673 "label": "test_15",
674 "parentId": 2
675 }, {
676 "id": 21,
677 "label": "test_16",
678 "parentId": 2
679 }, {
680 "id": 22,
681 "label": "test_17",
682 "parentId": 2
683 }, {
684 "id": 23,
685 "label": "test_18",
686 "parentId": 2
687 }, {
688 "id": 24,
689 "label": "test_19",
690 "parentId": 2
691 }, {
692 "id": 25,
693 "label": "test_root",
694 "parentId": 2
695 }, {
696 "id": 26,
697 "label": "test_out",
698 "parentId": 2
699 }]
700}`
701
702 actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString))
703 // Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
704 // are given via a deep depset, but the depset is flattened when returned as a
705 // BuildStatement slice.
706 inputPaths := []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root"}
707 for i := 1; i < 20; i++ {
708 inputPaths = append(inputPaths, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
709 }
710 expectedBuildStatements := []BuildStatement{
711 BuildStatement{
712 Command: "/bin/bash -c touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out",
713 OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
714 InputPaths: inputPaths,
715 Mnemonic: "Action",
716 },
717 }
718 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
719}
720
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400721func TestMiddlemenAction(t *testing.T) {
722 const inputString = `
723{
724 "artifacts": [{
725 "id": 1,
726 "pathFragmentId": 1
727 }, {
728 "id": 2,
729 "pathFragmentId": 2
730 }, {
731 "id": 3,
732 "pathFragmentId": 3
733 }, {
734 "id": 4,
735 "pathFragmentId": 4
736 }, {
737 "id": 5,
738 "pathFragmentId": 5
739 }, {
740 "id": 6,
741 "pathFragmentId": 6
742 }],
743 "pathFragments": [{
744 "id": 1,
745 "label": "middleinput_one"
746 }, {
747 "id": 2,
748 "label": "middleinput_two"
749 }, {
750 "id": 3,
751 "label": "middleman_artifact"
752 }, {
753 "id": 4,
754 "label": "maininput_one"
755 }, {
756 "id": 5,
757 "label": "maininput_two"
758 }, {
759 "id": 6,
760 "label": "output"
761 }],
762 "depSetOfFiles": [{
763 "id": 1,
764 "directArtifactIds": [1, 2]
765 }, {
766 "id": 2,
767 "directArtifactIds": [3, 4, 5]
768 }],
769 "actions": [{
770 "targetId": 1,
771 "actionKey": "x",
772 "mnemonic": "Middleman",
773 "arguments": ["touch", "foo"],
774 "inputDepSetIds": [1],
775 "outputIds": [3],
776 "primaryOutputId": 3
777 }, {
778 "targetId": 2,
779 "actionKey": "y",
780 "mnemonic": "Main action",
781 "arguments": ["touch", "foo"],
782 "inputDepSetIds": [2],
783 "outputIds": [6],
784 "primaryOutputId": 6
785 }]
786}`
787
788 actual, err := AqueryBuildStatements([]byte(inputString))
789 if err != nil {
790 t.Errorf("Unexpected error %q", err)
791 }
792 if expected := 1; len(actual) != expected {
793 t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
794 }
795
796 bs := actual[0]
797 expectedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
798 if !reflect.DeepEqual(bs.InputPaths, expectedInputs) {
799 t.Errorf("Expected main action inputs %q, but got %q", expectedInputs, bs.InputPaths)
800 }
801
802 expectedOutputs := []string{"output"}
803 if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) {
804 t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths)
805 }
806}
807
Liz Kammerc49e6822021-06-08 15:04:11 -0400808func TestSimpleSymlink(t *testing.T) {
809 const inputString = `
810{
811 "artifacts": [{
812 "id": 1,
813 "pathFragmentId": 3
814 }, {
815 "id": 2,
816 "pathFragmentId": 5
817 }],
818 "actions": [{
819 "targetId": 1,
820 "actionKey": "x",
821 "mnemonic": "Symlink",
822 "inputDepSetIds": [1],
823 "outputIds": [2],
824 "primaryOutputId": 2
825 }],
826 "depSetOfFiles": [{
827 "id": 1,
828 "directArtifactIds": [1]
829 }],
830 "pathFragments": [{
831 "id": 1,
832 "label": "one"
833 }, {
834 "id": 2,
835 "label": "file_subdir",
836 "parentId": 1
837 }, {
838 "id": 3,
839 "label": "file",
840 "parentId": 2
841 }, {
842 "id": 4,
843 "label": "symlink_subdir",
844 "parentId": 1
845 }, {
846 "id": 5,
847 "label": "symlink",
848 "parentId": 4
849 }]
850}`
851
852 actual, err := AqueryBuildStatements([]byte(inputString))
853
854 if err != nil {
855 t.Errorf("Unexpected error %q", err)
856 }
857
858 expectedBuildStatements := []BuildStatement{
859 BuildStatement{
860 Command: "mkdir -p one/symlink_subdir && " +
861 "rm -f one/symlink_subdir/symlink && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400862 "ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
Liz Kammerc49e6822021-06-08 15:04:11 -0400863 InputPaths: []string{"one/file_subdir/file"},
864 OutputPaths: []string{"one/symlink_subdir/symlink"},
865 SymlinkPaths: []string{"one/symlink_subdir/symlink"},
866 Mnemonic: "Symlink",
867 },
868 }
869 assertBuildStatements(t, actual, expectedBuildStatements)
870}
871
872func TestSymlinkQuotesPaths(t *testing.T) {
873 const inputString = `
874{
875 "artifacts": [{
876 "id": 1,
877 "pathFragmentId": 3
878 }, {
879 "id": 2,
880 "pathFragmentId": 5
881 }],
882 "actions": [{
883 "targetId": 1,
884 "actionKey": "x",
885 "mnemonic": "SolibSymlink",
886 "inputDepSetIds": [1],
887 "outputIds": [2],
888 "primaryOutputId": 2
889 }],
890 "depSetOfFiles": [{
891 "id": 1,
892 "directArtifactIds": [1]
893 }],
894 "pathFragments": [{
895 "id": 1,
896 "label": "one"
897 }, {
898 "id": 2,
899 "label": "file subdir",
900 "parentId": 1
901 }, {
902 "id": 3,
903 "label": "file",
904 "parentId": 2
905 }, {
906 "id": 4,
907 "label": "symlink subdir",
908 "parentId": 1
909 }, {
910 "id": 5,
911 "label": "symlink",
912 "parentId": 4
913 }]
914}`
915
916 actual, err := AqueryBuildStatements([]byte(inputString))
917
918 if err != nil {
919 t.Errorf("Unexpected error %q", err)
920 }
921
922 expectedBuildStatements := []BuildStatement{
923 BuildStatement{
924 Command: "mkdir -p 'one/symlink subdir' && " +
925 "rm -f 'one/symlink subdir/symlink' && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400926 "ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
Liz Kammerc49e6822021-06-08 15:04:11 -0400927 InputPaths: []string{"one/file subdir/file"},
928 OutputPaths: []string{"one/symlink subdir/symlink"},
929 SymlinkPaths: []string{"one/symlink subdir/symlink"},
930 Mnemonic: "SolibSymlink",
931 },
932 }
Liz Kammerc7737782021-11-04 10:56:13 -0400933 assertBuildStatements(t, expectedBuildStatements, actual)
Liz Kammerc49e6822021-06-08 15:04:11 -0400934}
935
936func TestSymlinkMultipleInputs(t *testing.T) {
937 const inputString = `
938{
939 "artifacts": [{
940 "id": 1,
941 "pathFragmentId": 1
942 }, {
943 "id": 2,
944 "pathFragmentId": 2
945 }, {
946 "id": 3,
947 "pathFragmentId": 3
948 }],
949 "actions": [{
950 "targetId": 1,
951 "actionKey": "x",
952 "mnemonic": "Symlink",
953 "inputDepSetIds": [1],
954 "outputIds": [3],
955 "primaryOutputId": 3
956 }],
957 "depSetOfFiles": [{
958 "id": 1,
959 "directArtifactIds": [1,2]
960 }],
961 "pathFragments": [{
962 "id": 1,
963 "label": "file"
964 }, {
965 "id": 2,
966 "label": "other_file"
967 }, {
968 "id": 3,
969 "label": "symlink"
970 }]
971}`
972
973 _, err := AqueryBuildStatements([]byte(inputString))
974 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
975}
976
977func TestSymlinkMultipleOutputs(t *testing.T) {
978 const inputString = `
979{
980 "artifacts": [{
981 "id": 1,
982 "pathFragmentId": 1
983 }, {
984 "id": 2,
985 "pathFragmentId": 2
986 }, {
987 "id": 3,
988 "pathFragmentId": 3
989 }],
990 "actions": [{
991 "targetId": 1,
992 "actionKey": "x",
993 "mnemonic": "Symlink",
994 "inputDepSetIds": [1],
995 "outputIds": [2,3],
996 "primaryOutputId": 2
997 }],
998 "depSetOfFiles": [{
999 "id": 1,
1000 "directArtifactIds": [1]
1001 }],
1002 "pathFragments": [{
1003 "id": 1,
1004 "label": "file"
1005 }, {
1006 "id": 2,
1007 "label": "symlink"
1008 }, {
1009 "id": 3,
1010 "label": "other_symlink"
1011 }]
1012}`
1013
1014 _, err := AqueryBuildStatements([]byte(inputString))
1015 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`)
1016}
1017
Chris Parsons4f069892021-01-15 12:22:41 -05001018func assertError(t *testing.T, err error, expected string) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001019 t.Helper()
Chris Parsons943f2432021-01-19 11:36:50 -05001020 if err == nil {
1021 t.Errorf("expected error '%s', but got no error", expected)
1022 } else if err.Error() != expected {
Liz Kammerc49e6822021-06-08 15:04:11 -04001023 t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
Chris Parsons4f069892021-01-15 12:22:41 -05001024 }
1025}
1026
1027// Asserts that the given actual build statements match the given expected build statements.
1028// Build statement equivalence is determined using buildStatementEquals.
1029func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001030 t.Helper()
Chris Parsons4f069892021-01-15 12:22:41 -05001031 if len(expected) != len(actual) {
Liz Kammerde116852021-03-25 16:42:37 -04001032 t.Errorf("expected %d build statements, but got %d,\n expected: %v,\n actual: %v",
Chris Parsons4f069892021-01-15 12:22:41 -05001033 len(expected), len(actual), expected, actual)
1034 return
1035 }
1036ACTUAL_LOOP:
1037 for _, actualStatement := range actual {
1038 for _, expectedStatement := range expected {
1039 if buildStatementEquals(actualStatement, expectedStatement) {
1040 continue ACTUAL_LOOP
1041 }
1042 }
Liz Kammerde116852021-03-25 16:42:37 -04001043 t.Errorf("unexpected build statement %v.\n expected: %v",
Chris Parsons4f069892021-01-15 12:22:41 -05001044 actualStatement, expected)
1045 return
1046 }
1047}
1048
1049func buildStatementEquals(first BuildStatement, second BuildStatement) bool {
1050 if first.Mnemonic != second.Mnemonic {
1051 return false
1052 }
1053 if first.Command != second.Command {
1054 return false
1055 }
1056 // Ordering is significant for environment variables.
1057 if !reflect.DeepEqual(first.Env, second.Env) {
1058 return false
1059 }
1060 // Ordering is irrelevant for input and output paths, so compare sets.
1061 if !reflect.DeepEqual(stringSet(first.InputPaths), stringSet(second.InputPaths)) {
1062 return false
1063 }
1064 if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) {
1065 return false
1066 }
Liz Kammerc49e6822021-06-08 15:04:11 -04001067 if !reflect.DeepEqual(stringSet(first.SymlinkPaths), stringSet(second.SymlinkPaths)) {
1068 return false
1069 }
1070 if first.Depfile != second.Depfile {
1071 return false
1072 }
Chris Parsons4f069892021-01-15 12:22:41 -05001073 return true
1074}
1075
1076func stringSet(stringSlice []string) map[string]struct{} {
1077 stringMap := make(map[string]struct{})
1078 for _, s := range stringSlice {
1079 stringMap[s] = struct{}{}
1080 }
1081 return stringMap
1082}