blob: 4d1503ea1eace9164b116838d8135422836cdace [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 (
Jason Wu118fd2b2022-10-27 18:41:15 +000018 "encoding/json"
Chris Parsons4f069892021-01-15 12:22:41 -050019 "fmt"
20 "reflect"
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -040021 "sort"
Chris Parsons4f069892021-01-15 12:22:41 -050022 "testing"
Jason Wu118fd2b2022-10-27 18:41:15 +000023
24 "google.golang.org/protobuf/proto"
25 analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
Chris Parsons4f069892021-01-15 12:22:41 -050026)
27
28func TestAqueryMultiArchGenrule(t *testing.T) {
29 // This input string is retrieved from a real build of bionic-related genrules.
30 const inputString = `
31{
Jason Wu118fd2b2022-10-27 18:41:15 +000032 "Artifacts": [
33 { "Id": 1, "path_fragment_id": 1 },
34 { "Id": 2, "path_fragment_id": 6 },
35 { "Id": 3, "path_fragment_id": 8 },
36 { "Id": 4, "path_fragment_id": 12 },
37 { "Id": 5, "path_fragment_id": 19 },
38 { "Id": 6, "path_fragment_id": 20 },
39 { "Id": 7, "path_fragment_id": 21 }],
40 "Actions": [{
41 "target_id": 1,
42 "action_key": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
43 "Mnemonic": "Genrule",
44 "configuration_id": 1,
45 "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"],
46 "environment_variables": [{
47 "Key": "PATH",
48 "Value": "/bin:/usr/bin:/usr/local/bin"
49 }],
50 "input_dep_set_ids": [1],
51 "output_ids": [4],
52 "primary_output_id": 4
53 }, {
54 "target_id": 2,
55 "action_key": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
56 "Mnemonic": "Genrule",
57 "configuration_id": 1,
58 "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"],
59 "environment_variables": [{
60 "Key": "PATH",
61 "Value": "/bin:/usr/bin:/usr/local/bin"
62 }],
63 "input_dep_set_ids": [2],
64 "output_ids": [5],
65 "primary_output_id": 5
66 }, {
67 "target_id": 3,
68 "action_key": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
69 "Mnemonic": "Genrule",
70 "configuration_id": 1,
71 "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"],
72 "environment_variables": [{
73 "Key": "PATH",
74 "Value": "/bin:/usr/bin:/usr/local/bin"
75 }],
76 "input_dep_set_ids": [3],
77 "output_ids": [6],
78 "primary_output_id": 6
79 }, {
80 "target_id": 4,
81 "action_key": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
82 "Mnemonic": "Genrule",
83 "configuration_id": 1,
84 "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"],
85 "environment_variables": [{
86 "Key": "PATH",
87 "Value": "/bin:/usr/bin:/usr/local/bin"
88 }],
89 "input_dep_set_ids": [4],
90 "output_ids": [7],
91 "primary_output_id": 7
92 }],
93 "Targets": [
94 { "Id": 1, "Label": "@sourceroot//bionic/libc:syscalls-arm", "rule_class_id": 1 },
95 { "Id": 2, "Label": "@sourceroot//bionic/libc:syscalls-x86", "rule_class_id": 1 },
96 { "Id": 3, "Label": "@sourceroot//bionic/libc:syscalls-x86_64", "rule_class_id": 1 },
97 { "Id": 4, "Label": "@sourceroot//bionic/libc:syscalls-arm64", "rule_class_id": 1 }],
98 "dep_set_of_files": [
99 { "Id": 1, "direct_artifact_ids": [1, 2, 3] },
100 { "Id": 2, "direct_artifact_ids": [1, 2, 3] },
101 { "Id": 3, "direct_artifact_ids": [1, 2, 3] },
102 { "Id": 4, "direct_artifact_ids": [1, 2, 3] }],
103 "Configuration": [{
104 "Id": 1,
105 "Mnemonic": "k8-fastbuild",
106 "platform_name": "k8",
107 "Checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
108 }],
109 "rule_classes": [{ "Id": 1, "Name": "genrule"}],
110 "path_fragments": [
111 { "Id": 5, "Label": ".." },
112 { "Id": 4, "Label": "sourceroot", "parent_id": 5 },
113 { "Id": 3, "Label": "bionic", "parent_id": 4 },
114 { "Id": 2, "Label": "libc", "parent_id": 3 },
115 { "Id": 1, "Label": "SYSCALLS.TXT", "parent_id": 2 },
116 { "Id": 7, "Label": "tools", "parent_id": 2 },
117 { "Id": 6, "Label": "gensyscalls.py", "parent_id": 7 },
118 { "Id": 11, "Label": "bazel_tools", "parent_id": 5 },
119 { "Id": 10, "Label": "tools", "parent_id": 11 },
120 { "Id": 9, "Label": "genrule", "parent_id": 10 },
121 { "Id": 8, "Label": "genrule-setup.sh", "parent_id": 9 },
122 { "Id": 18, "Label": "bazel-out" },
123 { "Id": 17, "Label": "sourceroot", "parent_id": 18 },
124 { "Id": 16, "Label": "k8-fastbuild", "parent_id": 17 },
125 { "Id": 15, "Label": "bin", "parent_id": 16 },
126 { "Id": 14, "Label": "bionic", "parent_id": 15 },
127 { "Id": 13, "Label": "libc", "parent_id": 14 },
128 { "Id": 12, "Label": "syscalls-arm.S", "parent_id": 13 },
129 { "Id": 19, "Label": "syscalls-x86.S", "parent_id": 13 },
130 { "Id": 20, "Label": "syscalls-x86_64.S", "parent_id": 13 },
131 { "Id": 21, "Label": "syscalls-arm64.S", "parent_id": 13 }]
132}
133`
134 data, err := JsonToActionGraphContainer(inputString)
135 if err != nil {
136 t.Error(err)
137 return
138 }
139 actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
Usta Shrestha16ac1352022-06-22 11:01:55 -0400140 var expectedBuildStatements []BuildStatement
Chris Parsons4f069892021-01-15 12:22:41 -0500141 for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
142 expectedBuildStatements = append(expectedBuildStatements,
143 BuildStatement{
144 Command: fmt.Sprintf(
145 "/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'",
146 arch, arch),
147 OutputPaths: []string{
148 fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
149 },
Chris Parsons4f069892021-01-15 12:22:41 -0500150 Env: []KeyValuePair{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400151 {Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
Chris Parsons4f069892021-01-15 12:22:41 -0500152 },
153 Mnemonic: "Genrule",
154 })
155 }
156 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400157
158 expectedFlattenedInputs := []string{
159 "../sourceroot/bionic/libc/SYSCALLS.TXT",
160 "../sourceroot/bionic/libc/tools/gensyscalls.py",
Chris Parsons1a7aca02022-04-25 22:35:15 -0400161 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400162 // In this example, each depset should have the same expected inputs.
163 for _, actualDepset := range actualDepsets {
164 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
165 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
166 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
167 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400168 }
Chris Parsons4f069892021-01-15 12:22:41 -0500169}
170
171func TestInvalidOutputId(t *testing.T) {
172 const inputString = `
173{
Jason Wu118fd2b2022-10-27 18:41:15 +0000174 "artifacts": [
175 { "id": 1, "path_fragment_id": 1 },
176 { "id": 2, "path_fragment_id": 2 }],
177 "actions": [{
178 "target_id": 1,
179 "action_key": "x",
180 "mnemonic": "x",
181 "arguments": ["touch", "foo"],
182 "input_dep_set_ids": [1],
183 "output_ids": [3],
184 "primary_output_id": 3
185 }],
186 "dep_set_of_files": [
187 { "id": 1, "direct_artifact_ids": [1, 2] }],
188 "path_fragments": [
189 { "id": 1, "label": "one" },
190 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500191}`
192
Jason Wu118fd2b2022-10-27 18:41:15 +0000193 data, err := JsonToActionGraphContainer(inputString)
194 if err != nil {
195 t.Error(err)
196 return
197 }
198 _, _, err = AqueryBuildStatements(data)
Chris Parsons4f069892021-01-15 12:22:41 -0500199 assertError(t, err, "undefined outputId 3")
200}
201
Chris Parsons1a7aca02022-04-25 22:35:15 -0400202func TestInvalidInputDepsetIdFromAction(t *testing.T) {
Chris Parsons4f069892021-01-15 12:22:41 -0500203 const inputString = `
204{
Jason Wu118fd2b2022-10-27 18:41:15 +0000205 "artifacts": [
206 { "id": 1, "path_fragment_id": 1 },
207 { "id": 2, "path_fragment_id": 2 }],
208 "actions": [{
209 "target_id": 1,
210 "action_key": "x",
211 "mnemonic": "x",
212 "arguments": ["touch", "foo"],
213 "input_dep_set_ids": [2],
214 "output_ids": [1],
215 "primary_output_id": 1
216 }],
217 "dep_set_of_files": [
218 { "id": 1, "direct_artifact_ids": [1, 2] }],
219 "path_fragments": [
220 { "id": 1, "label": "one" },
221 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500222}`
223
Jason Wu118fd2b2022-10-27 18:41:15 +0000224 data, err := JsonToActionGraphContainer(inputString)
225 if err != nil {
226 t.Error(err)
227 return
228 }
229 _, _, err = AqueryBuildStatements(data)
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500230 assertError(t, err, "undefined (not even empty) input depsetId 2")
Chris Parsons4f069892021-01-15 12:22:41 -0500231}
232
Chris Parsons1a7aca02022-04-25 22:35:15 -0400233func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
234 const inputString = `
235{
Jason Wu118fd2b2022-10-27 18:41:15 +0000236 "artifacts": [
237 { "id": 1, "path_fragment_id": 1 },
238 { "id": 2, "path_fragment_id": 2 }],
239 "actions": [{
240 "target_id": 1,
241 "action_key": "x",
242 "mnemonic": "x",
243 "arguments": ["touch", "foo"],
244 "input_dep_set_ids": [1],
245 "output_ids": [1],
246 "primary_output_id": 1
247 }],
248 "dep_set_of_files": [
249 { "id": 1, "direct_artifact_ids": [1, 2], "transitive_dep_set_ids": [42] }],
250 "path_fragments": [
251 { "id": 1, "label": "one"},
252 { "id": 2, "label": "two" }]
Chris Parsons1a7aca02022-04-25 22:35:15 -0400253}`
254
Jason Wu118fd2b2022-10-27 18:41:15 +0000255 data, err := JsonToActionGraphContainer(inputString)
256 if err != nil {
257 t.Error(err)
258 return
259 }
260 _, _, err = AqueryBuildStatements(data)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400261 assertError(t, err, "undefined input depsetId 42 (referenced by depsetId 1)")
262}
263
Chris Parsons4f069892021-01-15 12:22:41 -0500264func TestInvalidInputArtifactId(t *testing.T) {
265 const inputString = `
266{
Jason Wu118fd2b2022-10-27 18:41:15 +0000267 "artifacts": [
268 { "id": 1, "path_fragment_id": 1 },
269 { "id": 2, "path_fragment_id": 2 }],
270 "actions": [{
271 "target_id": 1,
272 "action_key": "x",
273 "mnemonic": "x",
274 "arguments": ["touch", "foo"],
275 "input_dep_set_ids": [1],
276 "output_ids": [1],
277 "primary_output_id": 1
278 }],
279 "dep_set_of_files": [
280 { "id": 1, "direct_artifact_ids": [1, 3] }],
281 "path_fragments": [
282 { "id": 1, "label": "one" },
283 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500284}`
285
Jason Wu118fd2b2022-10-27 18:41:15 +0000286 data, err := JsonToActionGraphContainer(inputString)
287 if err != nil {
288 t.Error(err)
289 return
290 }
291 _, _, err = AqueryBuildStatements(data)
Chris Parsons4f069892021-01-15 12:22:41 -0500292 assertError(t, err, "undefined input artifactId 3")
293}
294
295func TestInvalidPathFragmentId(t *testing.T) {
296 const inputString = `
297{
Jason Wu118fd2b2022-10-27 18:41:15 +0000298 "artifacts": [
299 { "id": 1, "path_fragment_id": 1 },
300 { "id": 2, "path_fragment_id": 2 }],
301 "actions": [{
302 "target_id": 1,
303 "action_key": "x",
304 "mnemonic": "x",
305 "arguments": ["touch", "foo"],
306 "input_dep_set_ids": [1],
307 "output_ids": [1],
308 "primary_output_id": 1
309 }],
310 "dep_set_of_files": [
311 { "id": 1, "direct_artifact_ids": [1, 2] }],
312 "path_fragments": [
313 { "id": 1, "label": "one" },
314 { "id": 2, "label": "two", "parent_id": 3 }]
Chris Parsons4f069892021-01-15 12:22:41 -0500315}`
316
Jason Wu118fd2b2022-10-27 18:41:15 +0000317 data, err := JsonToActionGraphContainer(inputString)
318 if err != nil {
319 t.Error(err)
320 return
321 }
322 _, _, err = AqueryBuildStatements(data)
Chris Parsons4f069892021-01-15 12:22:41 -0500323 assertError(t, err, "undefined path fragment id 3")
324}
325
Liz Kammerde116852021-03-25 16:42:37 -0400326func TestDepfiles(t *testing.T) {
327 const inputString = `
328{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700329 "artifacts": [
Jason Wu118fd2b2022-10-27 18:41:15 +0000330 { "id": 1, "path_fragment_id": 1 },
331 { "id": 2, "path_fragment_id": 2 },
332 { "id": 3, "path_fragment_id": 3 }],
Liz Kammerde116852021-03-25 16:42:37 -0400333 "actions": [{
Jason Wu118fd2b2022-10-27 18:41:15 +0000334 "target_Id": 1,
335 "action_Key": "x",
Liz Kammerde116852021-03-25 16:42:37 -0400336 "mnemonic": "x",
337 "arguments": ["touch", "foo"],
Jason Wu118fd2b2022-10-27 18:41:15 +0000338 "input_dep_set_ids": [1],
339 "output_ids": [2, 3],
340 "primary_output_id": 2
Liz Kammerde116852021-03-25 16:42:37 -0400341 }],
Jason Wu118fd2b2022-10-27 18:41:15 +0000342 "dep_set_of_files": [
343 { "id": 1, "direct_Artifact_Ids": [1, 2, 3] }],
344 "path_fragments": [
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700345 { "id": 1, "label": "one" },
346 { "id": 2, "label": "two" },
347 { "id": 3, "label": "two.d" }]
Liz Kammerde116852021-03-25 16:42:37 -0400348}`
349
Jason Wu118fd2b2022-10-27 18:41:15 +0000350 data, err := JsonToActionGraphContainer(inputString)
351 if err != nil {
352 t.Error(err)
353 return
354 }
355 actual, _, err := AqueryBuildStatements(data)
Liz Kammerde116852021-03-25 16:42:37 -0400356 if err != nil {
357 t.Errorf("Unexpected error %q", err)
358 }
359 if expected := 1; len(actual) != expected {
360 t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
361 }
362
363 bs := actual[0]
364 expectedDepfile := "two.d"
365 if bs.Depfile == nil {
366 t.Errorf("Expected depfile %q, but there was none found", expectedDepfile)
367 } else if *bs.Depfile != expectedDepfile {
368 t.Errorf("Expected depfile %q, but got %q", expectedDepfile, *bs.Depfile)
369 }
370}
371
372func TestMultipleDepfiles(t *testing.T) {
373 const inputString = `
374{
Jason Wu118fd2b2022-10-27 18:41:15 +0000375 "artifacts": [
376 { "id": 1, "path_fragment_id": 1 },
377 { "id": 2, "path_fragment_id": 2 },
378 { "id": 3, "path_fragment_id": 3 },
379 { "id": 4, "path_fragment_id": 4 }],
380 "actions": [{
381 "target_id": 1,
382 "action_key": "x",
383 "mnemonic": "x",
384 "arguments": ["touch", "foo"],
385 "input_dep_set_ids": [1],
386 "output_ids": [2,3,4],
387 "primary_output_id": 2
388 }],
389 "dep_set_of_files": [{
390 "id": 1,
391 "direct_artifact_ids": [1, 2, 3, 4]
392 }],
393 "path_fragments": [
394 { "id": 1, "label": "one" },
395 { "id": 2, "label": "two" },
396 { "id": 3, "label": "two.d" },
397 { "id": 4, "label": "other.d" }]
Liz Kammerde116852021-03-25 16:42:37 -0400398}`
399
Jason Wu118fd2b2022-10-27 18:41:15 +0000400 data, err := JsonToActionGraphContainer(inputString)
401 if err != nil {
402 t.Error(err)
403 return
404 }
405 _, _, err = AqueryBuildStatements(data)
Liz Kammerde116852021-03-25 16:42:37 -0400406 assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
407}
408
Chris Parsons943f2432021-01-19 11:36:50 -0500409func TestTransitiveInputDepsets(t *testing.T) {
410 // The input aquery for this test comes from a proof-of-concept starlark rule which registers
411 // a single action with many inputs given via a deep depset.
412 const inputString = `
413{
Jason Wu118fd2b2022-10-27 18:41:15 +0000414 "artifacts": [
415 { "id": 1, "path_fragment_id": 1 },
416 { "id": 2, "path_fragment_id": 7 },
417 { "id": 3, "path_fragment_id": 8 },
418 { "id": 4, "path_fragment_id": 9 },
419 { "id": 5, "path_fragment_id": 10 },
420 { "id": 6, "path_fragment_id": 11 },
421 { "id": 7, "path_fragment_id": 12 },
422 { "id": 8, "path_fragment_id": 13 },
423 { "id": 9, "path_fragment_id": 14 },
424 { "id": 10, "path_fragment_id": 15 },
425 { "id": 11, "path_fragment_id": 16 },
426 { "id": 12, "path_fragment_id": 17 },
427 { "id": 13, "path_fragment_id": 18 },
428 { "id": 14, "path_fragment_id": 19 },
429 { "id": 15, "path_fragment_id": 20 },
430 { "id": 16, "path_fragment_id": 21 },
431 { "id": 17, "path_fragment_id": 22 },
432 { "id": 18, "path_fragment_id": 23 },
433 { "id": 19, "path_fragment_id": 24 },
434 { "id": 20, "path_fragment_id": 25 },
435 { "id": 21, "path_fragment_id": 26 }],
436 "actions": [{
437 "target_id": 1,
438 "action_key": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
439 "mnemonic": "Action",
440 "configuration_id": 1,
441 "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"],
442 "input_dep_set_ids": [1],
443 "output_ids": [21],
444 "primary_output_id": 21
445 }],
446 "dep_set_of_files": [
447 { "id": 3, "direct_artifact_ids": [1, 2, 3, 4, 5] },
448 { "id": 4, "direct_artifact_ids": [6, 7, 8, 9, 10] },
449 { "id": 2, "transitive_dep_set_ids": [3, 4], "direct_artifact_ids": [11, 12, 13, 14, 15] },
450 { "id": 5, "direct_artifact_ids": [16, 17, 18, 19] },
451 { "id": 1, "transitive_dep_set_ids": [2, 5], "direct_artifact_ids": [20] }],
452 "path_fragments": [
453 { "id": 6, "label": "bazel-out" },
454 { "id": 5, "label": "sourceroot", "parent_id": 6 },
455 { "id": 4, "label": "k8-fastbuild", "parent_id": 5 },
456 { "id": 3, "label": "bin", "parent_id": 4 },
457 { "id": 2, "label": "testpkg", "parent_id": 3 },
458 { "id": 1, "label": "test_1", "parent_id": 2 },
459 { "id": 7, "label": "test_2", "parent_id": 2 },
460 { "id": 8, "label": "test_3", "parent_id": 2 },
461 { "id": 9, "label": "test_4", "parent_id": 2 },
462 { "id": 10, "label": "test_5", "parent_id": 2 },
463 { "id": 11, "label": "test_6", "parent_id": 2 },
464 { "id": 12, "label": "test_7", "parent_id": 2 },
465 { "id": 13, "label": "test_8", "parent_id": 2 },
466 { "id": 14, "label": "test_9", "parent_id": 2 },
467 { "id": 15, "label": "test_10", "parent_id": 2 },
468 { "id": 16, "label": "test_11", "parent_id": 2 },
469 { "id": 17, "label": "test_12", "parent_id": 2 },
470 { "id": 18, "label": "test_13", "parent_id": 2 },
471 { "id": 19, "label": "test_14", "parent_id": 2 },
472 { "id": 20, "label": "test_15", "parent_id": 2 },
473 { "id": 21, "label": "test_16", "parent_id": 2 },
474 { "id": 22, "label": "test_17", "parent_id": 2 },
475 { "id": 23, "label": "test_18", "parent_id": 2 },
476 { "id": 24, "label": "test_19", "parent_id": 2 },
477 { "id": 25, "label": "test_root", "parent_id": 2 },
478 { "id": 26,"label": "test_out", "parent_id": 2 }]
Chris Parsons943f2432021-01-19 11:36:50 -0500479}`
480
Jason Wu118fd2b2022-10-27 18:41:15 +0000481 data, err := JsonToActionGraphContainer(inputString)
482 if err != nil {
483 t.Error(err)
484 return
485 }
486 actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400487
Chris Parsons943f2432021-01-19 11:36:50 -0500488 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400489 {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400490 Command: "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
491 OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
492 Mnemonic: "Action",
Chris Parsons943f2432021-01-19 11:36:50 -0500493 },
494 }
495 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400496
497 // Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
498 // are given via a deep depset, but the depset is flattened when returned as a
499 // BuildStatement slice.
Usta Shrestha16ac1352022-06-22 11:01:55 -0400500 var expectedFlattenedInputs []string
Chris Parsons1a7aca02022-04-25 22:35:15 -0400501 for i := 1; i < 20; i++ {
502 expectedFlattenedInputs = append(expectedFlattenedInputs, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
503 }
504 expectedFlattenedInputs = append(expectedFlattenedInputs, "bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root")
505
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400506 actualDepsetHashes := actualbuildStatements[0].InputDepsetHashes
507 actualFlattenedInputs := flattenDepsets(actualDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400508 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
509 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
510 }
Chris Parsons943f2432021-01-19 11:36:50 -0500511}
512
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700513func TestSymlinkTree(t *testing.T) {
514 const inputString = `
515{
Jason Wu118fd2b2022-10-27 18:41:15 +0000516 "artifacts": [
517 { "id": 1, "path_fragment_id": 1 },
518 { "id": 2, "path_fragment_id": 2 }],
519 "actions": [{
520 "target_id": 1,
521 "action_key": "x",
522 "mnemonic": "SymlinkTree",
523 "configuration_id": 1,
524 "input_dep_set_ids": [1],
525 "output_ids": [2],
526 "primary_output_id": 2,
527 "execution_platform": "//build/bazel/platforms:linux_x86_64"
528 }],
529 "path_fragments": [
530 { "id": 1, "label": "foo.manifest" },
531 { "id": 2, "label": "foo.runfiles/MANIFEST" }],
532 "dep_set_of_files": [
533 { "id": 1, "direct_artifact_ids": [1] }]
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700534}
535`
Jason Wu118fd2b2022-10-27 18:41:15 +0000536 data, err := JsonToActionGraphContainer(inputString)
537 if err != nil {
538 t.Error(err)
539 return
540 }
541 actual, _, err := AqueryBuildStatements(data)
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700542 if err != nil {
543 t.Errorf("Unexpected error %q", err)
544 }
545 assertBuildStatements(t, []BuildStatement{
546 {
547 Command: "",
548 OutputPaths: []string{"foo.runfiles/MANIFEST"},
549 Mnemonic: "SymlinkTree",
550 InputPaths: []string{"foo.manifest"},
551 },
552 }, actual)
553}
554
Usta Shresthaef922252022-06-02 14:23:02 -0400555func TestBazelOutRemovalFromInputDepsets(t *testing.T) {
556 const inputString = `{
Jason Wu118fd2b2022-10-27 18:41:15 +0000557 "artifacts": [
558 { "id": 1, "path_fragment_id": 10 },
559 { "id": 2, "path_fragment_id": 20 },
560 { "id": 3, "path_fragment_id": 30 },
561 { "id": 4, "path_fragment_id": 40 }],
562 "dep_set_of_files": [{
563 "id": 1111,
564 "direct_artifact_ids": [3 , 4]
565 }, {
566 "id": 2222,
567 "direct_artifact_ids": [3]
568 }],
569 "actions": [{
570 "target_id": 100,
571 "action_key": "x",
572 "input_dep_set_ids": [1111, 2222],
573 "mnemonic": "x",
574 "arguments": ["bogus", "command"],
575 "output_ids": [2],
576 "primary_output_id": 1
577 }],
578 "path_fragments": [
579 { "id": 10, "label": "input" },
580 { "id": 20, "label": "output" },
581 { "id": 30, "label": "dep1", "parent_id": 50 },
582 { "id": 40, "label": "dep2", "parent_id": 60 },
583 { "id": 50, "label": "bazel_tools", "parent_id": 60 },
584 { "id": 60, "label": ".."}
585 ]
Usta Shresthaef922252022-06-02 14:23:02 -0400586}`
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500587 /* depsets
588 1111 2222
589 / \ |
590 ../dep2 ../bazel_tools/dep1
591 */
Jason Wu118fd2b2022-10-27 18:41:15 +0000592 data, err := JsonToActionGraphContainer(inputString)
593 if err != nil {
594 t.Error(err)
595 return
596 }
597 actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500598 if len(actualDepsets) != 1 {
Usta Shresthaef922252022-06-02 14:23:02 -0400599 t.Errorf("expected 1 depset but found %#v", actualDepsets)
600 return
601 }
602 dep2Found := false
603 for _, dep := range flattenDepsets([]string{actualDepsets[0].ContentHash}, actualDepsets) {
604 if dep == "../bazel_tools/dep1" {
605 t.Errorf("dependency %s expected to be removed but still exists", dep)
606 } else if dep == "../dep2" {
607 dep2Found = true
608 }
609 }
610 if !dep2Found {
611 t.Errorf("dependency ../dep2 expected but not found")
612 }
613
614 expectedBuildStatement := BuildStatement{
615 Command: "bogus command",
616 OutputPaths: []string{"output"},
617 Mnemonic: "x",
618 }
619 buildStatementFound := false
620 for _, actualBuildStatement := range actualBuildStatements {
621 if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
622 buildStatementFound = true
623 break
624 }
625 }
626 if !buildStatementFound {
627 t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
628 return
629 }
630}
631
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500632func TestBazelOutRemovalFromTransitiveInputDepsets(t *testing.T) {
633 const inputString = `{
634 "artifacts": [
635 { "id": 1, "path_fragment_id": 10 },
636 { "id": 2, "path_fragment_id": 20 },
637 { "id": 3, "path_fragment_id": 30 }],
638 "dep_set_of_files": [{
639 "id": 1111,
640 "transitive_dep_set_ids": [2222]
641 }, {
642 "id": 2222,
643 "direct_artifact_ids": [3]
644 }, {
645 "id": 3333,
646 "direct_artifact_ids": [3]
647 }, {
648 "id": 4444,
649 "transitive_dep_set_ids": [3333]
650 }],
651 "actions": [{
652 "target_id": 100,
653 "action_key": "x",
654 "input_dep_set_ids": [1111, 4444],
655 "mnemonic": "x",
656 "arguments": ["bogus", "command"],
657 "output_ids": [2],
658 "primary_output_id": 1
659 }],
660 "path_fragments": [
661 { "id": 10, "label": "input" },
662 { "id": 20, "label": "output" },
663 { "id": 30, "label": "dep", "parent_id": 50 },
664 { "id": 50, "label": "bazel_tools", "parent_id": 60 },
665 { "id": 60, "label": ".."}
666 ]
667}`
668 /* depsets
669 1111 4444
670 || ||
671 2222 3333
672 | |
673 ../bazel_tools/dep
674 Note: in dep_set_of_files:
675 1111 appears BEFORE its dependency,2222 while
676 4444 appears AFTER its dependency 3333
677 and this test shows that that order doesn't affect empty depset pruning
678 */
679 data, err := JsonToActionGraphContainer(inputString)
680 if err != nil {
681 t.Error(err)
682 return
683 }
684 actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
685 if len(actualDepsets) != 0 {
686 t.Errorf("expected 0 depsets but found %#v", actualDepsets)
687 return
688 }
689
690 expectedBuildStatement := BuildStatement{
691 Command: "bogus command",
692 OutputPaths: []string{"output"},
693 Mnemonic: "x",
694 }
695 buildStatementFound := false
696 for _, actualBuildStatement := range actualBuildStatements {
697 if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
698 buildStatementFound = true
699 break
700 }
701 }
702 if !buildStatementFound {
703 t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
704 return
705 }
706}
707
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400708func TestMiddlemenAction(t *testing.T) {
709 const inputString = `
710{
Jason Wu118fd2b2022-10-27 18:41:15 +0000711 "artifacts": [
712 { "id": 1, "path_fragment_id": 1 },
713 { "id": 2, "path_fragment_id": 2 },
714 { "id": 3, "path_fragment_id": 3 },
715 { "id": 4, "path_fragment_id": 4 },
716 { "id": 5, "path_fragment_id": 5 },
717 { "id": 6, "path_fragment_id": 6 }],
718 "path_fragments": [
719 { "id": 1, "label": "middleinput_one" },
720 { "id": 2, "label": "middleinput_two" },
721 { "id": 3, "label": "middleman_artifact" },
722 { "id": 4, "label": "maininput_one" },
723 { "id": 5, "label": "maininput_two" },
724 { "id": 6, "label": "output" }],
725 "dep_set_of_files": [
726 { "id": 1, "direct_artifact_ids": [1, 2] },
727 { "id": 2, "direct_artifact_ids": [3, 4, 5] }],
728 "actions": [{
729 "target_id": 1,
730 "action_key": "x",
731 "mnemonic": "Middleman",
732 "arguments": ["touch", "foo"],
733 "input_dep_set_ids": [1],
734 "output_ids": [3],
735 "primary_output_id": 3
736 }, {
737 "target_id": 2,
738 "action_key": "y",
739 "mnemonic": "Main action",
740 "arguments": ["touch", "foo"],
741 "input_dep_set_ids": [2],
742 "output_ids": [6],
743 "primary_output_id": 6
744 }]
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400745}`
Jason Wu118fd2b2022-10-27 18:41:15 +0000746 data, err := JsonToActionGraphContainer(inputString)
747 if err != nil {
748 t.Error(err)
749 return
750 }
751 actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400752 if err != nil {
753 t.Errorf("Unexpected error %q", err)
754 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400755 if expected := 1; len(actualBuildStatements) != expected {
756 t.Fatalf("Expected %d build statements, got %d", expected, len(actualBuildStatements))
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400757 }
758
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400759 expectedDepsetFiles := [][]string{
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400760 {"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"},
761 {"middleinput_one", "middleinput_two"},
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400762 }
763 assertFlattenedDepsets(t, actualDepsets, expectedDepsetFiles)
764
Chris Parsons1a7aca02022-04-25 22:35:15 -0400765 bs := actualBuildStatements[0]
766 if len(bs.InputPaths) > 0 {
767 t.Errorf("Expected main action raw inputs to be empty, but got %q", bs.InputPaths)
768 }
769
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400770 expectedOutputs := []string{"output"}
771 if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) {
772 t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths)
773 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400774
Chris Parsons1a7aca02022-04-25 22:35:15 -0400775 expectedFlattenedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400776 actualFlattenedInputs := flattenDepsets(bs.InputDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400777
778 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
779 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
780 }
781}
782
783// Returns the contents of given depsets in concatenated post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400784func flattenDepsets(depsetHashesToFlatten []string, allDepsets []AqueryDepset) []string {
785 depsetsByHash := map[string]AqueryDepset{}
Chris Parsons1a7aca02022-04-25 22:35:15 -0400786 for _, depset := range allDepsets {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400787 depsetsByHash[depset.ContentHash] = depset
Chris Parsons1a7aca02022-04-25 22:35:15 -0400788 }
Usta Shrestha16ac1352022-06-22 11:01:55 -0400789 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400790 for _, depsetId := range depsetHashesToFlatten {
791 result = append(result, flattenDepset(depsetId, depsetsByHash)...)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400792 }
793 return result
794}
795
796// Returns the contents of a given depset in post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400797func flattenDepset(depsetHashToFlatten string, allDepsets map[string]AqueryDepset) []string {
798 depset := allDepsets[depsetHashToFlatten]
Usta Shrestha16ac1352022-06-22 11:01:55 -0400799 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400800 for _, depsetId := range depset.TransitiveDepSetHashes {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400801 result = append(result, flattenDepset(depsetId, allDepsets)...)
802 }
803 result = append(result, depset.DirectArtifacts...)
804 return result
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400805}
806
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400807func assertFlattenedDepsets(t *testing.T, actualDepsets []AqueryDepset, expectedDepsetFiles [][]string) {
808 t.Helper()
809 if len(actualDepsets) != len(expectedDepsetFiles) {
Sasha Smundakf10c3ac2022-06-08 11:46:31 -0700810 t.Errorf("Expected %d depsets, but got %d depsets", len(expectedDepsetFiles), len(actualDepsets))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400811 }
812 for i, actualDepset := range actualDepsets {
813 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
814 if !reflect.DeepEqual(actualFlattenedInputs, expectedDepsetFiles[i]) {
815 t.Errorf("Expected depset files: %v, but got %v", expectedDepsetFiles[i], actualFlattenedInputs)
816 }
817 }
818}
819
Liz Kammerc49e6822021-06-08 15:04:11 -0400820func TestSimpleSymlink(t *testing.T) {
821 const inputString = `
822{
Jason Wu118fd2b2022-10-27 18:41:15 +0000823 "artifacts": [
824 { "id": 1, "path_fragment_id": 3 },
825 { "id": 2, "path_fragment_id": 5 }],
826 "actions": [{
827 "target_id": 1,
828 "action_key": "x",
829 "mnemonic": "Symlink",
830 "input_dep_set_ids": [1],
831 "output_ids": [2],
832 "primary_output_id": 2
833 }],
834 "dep_set_of_files": [
835 { "id": 1, "direct_artifact_ids": [1] }],
836 "path_fragments": [
837 { "id": 1, "label": "one" },
838 { "id": 2, "label": "file_subdir", "parent_id": 1 },
839 { "id": 3, "label": "file", "parent_id": 2 },
840 { "id": 4, "label": "symlink_subdir", "parent_id": 1 },
841 { "id": 5, "label": "symlink", "parent_id": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400842}`
Jason Wu118fd2b2022-10-27 18:41:15 +0000843 data, err := JsonToActionGraphContainer(inputString)
844 if err != nil {
845 t.Error(err)
846 return
847 }
848 actual, _, err := AqueryBuildStatements(data)
Liz Kammerc49e6822021-06-08 15:04:11 -0400849
850 if err != nil {
851 t.Errorf("Unexpected error %q", err)
852 }
853
854 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400855 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400856 Command: "mkdir -p one/symlink_subdir && " +
857 "rm -f one/symlink_subdir/symlink && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400858 "ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
Liz Kammerc49e6822021-06-08 15:04:11 -0400859 InputPaths: []string{"one/file_subdir/file"},
860 OutputPaths: []string{"one/symlink_subdir/symlink"},
861 SymlinkPaths: []string{"one/symlink_subdir/symlink"},
862 Mnemonic: "Symlink",
863 },
864 }
865 assertBuildStatements(t, actual, expectedBuildStatements)
866}
867
868func TestSymlinkQuotesPaths(t *testing.T) {
869 const inputString = `
870{
Jason Wu118fd2b2022-10-27 18:41:15 +0000871 "artifacts": [
872 { "id": 1, "path_fragment_id": 3 },
873 { "id": 2, "path_fragment_id": 5 }],
874 "actions": [{
875 "target_id": 1,
876 "action_key": "x",
877 "mnemonic": "SolibSymlink",
878 "input_dep_set_ids": [1],
879 "output_ids": [2],
880 "primary_output_id": 2
881 }],
882 "dep_set_of_files": [
883 { "id": 1, "direct_artifact_ids": [1] }],
884 "path_fragments": [
885 { "id": 1, "label": "one" },
886 { "id": 2, "label": "file subdir", "parent_id": 1 },
887 { "id": 3, "label": "file", "parent_id": 2 },
888 { "id": 4, "label": "symlink subdir", "parent_id": 1 },
889 { "id": 5, "label": "symlink", "parent_id": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400890}`
891
Jason Wu118fd2b2022-10-27 18:41:15 +0000892 data, err := JsonToActionGraphContainer(inputString)
893 if err != nil {
894 t.Error(err)
895 return
896 }
897 actual, _, err := AqueryBuildStatements(data)
Liz Kammerc49e6822021-06-08 15:04:11 -0400898 if err != nil {
899 t.Errorf("Unexpected error %q", err)
900 }
901
902 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400903 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400904 Command: "mkdir -p 'one/symlink subdir' && " +
905 "rm -f 'one/symlink subdir/symlink' && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400906 "ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
Liz Kammerc49e6822021-06-08 15:04:11 -0400907 InputPaths: []string{"one/file subdir/file"},
908 OutputPaths: []string{"one/symlink subdir/symlink"},
909 SymlinkPaths: []string{"one/symlink subdir/symlink"},
910 Mnemonic: "SolibSymlink",
911 },
912 }
Liz Kammerc7737782021-11-04 10:56:13 -0400913 assertBuildStatements(t, expectedBuildStatements, actual)
Liz Kammerc49e6822021-06-08 15:04:11 -0400914}
915
916func TestSymlinkMultipleInputs(t *testing.T) {
917 const inputString = `
918{
Jason Wu118fd2b2022-10-27 18:41:15 +0000919 "artifacts": [
920 { "id": 1, "path_fragment_id": 1 },
921 { "id": 2, "path_fragment_id": 2 },
922 { "id": 3, "path_fragment_id": 3 }],
923 "actions": [{
924 "target_id": 1,
925 "action_key": "x",
926 "mnemonic": "Symlink",
927 "input_dep_set_ids": [1],
928 "output_ids": [3],
929 "primary_output_id": 3
930 }],
931 "dep_set_of_files": [{ "id": 1, "direct_artifact_ids": [1,2] }],
932 "path_fragments": [
933 { "id": 1, "label": "file" },
934 { "id": 2, "label": "other_file" },
935 { "id": 3, "label": "symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400936}`
937
Jason Wu118fd2b2022-10-27 18:41:15 +0000938 data, err := JsonToActionGraphContainer(inputString)
939 if err != nil {
940 t.Error(err)
941 return
942 }
943 _, _, err = AqueryBuildStatements(data)
Liz Kammerc49e6822021-06-08 15:04:11 -0400944 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
945}
946
947func TestSymlinkMultipleOutputs(t *testing.T) {
948 const inputString = `
949{
Jason Wu118fd2b2022-10-27 18:41:15 +0000950 "artifacts": [
951 { "id": 1, "path_fragment_id": 1 },
952 { "id": 3, "path_fragment_id": 3 }],
953 "actions": [{
954 "target_id": 1,
955 "action_key": "x",
956 "mnemonic": "Symlink",
957 "input_dep_set_ids": [1],
958 "output_ids": [2,3],
959 "primary_output_id": 2
960 }],
961 "dep_set_of_files": [
962 { "id": 1, "direct_artifact_ids": [1] }],
963 "path_fragments": [
964 { "id": 1, "label": "file" },
965 { "id": 2, "label": "symlink" },
966 { "id": 3, "label": "other_symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400967}`
968
Jason Wu118fd2b2022-10-27 18:41:15 +0000969 data, err := JsonToActionGraphContainer(inputString)
970 if err != nil {
971 t.Error(err)
972 return
973 }
974 _, _, err = AqueryBuildStatements(data)
975 assertError(t, err, "undefined outputId 2")
Liz Kammerc49e6822021-06-08 15:04:11 -0400976}
977
Wei Li455ba832021-11-04 22:58:12 +0000978func TestTemplateExpandActionSubstitutions(t *testing.T) {
979 const inputString = `
980{
Jason Wu118fd2b2022-10-27 18:41:15 +0000981 "artifacts": [{
982 "id": 1,
983 "path_fragment_id": 1
984 }],
985 "actions": [{
986 "target_id": 1,
987 "action_key": "x",
988 "mnemonic": "TemplateExpand",
989 "configuration_id": 1,
990 "output_ids": [1],
991 "primary_output_id": 1,
992 "execution_platform": "//build/bazel/platforms:linux_x86_64",
993 "template_content": "Test template substitutions: %token1%, %python_binary%",
994 "substitutions": [
995 { "key": "%token1%", "value": "abcd" },
996 { "key": "%python_binary%", "value": "python3" }]
997 }],
998 "path_fragments": [
999 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +00001000}`
1001
Jason Wu118fd2b2022-10-27 18:41:15 +00001002 data, err := JsonToActionGraphContainer(inputString)
1003 if err != nil {
1004 t.Error(err)
1005 return
1006 }
1007 actual, _, err := AqueryBuildStatements(data)
Wei Li455ba832021-11-04 22:58:12 +00001008 if err != nil {
1009 t.Errorf("Unexpected error %q", err)
1010 }
1011
1012 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -04001013 {
Wei Li455ba832021-11-04 22:58:12 +00001014 Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
1015 "chmod a+x template_file'",
1016 OutputPaths: []string{"template_file"},
1017 Mnemonic: "TemplateExpand",
1018 },
1019 }
1020 assertBuildStatements(t, expectedBuildStatements, actual)
1021}
1022
1023func TestTemplateExpandActionNoOutput(t *testing.T) {
1024 const inputString = `
1025{
Jason Wu118fd2b2022-10-27 18:41:15 +00001026 "artifacts": [
1027 { "id": 1, "path_fragment_id": 1 }],
1028 "actions": [{
1029 "target_id": 1,
1030 "action_key": "x",
1031 "mnemonic": "TemplateExpand",
1032 "configuration_id": 1,
1033 "primary_output_id": 1,
1034 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1035 "templateContent": "Test template substitutions: %token1%, %python_binary%",
1036 "substitutions": [
1037 { "key": "%token1%", "value": "abcd" },
1038 { "key": "%python_binary%", "value": "python3" }]
1039 }],
1040 "path_fragments": [
1041 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +00001042}`
1043
Jason Wu118fd2b2022-10-27 18:41:15 +00001044 data, err := JsonToActionGraphContainer(inputString)
1045 if err != nil {
1046 t.Error(err)
1047 return
1048 }
1049 _, _, err = AqueryBuildStatements(data)
Wei Li455ba832021-11-04 22:58:12 +00001050 assertError(t, err, `Expect 1 output to template expand action, got: output []`)
1051}
1052
Sasha Smundak1da064c2022-06-08 16:36:16 -07001053func TestFileWrite(t *testing.T) {
1054 const inputString = `
1055{
Jason Wu118fd2b2022-10-27 18:41:15 +00001056 "artifacts": [
1057 { "id": 1, "path_fragment_id": 1 }],
1058 "actions": [{
1059 "target_id": 1,
1060 "action_key": "x",
1061 "mnemonic": "FileWrite",
1062 "configuration_id": 1,
1063 "output_ids": [1],
1064 "primary_output_id": 1,
1065 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1066 "file_contents": "file data\n"
1067 }],
1068 "path_fragments": [
1069 { "id": 1, "label": "foo.manifest" }]
Sasha Smundak1da064c2022-06-08 16:36:16 -07001070}
1071`
Jason Wu118fd2b2022-10-27 18:41:15 +00001072 data, err := JsonToActionGraphContainer(inputString)
1073 if err != nil {
1074 t.Error(err)
1075 return
1076 }
1077 actual, _, err := AqueryBuildStatements(data)
Sasha Smundak1da064c2022-06-08 16:36:16 -07001078 if err != nil {
1079 t.Errorf("Unexpected error %q", err)
1080 }
1081 assertBuildStatements(t, []BuildStatement{
1082 {
1083 OutputPaths: []string{"foo.manifest"},
1084 Mnemonic: "FileWrite",
1085 FileContents: "file data\n",
1086 },
1087 }, actual)
1088}
1089
1090func TestSourceSymlinkManifest(t *testing.T) {
1091 const inputString = `
1092{
Jason Wu118fd2b2022-10-27 18:41:15 +00001093 "artifacts": [
1094 { "id": 1, "path_fragment_id": 1 }],
1095 "actions": [{
1096 "target_id": 1,
1097 "action_key": "x",
1098 "mnemonic": "SourceSymlinkManifest",
1099 "configuration_id": 1,
1100 "output_ids": [1],
1101 "primary_output_id": 1,
1102 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1103 "file_contents": "symlink target\n"
1104 }],
1105 "path_fragments": [
1106 { "id": 1, "label": "foo.manifest" }]
Sasha Smundak1da064c2022-06-08 16:36:16 -07001107}
1108`
Jason Wu118fd2b2022-10-27 18:41:15 +00001109 data, err := JsonToActionGraphContainer(inputString)
1110 if err != nil {
1111 t.Error(err)
1112 return
1113 }
1114 actual, _, err := AqueryBuildStatements(data)
Sasha Smundak1da064c2022-06-08 16:36:16 -07001115 if err != nil {
1116 t.Errorf("Unexpected error %q", err)
1117 }
1118 assertBuildStatements(t, []BuildStatement{
1119 {
1120 OutputPaths: []string{"foo.manifest"},
1121 Mnemonic: "SourceSymlinkManifest",
1122 },
1123 }, actual)
1124}
1125
Chris Parsons4f069892021-01-15 12:22:41 -05001126func assertError(t *testing.T, err error, expected string) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001127 t.Helper()
Chris Parsons943f2432021-01-19 11:36:50 -05001128 if err == nil {
1129 t.Errorf("expected error '%s', but got no error", expected)
1130 } else if err.Error() != expected {
Liz Kammerc49e6822021-06-08 15:04:11 -04001131 t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
Chris Parsons4f069892021-01-15 12:22:41 -05001132 }
1133}
1134
1135// Asserts that the given actual build statements match the given expected build statements.
1136// Build statement equivalence is determined using buildStatementEquals.
1137func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001138 t.Helper()
Chris Parsons4f069892021-01-15 12:22:41 -05001139 if len(expected) != len(actual) {
Liz Kammerc3192992021-11-16 17:01:11 -05001140 t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
Chris Parsons4f069892021-01-15 12:22:41 -05001141 len(expected), len(actual), expected, actual)
1142 return
1143 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001144 type compareFn = func(i int, j int) bool
1145 byCommand := func(slice []BuildStatement) compareFn {
1146 return func(i int, j int) bool {
1147 return slice[i].Command < slice[j].Command
Chris Parsons4f069892021-01-15 12:22:41 -05001148 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001149 }
1150 sort.SliceStable(expected, byCommand(expected))
1151 sort.SliceStable(actual, byCommand(actual))
1152 for i, actualStatement := range actual {
1153 expectedStatement := expected[i]
1154 if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
1155 t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
Usta Shresthaef922252022-06-02 14:23:02 -04001156 differingField, actualStatement, expectedStatement)
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001157 return
1158 }
Chris Parsons4f069892021-01-15 12:22:41 -05001159 }
1160}
1161
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001162func buildStatementEquals(first BuildStatement, second BuildStatement) string {
Chris Parsons4f069892021-01-15 12:22:41 -05001163 if first.Mnemonic != second.Mnemonic {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001164 return "Mnemonic"
Chris Parsons4f069892021-01-15 12:22:41 -05001165 }
1166 if first.Command != second.Command {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001167 return "Command"
Chris Parsons4f069892021-01-15 12:22:41 -05001168 }
1169 // Ordering is significant for environment variables.
1170 if !reflect.DeepEqual(first.Env, second.Env) {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001171 return "Env"
Chris Parsons4f069892021-01-15 12:22:41 -05001172 }
1173 // Ordering is irrelevant for input and output paths, so compare sets.
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001174 if !reflect.DeepEqual(sortedStrings(first.InputPaths), sortedStrings(second.InputPaths)) {
1175 return "InputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001176 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001177 if !reflect.DeepEqual(sortedStrings(first.OutputPaths), sortedStrings(second.OutputPaths)) {
1178 return "OutputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001179 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001180 if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
1181 return "SymlinkPaths"
Liz Kammerc49e6822021-06-08 15:04:11 -04001182 }
1183 if first.Depfile != second.Depfile {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001184 return "Depfile"
Liz Kammerc49e6822021-06-08 15:04:11 -04001185 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001186 return ""
Chris Parsons4f069892021-01-15 12:22:41 -05001187}
1188
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001189func sortedStrings(stringSlice []string) []string {
1190 sorted := make([]string, len(stringSlice))
1191 copy(sorted, stringSlice)
1192 sort.Strings(sorted)
1193 return sorted
Chris Parsons4f069892021-01-15 12:22:41 -05001194}
Jason Wu118fd2b2022-10-27 18:41:15 +00001195
1196// Transform the json format to ActionGraphContainer
1197func JsonToActionGraphContainer(inputString string) ([]byte, error) {
1198 var aqueryProtoResult analysis_v2_proto.ActionGraphContainer
1199 err := json.Unmarshal([]byte(inputString), &aqueryProtoResult)
1200 if err != nil {
1201 return []byte(""), err
1202 }
1203 data, _ := proto.Marshal(&aqueryProtoResult)
1204 return data, err
1205}