• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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
396func 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
499func 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
721func 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
808func 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 && " +
862				"ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
863			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' && " +
926				"ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
927			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	}
933	assertBuildStatements(t, expectedBuildStatements, actual)
934}
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
1018func TestTemplateExpandActionSubstitutions(t *testing.T) {
1019	const inputString = `
1020{
1021  "artifacts": [{
1022    "id": 1,
1023    "pathFragmentId": 1
1024  }],
1025  "actions": [{
1026    "targetId": 1,
1027    "actionKey": "x",
1028    "mnemonic": "TemplateExpand",
1029    "configurationId": 1,
1030    "outputIds": [1],
1031    "primaryOutputId": 1,
1032    "executionPlatform": "//build/bazel/platforms:linux_x86_64",
1033    "templateContent": "Test template substitutions: %token1%, %python_binary%",
1034    "substitutions": [{
1035      "key": "%token1%",
1036      "value": "abcd"
1037    },{
1038      "key": "%python_binary%",
1039      "value": "python3"
1040    }]
1041  }],
1042  "pathFragments": [{
1043    "id": 1,
1044    "label": "template_file"
1045  }]
1046}`
1047
1048	actual, err := AqueryBuildStatements([]byte(inputString))
1049
1050	if err != nil {
1051		t.Errorf("Unexpected error %q", err)
1052	}
1053
1054	expectedBuildStatements := []BuildStatement{
1055		BuildStatement{
1056			Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
1057				"chmod a+x template_file'",
1058			OutputPaths: []string{"template_file"},
1059			Mnemonic:    "TemplateExpand",
1060		},
1061	}
1062	assertBuildStatements(t, expectedBuildStatements, actual)
1063}
1064
1065func TestTemplateExpandActionNoOutput(t *testing.T) {
1066	const inputString = `
1067{
1068  "artifacts": [{
1069    "id": 1,
1070    "pathFragmentId": 1
1071  }],
1072  "actions": [{
1073    "targetId": 1,
1074    "actionKey": "x",
1075    "mnemonic": "TemplateExpand",
1076    "configurationId": 1,
1077    "primaryOutputId": 1,
1078    "executionPlatform": "//build/bazel/platforms:linux_x86_64",
1079    "templateContent": "Test template substitutions: %token1%, %python_binary%",
1080    "substitutions": [{
1081      "key": "%token1%",
1082      "value": "abcd"
1083    },{
1084      "key": "%python_binary%",
1085      "value": "python3"
1086    }]
1087  }],
1088  "pathFragments": [{
1089    "id": 1,
1090    "label": "template_file"
1091  }]
1092}`
1093
1094	_, err := AqueryBuildStatements([]byte(inputString))
1095	assertError(t, err, `Expect 1 output to template expand action, got: output []`)
1096}
1097
1098func TestPythonZipperActionSuccess(t *testing.T) {
1099	const inputString = `
1100{
1101  "artifacts": [{
1102    "id": 1,
1103    "pathFragmentId": 1
1104  },{
1105    "id": 2,
1106    "pathFragmentId": 2
1107  },{
1108    "id": 3,
1109    "pathFragmentId": 3
1110  },{
1111    "id": 4,
1112    "pathFragmentId": 4
1113  },{
1114    "id": 5,
1115    "pathFragmentId": 10
1116  },{
1117    "id": 10,
1118    "pathFragmentId": 20
1119  }],
1120  "actions": [{
1121    "targetId": 1,
1122    "actionKey": "x",
1123    "mnemonic": "TemplateExpand",
1124    "configurationId": 1,
1125    "outputIds": [1],
1126    "primaryOutputId": 1,
1127    "executionPlatform": "//build/bazel/platforms:linux_x86_64",
1128    "templateContent": "Test template substitutions: %token1%, %python_binary%",
1129    "substitutions": [{
1130      "key": "%token1%",
1131      "value": "abcd"
1132    },{
1133      "key": "%python_binary%",
1134      "value": "python3"
1135    }]
1136  },{
1137    "targetId": 1,
1138    "actionKey": "x",
1139    "mnemonic": "PythonZipper",
1140    "configurationId": 1,
1141    "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
1142    "outputIds": [2],
1143    "inputDepSetIds": [1],
1144    "primaryOutputId": 2
1145  }],
1146  "depSetOfFiles": [{
1147    "id": 1,
1148    "directArtifactIds": [4, 3, 5]
1149  }],
1150  "pathFragments": [{
1151    "id": 1,
1152    "label": "python_binary"
1153  },{
1154    "id": 2,
1155    "label": "python_binary.zip"
1156  },{
1157    "id": 3,
1158    "label": "python_binary.py"
1159  },{
1160    "id": 9,
1161    "label": ".."
1162  }, {
1163    "id": 8,
1164    "label": "bazel_tools",
1165    "parentId": 9
1166  }, {
1167    "id": 7,
1168    "label": "tools",
1169    "parentId": 8
1170  }, {
1171    "id": 6,
1172    "label": "zip",
1173    "parentId": 7
1174  }, {
1175    "id": 5,
1176    "label": "zipper",
1177    "parentId": 6
1178  }, {
1179    "id": 4,
1180    "label": "zipper",
1181    "parentId": 5
1182  },{
1183    "id": 16,
1184    "label": "bazel-out"
1185  },{
1186    "id": 15,
1187    "label": "bazel_tools",
1188    "parentId": 16
1189  }, {
1190    "id": 14,
1191    "label": "k8-fastbuild",
1192    "parentId": 15
1193  }, {
1194    "id": 13,
1195    "label": "bin",
1196    "parentId": 14
1197  }, {
1198    "id": 12,
1199    "label": "tools",
1200    "parentId": 13
1201  }, {
1202    "id": 11,
1203    "label": "python",
1204    "parentId": 12
1205  }, {
1206    "id": 10,
1207    "label": "py3wrapper.sh",
1208    "parentId": 11
1209  },{
1210    "id": 20,
1211    "label": "python_binary"
1212  }]
1213}`
1214	actual, err := AqueryBuildStatements([]byte(inputString))
1215
1216	if err != nil {
1217		t.Errorf("Unexpected error %q", err)
1218	}
1219
1220	expectedBuildStatements := []BuildStatement{
1221		BuildStatement{
1222			Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > python_binary && " +
1223				"chmod a+x python_binary'",
1224			InputPaths:  []string{"python_binary.zip"},
1225			OutputPaths: []string{"python_binary"},
1226			Mnemonic:    "TemplateExpand",
1227		},
1228		BuildStatement{
1229			Command: "../bazel_tools/tools/zip/zipper/zipper cC python_binary.zip __main__.py=bazel-out/k8-fastbuild/bin/python_binary.temp " +
1230				"__init__.py= runfiles/__main__/__init__.py= runfiles/__main__/python_binary.py=python_binary.py  && " +
1231				"../bazel_tools/tools/zip/zipper/zipper x python_binary.zip -d python_binary.runfiles && ln -sf runfiles/__main__ python_binary.runfiles",
1232			InputPaths:  []string{"../bazel_tools/tools/zip/zipper/zipper", "python_binary.py"},
1233			OutputPaths: []string{"python_binary.zip"},
1234			Mnemonic:    "PythonZipper",
1235		},
1236	}
1237	assertBuildStatements(t, expectedBuildStatements, actual)
1238}
1239
1240func TestPythonZipperActionNoInput(t *testing.T) {
1241	const inputString = `
1242{
1243  "artifacts": [{
1244    "id": 1,
1245    "pathFragmentId": 1
1246  },{
1247    "id": 2,
1248    "pathFragmentId": 2
1249  }],
1250  "actions": [{
1251    "targetId": 1,
1252    "actionKey": "x",
1253    "mnemonic": "PythonZipper",
1254    "configurationId": 1,
1255    "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
1256    "outputIds": [2],
1257    "primaryOutputId": 2
1258  }],
1259  "pathFragments": [{
1260    "id": 1,
1261    "label": "python_binary"
1262  },{
1263    "id": 2,
1264    "label": "python_binary.zip"
1265  }]
1266}`
1267	_, err := AqueryBuildStatements([]byte(inputString))
1268	assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input [], output ["python_binary.zip"]`)
1269}
1270
1271func TestPythonZipperActionNoOutput(t *testing.T) {
1272	const inputString = `
1273{
1274  "artifacts": [{
1275    "id": 1,
1276    "pathFragmentId": 1
1277  },{
1278    "id": 2,
1279    "pathFragmentId": 2
1280  },{
1281    "id": 3,
1282    "pathFragmentId": 3
1283  },{
1284    "id": 4,
1285    "pathFragmentId": 4
1286  },{
1287    "id": 5,
1288    "pathFragmentId": 10
1289  }],
1290  "actions": [{
1291    "targetId": 1,
1292    "actionKey": "x",
1293    "mnemonic": "PythonZipper",
1294    "configurationId": 1,
1295    "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
1296    "inputDepSetIds": [1]
1297  }],
1298  "depSetOfFiles": [{
1299    "id": 1,
1300    "directArtifactIds": [4, 3, 5]
1301  }],
1302  "pathFragments": [{
1303    "id": 1,
1304    "label": "python_binary"
1305  },{
1306    "id": 2,
1307    "label": "python_binary.zip"
1308  },{
1309    "id": 3,
1310    "label": "python_binary.py"
1311  },{
1312    "id": 9,
1313    "label": ".."
1314  }, {
1315    "id": 8,
1316    "label": "bazel_tools",
1317    "parentId": 9
1318  }, {
1319    "id": 7,
1320    "label": "tools",
1321    "parentId": 8
1322  }, {
1323    "id": 6,
1324    "label": "zip",
1325    "parentId": 7
1326  }, {
1327    "id": 5,
1328    "label": "zipper",
1329    "parentId": 6
1330  }, {
1331    "id": 4,
1332    "label": "zipper",
1333    "parentId": 5
1334  },{
1335    "id": 16,
1336    "label": "bazel-out"
1337  },{
1338    "id": 15,
1339    "label": "bazel_tools",
1340    "parentId": 16
1341  }, {
1342    "id": 14,
1343    "label": "k8-fastbuild",
1344    "parentId": 15
1345  }, {
1346    "id": 13,
1347    "label": "bin",
1348    "parentId": 14
1349  }, {
1350    "id": 12,
1351    "label": "tools",
1352    "parentId": 13
1353  }, {
1354    "id": 11,
1355    "label": "python",
1356    "parentId": 12
1357  }, {
1358    "id": 10,
1359    "label": "py3wrapper.sh",
1360    "parentId": 11
1361  }]
1362}`
1363	_, err := AqueryBuildStatements([]byte(inputString))
1364	assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input ["../bazel_tools/tools/zip/zipper/zipper" "python_binary.py"], output []`)
1365}
1366
1367func assertError(t *testing.T, err error, expected string) {
1368	t.Helper()
1369	if err == nil {
1370		t.Errorf("expected error '%s', but got no error", expected)
1371	} else if err.Error() != expected {
1372		t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
1373	}
1374}
1375
1376// Asserts that the given actual build statements match the given expected build statements.
1377// Build statement equivalence is determined using buildStatementEquals.
1378func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
1379	t.Helper()
1380	if len(expected) != len(actual) {
1381		t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
1382			len(expected), len(actual), expected, actual)
1383		return
1384	}
1385ACTUAL_LOOP:
1386	for _, actualStatement := range actual {
1387		for _, expectedStatement := range expected {
1388			if buildStatementEquals(actualStatement, expectedStatement) {
1389				continue ACTUAL_LOOP
1390			}
1391		}
1392		t.Errorf("unexpected build statement %#v.\n expected: %#v",
1393			actualStatement, expected)
1394		return
1395	}
1396}
1397
1398func buildStatementEquals(first BuildStatement, second BuildStatement) bool {
1399	if first.Mnemonic != second.Mnemonic {
1400		return false
1401	}
1402	if first.Command != second.Command {
1403		return false
1404	}
1405	// Ordering is significant for environment variables.
1406	if !reflect.DeepEqual(first.Env, second.Env) {
1407		return false
1408	}
1409	// Ordering is irrelevant for input and output paths, so compare sets.
1410	if !reflect.DeepEqual(stringSet(first.InputPaths), stringSet(second.InputPaths)) {
1411		return false
1412	}
1413	if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) {
1414		return false
1415	}
1416	if !reflect.DeepEqual(stringSet(first.SymlinkPaths), stringSet(second.SymlinkPaths)) {
1417		return false
1418	}
1419	if first.Depfile != second.Depfile {
1420		return false
1421	}
1422	return true
1423}
1424
1425func stringSet(stringSlice []string) map[string]struct{} {
1426	stringMap := make(map[string]struct{})
1427	for _, s := range stringSlice {
1428		stringMap[s] = struct{}{}
1429	}
1430	return stringMap
1431}
1432