blob: 32c87a0a68445d090ad1e43d2cc401b79f801433 [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
Jason Wu118fd2b2022-10-27 18:41:15 +000024 analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
Liz Kammer690fbac2023-02-10 11:11:17 -050025
26 "github.com/google/blueprint/metrics"
27 "google.golang.org/protobuf/proto"
Chris Parsons4f069892021-01-15 12:22:41 -050028)
29
30func TestAqueryMultiArchGenrule(t *testing.T) {
31 // This input string is retrieved from a real build of bionic-related genrules.
32 const inputString = `
33{
Jason Wu118fd2b2022-10-27 18:41:15 +000034 "Artifacts": [
35 { "Id": 1, "path_fragment_id": 1 },
36 { "Id": 2, "path_fragment_id": 6 },
37 { "Id": 3, "path_fragment_id": 8 },
38 { "Id": 4, "path_fragment_id": 12 },
39 { "Id": 5, "path_fragment_id": 19 },
40 { "Id": 6, "path_fragment_id": 20 },
41 { "Id": 7, "path_fragment_id": 21 }],
42 "Actions": [{
43 "target_id": 1,
44 "action_key": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
45 "Mnemonic": "Genrule",
46 "configuration_id": 1,
47 "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"],
48 "environment_variables": [{
49 "Key": "PATH",
50 "Value": "/bin:/usr/bin:/usr/local/bin"
51 }],
52 "input_dep_set_ids": [1],
53 "output_ids": [4],
54 "primary_output_id": 4
55 }, {
56 "target_id": 2,
57 "action_key": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
58 "Mnemonic": "Genrule",
59 "configuration_id": 1,
60 "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"],
61 "environment_variables": [{
62 "Key": "PATH",
63 "Value": "/bin:/usr/bin:/usr/local/bin"
64 }],
65 "input_dep_set_ids": [2],
66 "output_ids": [5],
67 "primary_output_id": 5
68 }, {
69 "target_id": 3,
70 "action_key": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
71 "Mnemonic": "Genrule",
72 "configuration_id": 1,
73 "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"],
74 "environment_variables": [{
75 "Key": "PATH",
76 "Value": "/bin:/usr/bin:/usr/local/bin"
77 }],
78 "input_dep_set_ids": [3],
79 "output_ids": [6],
80 "primary_output_id": 6
81 }, {
82 "target_id": 4,
83 "action_key": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
84 "Mnemonic": "Genrule",
85 "configuration_id": 1,
86 "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"],
87 "environment_variables": [{
88 "Key": "PATH",
89 "Value": "/bin:/usr/bin:/usr/local/bin"
90 }],
91 "input_dep_set_ids": [4],
92 "output_ids": [7],
93 "primary_output_id": 7
94 }],
95 "Targets": [
96 { "Id": 1, "Label": "@sourceroot//bionic/libc:syscalls-arm", "rule_class_id": 1 },
97 { "Id": 2, "Label": "@sourceroot//bionic/libc:syscalls-x86", "rule_class_id": 1 },
98 { "Id": 3, "Label": "@sourceroot//bionic/libc:syscalls-x86_64", "rule_class_id": 1 },
99 { "Id": 4, "Label": "@sourceroot//bionic/libc:syscalls-arm64", "rule_class_id": 1 }],
100 "dep_set_of_files": [
101 { "Id": 1, "direct_artifact_ids": [1, 2, 3] },
102 { "Id": 2, "direct_artifact_ids": [1, 2, 3] },
103 { "Id": 3, "direct_artifact_ids": [1, 2, 3] },
104 { "Id": 4, "direct_artifact_ids": [1, 2, 3] }],
105 "Configuration": [{
106 "Id": 1,
107 "Mnemonic": "k8-fastbuild",
108 "platform_name": "k8",
109 "Checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
110 }],
111 "rule_classes": [{ "Id": 1, "Name": "genrule"}],
112 "path_fragments": [
113 { "Id": 5, "Label": ".." },
114 { "Id": 4, "Label": "sourceroot", "parent_id": 5 },
115 { "Id": 3, "Label": "bionic", "parent_id": 4 },
116 { "Id": 2, "Label": "libc", "parent_id": 3 },
117 { "Id": 1, "Label": "SYSCALLS.TXT", "parent_id": 2 },
118 { "Id": 7, "Label": "tools", "parent_id": 2 },
119 { "Id": 6, "Label": "gensyscalls.py", "parent_id": 7 },
120 { "Id": 11, "Label": "bazel_tools", "parent_id": 5 },
121 { "Id": 10, "Label": "tools", "parent_id": 11 },
122 { "Id": 9, "Label": "genrule", "parent_id": 10 },
123 { "Id": 8, "Label": "genrule-setup.sh", "parent_id": 9 },
124 { "Id": 18, "Label": "bazel-out" },
125 { "Id": 17, "Label": "sourceroot", "parent_id": 18 },
126 { "Id": 16, "Label": "k8-fastbuild", "parent_id": 17 },
127 { "Id": 15, "Label": "bin", "parent_id": 16 },
128 { "Id": 14, "Label": "bionic", "parent_id": 15 },
129 { "Id": 13, "Label": "libc", "parent_id": 14 },
130 { "Id": 12, "Label": "syscalls-arm.S", "parent_id": 13 },
131 { "Id": 19, "Label": "syscalls-x86.S", "parent_id": 13 },
132 { "Id": 20, "Label": "syscalls-x86_64.S", "parent_id": 13 },
133 { "Id": 21, "Label": "syscalls-arm64.S", "parent_id": 13 }]
134}
135`
136 data, err := JsonToActionGraphContainer(inputString)
137 if err != nil {
138 t.Error(err)
139 return
140 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500141 actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammera4655a92023-02-10 17:17:28 -0500142 var expectedBuildStatements []*BuildStatement
Chris Parsons4f069892021-01-15 12:22:41 -0500143 for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
144 expectedBuildStatements = append(expectedBuildStatements,
Liz Kammera4655a92023-02-10 17:17:28 -0500145 &BuildStatement{
Chris Parsons4f069892021-01-15 12:22:41 -0500146 Command: fmt.Sprintf(
147 "/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'",
148 arch, arch),
149 OutputPaths: []string{
150 fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
151 },
Liz Kammer00629db2023-02-09 14:28:15 -0500152 Env: []*analysis_v2_proto.KeyValuePair{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400153 {Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
Chris Parsons4f069892021-01-15 12:22:41 -0500154 },
155 Mnemonic: "Genrule",
156 })
157 }
158 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400159
160 expectedFlattenedInputs := []string{
161 "../sourceroot/bionic/libc/SYSCALLS.TXT",
162 "../sourceroot/bionic/libc/tools/gensyscalls.py",
Chris Parsons1a7aca02022-04-25 22:35:15 -0400163 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400164 // In this example, each depset should have the same expected inputs.
165 for _, actualDepset := range actualDepsets {
166 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
167 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
168 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
169 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400170 }
Chris Parsons4f069892021-01-15 12:22:41 -0500171}
172
173func TestInvalidOutputId(t *testing.T) {
174 const inputString = `
175{
Jason Wu118fd2b2022-10-27 18:41:15 +0000176 "artifacts": [
177 { "id": 1, "path_fragment_id": 1 },
178 { "id": 2, "path_fragment_id": 2 }],
179 "actions": [{
180 "target_id": 1,
181 "action_key": "x",
182 "mnemonic": "x",
183 "arguments": ["touch", "foo"],
184 "input_dep_set_ids": [1],
185 "output_ids": [3],
186 "primary_output_id": 3
187 }],
188 "dep_set_of_files": [
189 { "id": 1, "direct_artifact_ids": [1, 2] }],
190 "path_fragments": [
191 { "id": 1, "label": "one" },
192 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500193}`
194
Jason Wu118fd2b2022-10-27 18:41:15 +0000195 data, err := JsonToActionGraphContainer(inputString)
196 if err != nil {
197 t.Error(err)
198 return
199 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500200 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Chris Parsons4f069892021-01-15 12:22:41 -0500201 assertError(t, err, "undefined outputId 3")
202}
203
Chris Parsons1a7aca02022-04-25 22:35:15 -0400204func TestInvalidInputDepsetIdFromAction(t *testing.T) {
Chris Parsons4f069892021-01-15 12:22:41 -0500205 const inputString = `
206{
Jason Wu118fd2b2022-10-27 18:41:15 +0000207 "artifacts": [
208 { "id": 1, "path_fragment_id": 1 },
209 { "id": 2, "path_fragment_id": 2 }],
210 "actions": [{
211 "target_id": 1,
212 "action_key": "x",
213 "mnemonic": "x",
214 "arguments": ["touch", "foo"],
215 "input_dep_set_ids": [2],
216 "output_ids": [1],
217 "primary_output_id": 1
218 }],
219 "dep_set_of_files": [
220 { "id": 1, "direct_artifact_ids": [1, 2] }],
221 "path_fragments": [
222 { "id": 1, "label": "one" },
223 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500224}`
225
Jason Wu118fd2b2022-10-27 18:41:15 +0000226 data, err := JsonToActionGraphContainer(inputString)
227 if err != nil {
228 t.Error(err)
229 return
230 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500231 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500232 assertError(t, err, "undefined (not even empty) input depsetId 2")
Chris Parsons4f069892021-01-15 12:22:41 -0500233}
234
Chris Parsons1a7aca02022-04-25 22:35:15 -0400235func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
236 const inputString = `
237{
Jason Wu118fd2b2022-10-27 18:41:15 +0000238 "artifacts": [
239 { "id": 1, "path_fragment_id": 1 },
240 { "id": 2, "path_fragment_id": 2 }],
241 "actions": [{
242 "target_id": 1,
243 "action_key": "x",
244 "mnemonic": "x",
245 "arguments": ["touch", "foo"],
246 "input_dep_set_ids": [1],
247 "output_ids": [1],
248 "primary_output_id": 1
249 }],
250 "dep_set_of_files": [
251 { "id": 1, "direct_artifact_ids": [1, 2], "transitive_dep_set_ids": [42] }],
252 "path_fragments": [
253 { "id": 1, "label": "one"},
254 { "id": 2, "label": "two" }]
Chris Parsons1a7aca02022-04-25 22:35:15 -0400255}`
256
Jason Wu118fd2b2022-10-27 18:41:15 +0000257 data, err := JsonToActionGraphContainer(inputString)
258 if err != nil {
259 t.Error(err)
260 return
261 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500262 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Chris Parsons1a7aca02022-04-25 22:35:15 -0400263 assertError(t, err, "undefined input depsetId 42 (referenced by depsetId 1)")
264}
265
Chris Parsons4f069892021-01-15 12:22:41 -0500266func TestInvalidInputArtifactId(t *testing.T) {
267 const inputString = `
268{
Jason Wu118fd2b2022-10-27 18:41:15 +0000269 "artifacts": [
270 { "id": 1, "path_fragment_id": 1 },
271 { "id": 2, "path_fragment_id": 2 }],
272 "actions": [{
273 "target_id": 1,
274 "action_key": "x",
275 "mnemonic": "x",
276 "arguments": ["touch", "foo"],
277 "input_dep_set_ids": [1],
278 "output_ids": [1],
279 "primary_output_id": 1
280 }],
281 "dep_set_of_files": [
282 { "id": 1, "direct_artifact_ids": [1, 3] }],
283 "path_fragments": [
284 { "id": 1, "label": "one" },
285 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500286}`
287
Jason Wu118fd2b2022-10-27 18:41:15 +0000288 data, err := JsonToActionGraphContainer(inputString)
289 if err != nil {
290 t.Error(err)
291 return
292 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500293 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Chris Parsons4f069892021-01-15 12:22:41 -0500294 assertError(t, err, "undefined input artifactId 3")
295}
296
297func TestInvalidPathFragmentId(t *testing.T) {
298 const inputString = `
299{
Jason Wu118fd2b2022-10-27 18:41:15 +0000300 "artifacts": [
301 { "id": 1, "path_fragment_id": 1 },
302 { "id": 2, "path_fragment_id": 2 }],
303 "actions": [{
304 "target_id": 1,
305 "action_key": "x",
306 "mnemonic": "x",
307 "arguments": ["touch", "foo"],
308 "input_dep_set_ids": [1],
309 "output_ids": [1],
310 "primary_output_id": 1
311 }],
312 "dep_set_of_files": [
313 { "id": 1, "direct_artifact_ids": [1, 2] }],
314 "path_fragments": [
315 { "id": 1, "label": "one" },
316 { "id": 2, "label": "two", "parent_id": 3 }]
Chris Parsons4f069892021-01-15 12:22:41 -0500317}`
318
Jason Wu118fd2b2022-10-27 18:41:15 +0000319 data, err := JsonToActionGraphContainer(inputString)
320 if err != nil {
321 t.Error(err)
322 return
323 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500324 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Chris Parsons4f069892021-01-15 12:22:41 -0500325 assertError(t, err, "undefined path fragment id 3")
326}
327
Liz Kammerde116852021-03-25 16:42:37 -0400328func TestDepfiles(t *testing.T) {
329 const inputString = `
330{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700331 "artifacts": [
Jason Wu118fd2b2022-10-27 18:41:15 +0000332 { "id": 1, "path_fragment_id": 1 },
333 { "id": 2, "path_fragment_id": 2 },
334 { "id": 3, "path_fragment_id": 3 }],
Liz Kammerde116852021-03-25 16:42:37 -0400335 "actions": [{
Jason Wu118fd2b2022-10-27 18:41:15 +0000336 "target_Id": 1,
337 "action_Key": "x",
Liz Kammerde116852021-03-25 16:42:37 -0400338 "mnemonic": "x",
339 "arguments": ["touch", "foo"],
Jason Wu118fd2b2022-10-27 18:41:15 +0000340 "input_dep_set_ids": [1],
341 "output_ids": [2, 3],
342 "primary_output_id": 2
Liz Kammerde116852021-03-25 16:42:37 -0400343 }],
Jason Wu118fd2b2022-10-27 18:41:15 +0000344 "dep_set_of_files": [
345 { "id": 1, "direct_Artifact_Ids": [1, 2, 3] }],
346 "path_fragments": [
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700347 { "id": 1, "label": "one" },
348 { "id": 2, "label": "two" },
349 { "id": 3, "label": "two.d" }]
Liz Kammerde116852021-03-25 16:42:37 -0400350}`
351
Jason Wu118fd2b2022-10-27 18:41:15 +0000352 data, err := JsonToActionGraphContainer(inputString)
353 if err != nil {
354 t.Error(err)
355 return
356 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500357 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerde116852021-03-25 16:42:37 -0400358 if err != nil {
359 t.Errorf("Unexpected error %q", err)
Cole Faustbc65a3f2023-08-01 16:38:55 +0000360 return
Liz Kammerde116852021-03-25 16:42:37 -0400361 }
362 if expected := 1; len(actual) != expected {
363 t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
Cole Faustbc65a3f2023-08-01 16:38:55 +0000364 return
Liz Kammerde116852021-03-25 16:42:37 -0400365 }
366
367 bs := actual[0]
368 expectedDepfile := "two.d"
369 if bs.Depfile == nil {
370 t.Errorf("Expected depfile %q, but there was none found", expectedDepfile)
371 } else if *bs.Depfile != expectedDepfile {
372 t.Errorf("Expected depfile %q, but got %q", expectedDepfile, *bs.Depfile)
373 }
374}
375
376func TestMultipleDepfiles(t *testing.T) {
377 const inputString = `
378{
Jason Wu118fd2b2022-10-27 18:41:15 +0000379 "artifacts": [
380 { "id": 1, "path_fragment_id": 1 },
381 { "id": 2, "path_fragment_id": 2 },
382 { "id": 3, "path_fragment_id": 3 },
383 { "id": 4, "path_fragment_id": 4 }],
384 "actions": [{
385 "target_id": 1,
386 "action_key": "x",
387 "mnemonic": "x",
388 "arguments": ["touch", "foo"],
389 "input_dep_set_ids": [1],
390 "output_ids": [2,3,4],
391 "primary_output_id": 2
392 }],
393 "dep_set_of_files": [{
394 "id": 1,
395 "direct_artifact_ids": [1, 2, 3, 4]
396 }],
397 "path_fragments": [
398 { "id": 1, "label": "one" },
399 { "id": 2, "label": "two" },
400 { "id": 3, "label": "two.d" },
401 { "id": 4, "label": "other.d" }]
Liz Kammerde116852021-03-25 16:42:37 -0400402}`
403
Jason Wu118fd2b2022-10-27 18:41:15 +0000404 data, err := JsonToActionGraphContainer(inputString)
405 if err != nil {
406 t.Error(err)
407 return
408 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500409 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerde116852021-03-25 16:42:37 -0400410 assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
411}
412
Chris Parsons943f2432021-01-19 11:36:50 -0500413func TestTransitiveInputDepsets(t *testing.T) {
414 // The input aquery for this test comes from a proof-of-concept starlark rule which registers
415 // a single action with many inputs given via a deep depset.
416 const inputString = `
417{
Jason Wu118fd2b2022-10-27 18:41:15 +0000418 "artifacts": [
419 { "id": 1, "path_fragment_id": 1 },
420 { "id": 2, "path_fragment_id": 7 },
421 { "id": 3, "path_fragment_id": 8 },
422 { "id": 4, "path_fragment_id": 9 },
423 { "id": 5, "path_fragment_id": 10 },
424 { "id": 6, "path_fragment_id": 11 },
425 { "id": 7, "path_fragment_id": 12 },
426 { "id": 8, "path_fragment_id": 13 },
427 { "id": 9, "path_fragment_id": 14 },
428 { "id": 10, "path_fragment_id": 15 },
429 { "id": 11, "path_fragment_id": 16 },
430 { "id": 12, "path_fragment_id": 17 },
431 { "id": 13, "path_fragment_id": 18 },
432 { "id": 14, "path_fragment_id": 19 },
433 { "id": 15, "path_fragment_id": 20 },
434 { "id": 16, "path_fragment_id": 21 },
435 { "id": 17, "path_fragment_id": 22 },
436 { "id": 18, "path_fragment_id": 23 },
437 { "id": 19, "path_fragment_id": 24 },
438 { "id": 20, "path_fragment_id": 25 },
439 { "id": 21, "path_fragment_id": 26 }],
440 "actions": [{
441 "target_id": 1,
442 "action_key": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
443 "mnemonic": "Action",
444 "configuration_id": 1,
445 "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"],
446 "input_dep_set_ids": [1],
447 "output_ids": [21],
448 "primary_output_id": 21
449 }],
450 "dep_set_of_files": [
451 { "id": 3, "direct_artifact_ids": [1, 2, 3, 4, 5] },
452 { "id": 4, "direct_artifact_ids": [6, 7, 8, 9, 10] },
453 { "id": 2, "transitive_dep_set_ids": [3, 4], "direct_artifact_ids": [11, 12, 13, 14, 15] },
454 { "id": 5, "direct_artifact_ids": [16, 17, 18, 19] },
455 { "id": 1, "transitive_dep_set_ids": [2, 5], "direct_artifact_ids": [20] }],
456 "path_fragments": [
457 { "id": 6, "label": "bazel-out" },
458 { "id": 5, "label": "sourceroot", "parent_id": 6 },
459 { "id": 4, "label": "k8-fastbuild", "parent_id": 5 },
460 { "id": 3, "label": "bin", "parent_id": 4 },
461 { "id": 2, "label": "testpkg", "parent_id": 3 },
462 { "id": 1, "label": "test_1", "parent_id": 2 },
463 { "id": 7, "label": "test_2", "parent_id": 2 },
464 { "id": 8, "label": "test_3", "parent_id": 2 },
465 { "id": 9, "label": "test_4", "parent_id": 2 },
466 { "id": 10, "label": "test_5", "parent_id": 2 },
467 { "id": 11, "label": "test_6", "parent_id": 2 },
468 { "id": 12, "label": "test_7", "parent_id": 2 },
469 { "id": 13, "label": "test_8", "parent_id": 2 },
470 { "id": 14, "label": "test_9", "parent_id": 2 },
471 { "id": 15, "label": "test_10", "parent_id": 2 },
472 { "id": 16, "label": "test_11", "parent_id": 2 },
473 { "id": 17, "label": "test_12", "parent_id": 2 },
474 { "id": 18, "label": "test_13", "parent_id": 2 },
475 { "id": 19, "label": "test_14", "parent_id": 2 },
476 { "id": 20, "label": "test_15", "parent_id": 2 },
477 { "id": 21, "label": "test_16", "parent_id": 2 },
478 { "id": 22, "label": "test_17", "parent_id": 2 },
479 { "id": 23, "label": "test_18", "parent_id": 2 },
480 { "id": 24, "label": "test_19", "parent_id": 2 },
481 { "id": 25, "label": "test_root", "parent_id": 2 },
482 { "id": 26,"label": "test_out", "parent_id": 2 }]
Chris Parsons943f2432021-01-19 11:36:50 -0500483}`
484
Jason Wu118fd2b2022-10-27 18:41:15 +0000485 data, err := JsonToActionGraphContainer(inputString)
486 if err != nil {
487 t.Error(err)
488 return
489 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500490 actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
Chris Parsons1a7aca02022-04-25 22:35:15 -0400491
Liz Kammera4655a92023-02-10 17:17:28 -0500492 expectedBuildStatements := []*BuildStatement{
493 &BuildStatement{
Liz Kammer00629db2023-02-09 14:28:15 -0500494 Command: "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
495 OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
496 Mnemonic: "Action",
497 SymlinkPaths: []string{},
Chris Parsons943f2432021-01-19 11:36:50 -0500498 },
499 }
500 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400501
502 // Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
503 // are given via a deep depset, but the depset is flattened when returned as a
504 // BuildStatement slice.
Usta Shrestha16ac1352022-06-22 11:01:55 -0400505 var expectedFlattenedInputs []string
Chris Parsons1a7aca02022-04-25 22:35:15 -0400506 for i := 1; i < 20; i++ {
507 expectedFlattenedInputs = append(expectedFlattenedInputs, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
508 }
509 expectedFlattenedInputs = append(expectedFlattenedInputs, "bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root")
510
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400511 actualDepsetHashes := actualbuildStatements[0].InputDepsetHashes
512 actualFlattenedInputs := flattenDepsets(actualDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400513 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
514 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
515 }
Chris Parsons943f2432021-01-19 11:36:50 -0500516}
517
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700518func TestSymlinkTree(t *testing.T) {
519 const inputString = `
520{
Jason Wu118fd2b2022-10-27 18:41:15 +0000521 "artifacts": [
522 { "id": 1, "path_fragment_id": 1 },
523 { "id": 2, "path_fragment_id": 2 }],
524 "actions": [{
525 "target_id": 1,
526 "action_key": "x",
527 "mnemonic": "SymlinkTree",
528 "configuration_id": 1,
529 "input_dep_set_ids": [1],
530 "output_ids": [2],
531 "primary_output_id": 2,
532 "execution_platform": "//build/bazel/platforms:linux_x86_64"
533 }],
534 "path_fragments": [
535 { "id": 1, "label": "foo.manifest" },
536 { "id": 2, "label": "foo.runfiles/MANIFEST" }],
537 "dep_set_of_files": [
538 { "id": 1, "direct_artifact_ids": [1] }]
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700539}
540`
Jason Wu118fd2b2022-10-27 18:41:15 +0000541 data, err := JsonToActionGraphContainer(inputString)
542 if err != nil {
543 t.Error(err)
544 return
545 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500546 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700547 if err != nil {
548 t.Errorf("Unexpected error %q", err)
Cole Faustbc65a3f2023-08-01 16:38:55 +0000549 return
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700550 }
Liz Kammera4655a92023-02-10 17:17:28 -0500551 assertBuildStatements(t, []*BuildStatement{
552 &BuildStatement{
Liz Kammer00629db2023-02-09 14:28:15 -0500553 Command: "",
554 OutputPaths: []string{"foo.runfiles/MANIFEST"},
555 Mnemonic: "SymlinkTree",
556 InputPaths: []string{"foo.manifest"},
557 SymlinkPaths: []string{},
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700558 },
559 }, actual)
560}
561
Usta Shresthaef922252022-06-02 14:23:02 -0400562func TestBazelOutRemovalFromInputDepsets(t *testing.T) {
563 const inputString = `{
Jason Wu118fd2b2022-10-27 18:41:15 +0000564 "artifacts": [
565 { "id": 1, "path_fragment_id": 10 },
566 { "id": 2, "path_fragment_id": 20 },
567 { "id": 3, "path_fragment_id": 30 },
568 { "id": 4, "path_fragment_id": 40 }],
569 "dep_set_of_files": [{
570 "id": 1111,
571 "direct_artifact_ids": [3 , 4]
572 }, {
573 "id": 2222,
574 "direct_artifact_ids": [3]
575 }],
576 "actions": [{
577 "target_id": 100,
578 "action_key": "x",
579 "input_dep_set_ids": [1111, 2222],
580 "mnemonic": "x",
581 "arguments": ["bogus", "command"],
582 "output_ids": [2],
583 "primary_output_id": 1
584 }],
585 "path_fragments": [
586 { "id": 10, "label": "input" },
587 { "id": 20, "label": "output" },
588 { "id": 30, "label": "dep1", "parent_id": 50 },
589 { "id": 40, "label": "dep2", "parent_id": 60 },
590 { "id": 50, "label": "bazel_tools", "parent_id": 60 },
591 { "id": 60, "label": ".."}
592 ]
Usta Shresthaef922252022-06-02 14:23:02 -0400593}`
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500594 /* depsets
595 1111 2222
596 / \ |
597 ../dep2 ../bazel_tools/dep1
598 */
Jason Wu118fd2b2022-10-27 18:41:15 +0000599 data, err := JsonToActionGraphContainer(inputString)
600 if err != nil {
601 t.Error(err)
602 return
603 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500604 actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500605 if len(actualDepsets) != 1 {
Usta Shresthaef922252022-06-02 14:23:02 -0400606 t.Errorf("expected 1 depset but found %#v", actualDepsets)
607 return
608 }
609 dep2Found := false
610 for _, dep := range flattenDepsets([]string{actualDepsets[0].ContentHash}, actualDepsets) {
611 if dep == "../bazel_tools/dep1" {
612 t.Errorf("dependency %s expected to be removed but still exists", dep)
613 } else if dep == "../dep2" {
614 dep2Found = true
615 }
616 }
617 if !dep2Found {
618 t.Errorf("dependency ../dep2 expected but not found")
619 }
620
Liz Kammera4655a92023-02-10 17:17:28 -0500621 expectedBuildStatement := &BuildStatement{
Liz Kammer00629db2023-02-09 14:28:15 -0500622 Command: "bogus command",
623 OutputPaths: []string{"output"},
624 Mnemonic: "x",
625 SymlinkPaths: []string{},
Usta Shresthaef922252022-06-02 14:23:02 -0400626 }
627 buildStatementFound := false
628 for _, actualBuildStatement := range actualBuildStatements {
629 if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
630 buildStatementFound = true
631 break
632 }
633 }
634 if !buildStatementFound {
635 t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
636 return
637 }
638}
639
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500640func TestBazelOutRemovalFromTransitiveInputDepsets(t *testing.T) {
641 const inputString = `{
642 "artifacts": [
643 { "id": 1, "path_fragment_id": 10 },
644 { "id": 2, "path_fragment_id": 20 },
645 { "id": 3, "path_fragment_id": 30 }],
646 "dep_set_of_files": [{
647 "id": 1111,
648 "transitive_dep_set_ids": [2222]
649 }, {
650 "id": 2222,
651 "direct_artifact_ids": [3]
652 }, {
653 "id": 3333,
654 "direct_artifact_ids": [3]
655 }, {
656 "id": 4444,
657 "transitive_dep_set_ids": [3333]
658 }],
659 "actions": [{
660 "target_id": 100,
661 "action_key": "x",
662 "input_dep_set_ids": [1111, 4444],
663 "mnemonic": "x",
664 "arguments": ["bogus", "command"],
665 "output_ids": [2],
666 "primary_output_id": 1
667 }],
668 "path_fragments": [
669 { "id": 10, "label": "input" },
670 { "id": 20, "label": "output" },
671 { "id": 30, "label": "dep", "parent_id": 50 },
672 { "id": 50, "label": "bazel_tools", "parent_id": 60 },
673 { "id": 60, "label": ".."}
674 ]
675}`
676 /* depsets
677 1111 4444
678 || ||
679 2222 3333
680 | |
681 ../bazel_tools/dep
682 Note: in dep_set_of_files:
683 1111 appears BEFORE its dependency,2222 while
684 4444 appears AFTER its dependency 3333
685 and this test shows that that order doesn't affect empty depset pruning
686 */
687 data, err := JsonToActionGraphContainer(inputString)
688 if err != nil {
689 t.Error(err)
690 return
691 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500692 actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500693 if len(actualDepsets) != 0 {
694 t.Errorf("expected 0 depsets but found %#v", actualDepsets)
695 return
696 }
697
Liz Kammera4655a92023-02-10 17:17:28 -0500698 expectedBuildStatement := &BuildStatement{
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500699 Command: "bogus command",
700 OutputPaths: []string{"output"},
701 Mnemonic: "x",
702 }
703 buildStatementFound := false
704 for _, actualBuildStatement := range actualBuildStatements {
705 if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
706 buildStatementFound = true
707 break
708 }
709 }
710 if !buildStatementFound {
711 t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
712 return
713 }
714}
715
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400716func TestMiddlemenAction(t *testing.T) {
717 const inputString = `
718{
Jason Wu118fd2b2022-10-27 18:41:15 +0000719 "artifacts": [
720 { "id": 1, "path_fragment_id": 1 },
721 { "id": 2, "path_fragment_id": 2 },
722 { "id": 3, "path_fragment_id": 3 },
723 { "id": 4, "path_fragment_id": 4 },
724 { "id": 5, "path_fragment_id": 5 },
725 { "id": 6, "path_fragment_id": 6 }],
726 "path_fragments": [
727 { "id": 1, "label": "middleinput_one" },
728 { "id": 2, "label": "middleinput_two" },
729 { "id": 3, "label": "middleman_artifact" },
730 { "id": 4, "label": "maininput_one" },
731 { "id": 5, "label": "maininput_two" },
732 { "id": 6, "label": "output" }],
733 "dep_set_of_files": [
734 { "id": 1, "direct_artifact_ids": [1, 2] },
735 { "id": 2, "direct_artifact_ids": [3, 4, 5] }],
736 "actions": [{
737 "target_id": 1,
738 "action_key": "x",
739 "mnemonic": "Middleman",
740 "arguments": ["touch", "foo"],
741 "input_dep_set_ids": [1],
742 "output_ids": [3],
743 "primary_output_id": 3
744 }, {
745 "target_id": 2,
746 "action_key": "y",
747 "mnemonic": "Main action",
748 "arguments": ["touch", "foo"],
749 "input_dep_set_ids": [2],
750 "output_ids": [6],
751 "primary_output_id": 6
752 }]
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400753}`
Jason Wu118fd2b2022-10-27 18:41:15 +0000754 data, err := JsonToActionGraphContainer(inputString)
755 if err != nil {
756 t.Error(err)
757 return
758 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500759 actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400760 if err != nil {
761 t.Errorf("Unexpected error %q", err)
Cole Faustbc65a3f2023-08-01 16:38:55 +0000762 return
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400763 }
Liz Kammera4655a92023-02-10 17:17:28 -0500764 if expected := 2; len(actualBuildStatements) != expected {
765 t.Fatalf("Expected %d build statements, got %d %#v", expected, len(actualBuildStatements), actualBuildStatements)
Cole Faustbc65a3f2023-08-01 16:38:55 +0000766 return
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400767 }
768
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400769 expectedDepsetFiles := [][]string{
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400770 {"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"},
771 {"middleinput_one", "middleinput_two"},
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400772 }
773 assertFlattenedDepsets(t, actualDepsets, expectedDepsetFiles)
774
Chris Parsons1a7aca02022-04-25 22:35:15 -0400775 bs := actualBuildStatements[0]
776 if len(bs.InputPaths) > 0 {
777 t.Errorf("Expected main action raw inputs to be empty, but got %q", bs.InputPaths)
778 }
779
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400780 expectedOutputs := []string{"output"}
781 if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) {
782 t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths)
783 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400784
Chris Parsons1a7aca02022-04-25 22:35:15 -0400785 expectedFlattenedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400786 actualFlattenedInputs := flattenDepsets(bs.InputDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400787
788 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
789 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
790 }
Liz Kammera4655a92023-02-10 17:17:28 -0500791
792 bs = actualBuildStatements[1]
793 if bs != nil {
794 t.Errorf("Expected nil action for skipped")
795 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400796}
797
798// Returns the contents of given depsets in concatenated post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400799func flattenDepsets(depsetHashesToFlatten []string, allDepsets []AqueryDepset) []string {
800 depsetsByHash := map[string]AqueryDepset{}
Chris Parsons1a7aca02022-04-25 22:35:15 -0400801 for _, depset := range allDepsets {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400802 depsetsByHash[depset.ContentHash] = depset
Chris Parsons1a7aca02022-04-25 22:35:15 -0400803 }
Usta Shrestha16ac1352022-06-22 11:01:55 -0400804 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400805 for _, depsetId := range depsetHashesToFlatten {
806 result = append(result, flattenDepset(depsetId, depsetsByHash)...)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400807 }
808 return result
809}
810
811// Returns the contents of a given depset in post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400812func flattenDepset(depsetHashToFlatten string, allDepsets map[string]AqueryDepset) []string {
813 depset := allDepsets[depsetHashToFlatten]
Usta Shrestha16ac1352022-06-22 11:01:55 -0400814 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400815 for _, depsetId := range depset.TransitiveDepSetHashes {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400816 result = append(result, flattenDepset(depsetId, allDepsets)...)
817 }
818 result = append(result, depset.DirectArtifacts...)
819 return result
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400820}
821
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400822func assertFlattenedDepsets(t *testing.T, actualDepsets []AqueryDepset, expectedDepsetFiles [][]string) {
823 t.Helper()
824 if len(actualDepsets) != len(expectedDepsetFiles) {
Sasha Smundakf10c3ac2022-06-08 11:46:31 -0700825 t.Errorf("Expected %d depsets, but got %d depsets", len(expectedDepsetFiles), len(actualDepsets))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400826 }
827 for i, actualDepset := range actualDepsets {
828 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
829 if !reflect.DeepEqual(actualFlattenedInputs, expectedDepsetFiles[i]) {
830 t.Errorf("Expected depset files: %v, but got %v", expectedDepsetFiles[i], actualFlattenedInputs)
831 }
832 }
833}
834
Liz Kammerc49e6822021-06-08 15:04:11 -0400835func TestSimpleSymlink(t *testing.T) {
836 const inputString = `
837{
Jason Wu118fd2b2022-10-27 18:41:15 +0000838 "artifacts": [
839 { "id": 1, "path_fragment_id": 3 },
840 { "id": 2, "path_fragment_id": 5 }],
841 "actions": [{
842 "target_id": 1,
843 "action_key": "x",
844 "mnemonic": "Symlink",
845 "input_dep_set_ids": [1],
846 "output_ids": [2],
847 "primary_output_id": 2
848 }],
849 "dep_set_of_files": [
850 { "id": 1, "direct_artifact_ids": [1] }],
851 "path_fragments": [
852 { "id": 1, "label": "one" },
853 { "id": 2, "label": "file_subdir", "parent_id": 1 },
854 { "id": 3, "label": "file", "parent_id": 2 },
855 { "id": 4, "label": "symlink_subdir", "parent_id": 1 },
856 { "id": 5, "label": "symlink", "parent_id": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400857}`
Jason Wu118fd2b2022-10-27 18:41:15 +0000858 data, err := JsonToActionGraphContainer(inputString)
859 if err != nil {
860 t.Error(err)
861 return
862 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500863 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerc49e6822021-06-08 15:04:11 -0400864
865 if err != nil {
866 t.Errorf("Unexpected error %q", err)
Cole Faustbc65a3f2023-08-01 16:38:55 +0000867 return
Liz Kammerc49e6822021-06-08 15:04:11 -0400868 }
869
Liz Kammera4655a92023-02-10 17:17:28 -0500870 expectedBuildStatements := []*BuildStatement{
871 &BuildStatement{
Liz Kammerc49e6822021-06-08 15:04:11 -0400872 Command: "mkdir -p one/symlink_subdir && " +
873 "rm -f one/symlink_subdir/symlink && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400874 "ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
Liz Kammerc49e6822021-06-08 15:04:11 -0400875 InputPaths: []string{"one/file_subdir/file"},
876 OutputPaths: []string{"one/symlink_subdir/symlink"},
877 SymlinkPaths: []string{"one/symlink_subdir/symlink"},
878 Mnemonic: "Symlink",
879 },
880 }
881 assertBuildStatements(t, actual, expectedBuildStatements)
882}
883
884func TestSymlinkQuotesPaths(t *testing.T) {
885 const inputString = `
886{
Jason Wu118fd2b2022-10-27 18:41:15 +0000887 "artifacts": [
888 { "id": 1, "path_fragment_id": 3 },
889 { "id": 2, "path_fragment_id": 5 }],
890 "actions": [{
891 "target_id": 1,
892 "action_key": "x",
893 "mnemonic": "SolibSymlink",
894 "input_dep_set_ids": [1],
895 "output_ids": [2],
896 "primary_output_id": 2
897 }],
898 "dep_set_of_files": [
899 { "id": 1, "direct_artifact_ids": [1] }],
900 "path_fragments": [
901 { "id": 1, "label": "one" },
902 { "id": 2, "label": "file subdir", "parent_id": 1 },
903 { "id": 3, "label": "file", "parent_id": 2 },
904 { "id": 4, "label": "symlink subdir", "parent_id": 1 },
905 { "id": 5, "label": "symlink", "parent_id": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400906}`
907
Jason Wu118fd2b2022-10-27 18:41:15 +0000908 data, err := JsonToActionGraphContainer(inputString)
909 if err != nil {
910 t.Error(err)
911 return
912 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500913 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerc49e6822021-06-08 15:04:11 -0400914 if err != nil {
915 t.Errorf("Unexpected error %q", err)
Cole Faustbc65a3f2023-08-01 16:38:55 +0000916 return
Liz Kammerc49e6822021-06-08 15:04:11 -0400917 }
918
Liz Kammera4655a92023-02-10 17:17:28 -0500919 expectedBuildStatements := []*BuildStatement{
920 &BuildStatement{
Liz Kammerc49e6822021-06-08 15:04:11 -0400921 Command: "mkdir -p 'one/symlink subdir' && " +
922 "rm -f 'one/symlink subdir/symlink' && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400923 "ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
Liz Kammerc49e6822021-06-08 15:04:11 -0400924 InputPaths: []string{"one/file subdir/file"},
925 OutputPaths: []string{"one/symlink subdir/symlink"},
926 SymlinkPaths: []string{"one/symlink subdir/symlink"},
927 Mnemonic: "SolibSymlink",
928 },
929 }
Liz Kammerc7737782021-11-04 10:56:13 -0400930 assertBuildStatements(t, expectedBuildStatements, actual)
Liz Kammerc49e6822021-06-08 15:04:11 -0400931}
932
933func TestSymlinkMultipleInputs(t *testing.T) {
934 const inputString = `
935{
Jason Wu118fd2b2022-10-27 18:41:15 +0000936 "artifacts": [
937 { "id": 1, "path_fragment_id": 1 },
938 { "id": 2, "path_fragment_id": 2 },
939 { "id": 3, "path_fragment_id": 3 }],
940 "actions": [{
941 "target_id": 1,
942 "action_key": "x",
943 "mnemonic": "Symlink",
944 "input_dep_set_ids": [1],
945 "output_ids": [3],
946 "primary_output_id": 3
947 }],
948 "dep_set_of_files": [{ "id": 1, "direct_artifact_ids": [1,2] }],
949 "path_fragments": [
950 { "id": 1, "label": "file" },
951 { "id": 2, "label": "other_file" },
952 { "id": 3, "label": "symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400953}`
954
Jason Wu118fd2b2022-10-27 18:41:15 +0000955 data, err := JsonToActionGraphContainer(inputString)
956 if err != nil {
957 t.Error(err)
958 return
959 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500960 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerc49e6822021-06-08 15:04:11 -0400961 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
962}
963
964func TestSymlinkMultipleOutputs(t *testing.T) {
965 const inputString = `
966{
Jason Wu118fd2b2022-10-27 18:41:15 +0000967 "artifacts": [
968 { "id": 1, "path_fragment_id": 1 },
969 { "id": 3, "path_fragment_id": 3 }],
970 "actions": [{
971 "target_id": 1,
972 "action_key": "x",
973 "mnemonic": "Symlink",
974 "input_dep_set_ids": [1],
975 "output_ids": [2,3],
976 "primary_output_id": 2
977 }],
978 "dep_set_of_files": [
979 { "id": 1, "direct_artifact_ids": [1] }],
980 "path_fragments": [
981 { "id": 1, "label": "file" },
982 { "id": 2, "label": "symlink" },
983 { "id": 3, "label": "other_symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400984}`
985
Jason Wu118fd2b2022-10-27 18:41:15 +0000986 data, err := JsonToActionGraphContainer(inputString)
987 if err != nil {
988 t.Error(err)
989 return
990 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500991 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Jason Wu118fd2b2022-10-27 18:41:15 +0000992 assertError(t, err, "undefined outputId 2")
Liz Kammerc49e6822021-06-08 15:04:11 -0400993}
994
Wei Li455ba832021-11-04 22:58:12 +0000995func TestTemplateExpandActionSubstitutions(t *testing.T) {
996 const inputString = `
997{
Jason Wu118fd2b2022-10-27 18:41:15 +0000998 "artifacts": [{
999 "id": 1,
1000 "path_fragment_id": 1
1001 }],
1002 "actions": [{
1003 "target_id": 1,
1004 "action_key": "x",
1005 "mnemonic": "TemplateExpand",
1006 "configuration_id": 1,
1007 "output_ids": [1],
1008 "primary_output_id": 1,
1009 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1010 "template_content": "Test template substitutions: %token1%, %python_binary%",
1011 "substitutions": [
1012 { "key": "%token1%", "value": "abcd" },
1013 { "key": "%python_binary%", "value": "python3" }]
1014 }],
1015 "path_fragments": [
1016 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +00001017}`
1018
Jason Wu118fd2b2022-10-27 18:41:15 +00001019 data, err := JsonToActionGraphContainer(inputString)
1020 if err != nil {
1021 t.Error(err)
1022 return
1023 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001024 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Wei Li455ba832021-11-04 22:58:12 +00001025 if err != nil {
1026 t.Errorf("Unexpected error %q", err)
Cole Faustbc65a3f2023-08-01 16:38:55 +00001027 return
Wei Li455ba832021-11-04 22:58:12 +00001028 }
1029
Liz Kammera4655a92023-02-10 17:17:28 -05001030 expectedBuildStatements := []*BuildStatement{
1031 &BuildStatement{
Wei Li455ba832021-11-04 22:58:12 +00001032 Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
1033 "chmod a+x template_file'",
Liz Kammer00629db2023-02-09 14:28:15 -05001034 OutputPaths: []string{"template_file"},
1035 Mnemonic: "TemplateExpand",
1036 SymlinkPaths: []string{},
Wei Li455ba832021-11-04 22:58:12 +00001037 },
1038 }
1039 assertBuildStatements(t, expectedBuildStatements, actual)
1040}
1041
1042func TestTemplateExpandActionNoOutput(t *testing.T) {
1043 const inputString = `
1044{
Jason Wu118fd2b2022-10-27 18:41:15 +00001045 "artifacts": [
1046 { "id": 1, "path_fragment_id": 1 }],
1047 "actions": [{
1048 "target_id": 1,
1049 "action_key": "x",
1050 "mnemonic": "TemplateExpand",
1051 "configuration_id": 1,
1052 "primary_output_id": 1,
1053 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1054 "templateContent": "Test template substitutions: %token1%, %python_binary%",
1055 "substitutions": [
1056 { "key": "%token1%", "value": "abcd" },
1057 { "key": "%python_binary%", "value": "python3" }]
1058 }],
1059 "path_fragments": [
1060 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +00001061}`
1062
Jason Wu118fd2b2022-10-27 18:41:15 +00001063 data, err := JsonToActionGraphContainer(inputString)
1064 if err != nil {
1065 t.Error(err)
1066 return
1067 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001068 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Wei Li455ba832021-11-04 22:58:12 +00001069 assertError(t, err, `Expect 1 output to template expand action, got: output []`)
1070}
1071
Sasha Smundak1da064c2022-06-08 16:36:16 -07001072func TestFileWrite(t *testing.T) {
1073 const inputString = `
1074{
Jason Wu118fd2b2022-10-27 18:41:15 +00001075 "artifacts": [
1076 { "id": 1, "path_fragment_id": 1 }],
1077 "actions": [{
1078 "target_id": 1,
1079 "action_key": "x",
1080 "mnemonic": "FileWrite",
1081 "configuration_id": 1,
1082 "output_ids": [1],
1083 "primary_output_id": 1,
1084 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1085 "file_contents": "file data\n"
1086 }],
1087 "path_fragments": [
1088 { "id": 1, "label": "foo.manifest" }]
Sasha Smundak1da064c2022-06-08 16:36:16 -07001089}
1090`
Jason Wu118fd2b2022-10-27 18:41:15 +00001091 data, err := JsonToActionGraphContainer(inputString)
1092 if err != nil {
1093 t.Error(err)
1094 return
1095 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001096 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Sasha Smundak1da064c2022-06-08 16:36:16 -07001097 if err != nil {
1098 t.Errorf("Unexpected error %q", err)
Cole Faustbc65a3f2023-08-01 16:38:55 +00001099 return
Sasha Smundak1da064c2022-06-08 16:36:16 -07001100 }
Liz Kammera4655a92023-02-10 17:17:28 -05001101 assertBuildStatements(t, []*BuildStatement{
1102 &BuildStatement{
Sasha Smundak1da064c2022-06-08 16:36:16 -07001103 OutputPaths: []string{"foo.manifest"},
1104 Mnemonic: "FileWrite",
1105 FileContents: "file data\n",
Liz Kammer00629db2023-02-09 14:28:15 -05001106 SymlinkPaths: []string{},
Sasha Smundak1da064c2022-06-08 16:36:16 -07001107 },
1108 }, actual)
1109}
1110
1111func TestSourceSymlinkManifest(t *testing.T) {
1112 const inputString = `
1113{
Jason Wu118fd2b2022-10-27 18:41:15 +00001114 "artifacts": [
1115 { "id": 1, "path_fragment_id": 1 }],
1116 "actions": [{
1117 "target_id": 1,
1118 "action_key": "x",
1119 "mnemonic": "SourceSymlinkManifest",
1120 "configuration_id": 1,
1121 "output_ids": [1],
1122 "primary_output_id": 1,
1123 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1124 "file_contents": "symlink target\n"
1125 }],
1126 "path_fragments": [
1127 { "id": 1, "label": "foo.manifest" }]
Sasha Smundak1da064c2022-06-08 16:36:16 -07001128}
1129`
Jason Wu118fd2b2022-10-27 18:41:15 +00001130 data, err := JsonToActionGraphContainer(inputString)
1131 if err != nil {
1132 t.Error(err)
1133 return
1134 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001135 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Sasha Smundak1da064c2022-06-08 16:36:16 -07001136 if err != nil {
1137 t.Errorf("Unexpected error %q", err)
Cole Faustbc65a3f2023-08-01 16:38:55 +00001138 return
Sasha Smundak1da064c2022-06-08 16:36:16 -07001139 }
Liz Kammera4655a92023-02-10 17:17:28 -05001140 assertBuildStatements(t, []*BuildStatement{
1141 &BuildStatement{
Liz Kammer00629db2023-02-09 14:28:15 -05001142 OutputPaths: []string{"foo.manifest"},
1143 Mnemonic: "SourceSymlinkManifest",
1144 SymlinkPaths: []string{},
Sasha Smundak1da064c2022-06-08 16:36:16 -07001145 },
1146 }, actual)
1147}
1148
Cole Faustbc65a3f2023-08-01 16:38:55 +00001149func TestUnresolvedSymlink(t *testing.T) {
1150 const inputString = `
1151{
1152 "artifacts": [
1153 { "id": 1, "path_fragment_id": 1 }
1154 ],
1155 "actions": [{
1156 "target_id": 1,
1157 "action_key": "x",
1158 "mnemonic": "UnresolvedSymlink",
1159 "configuration_id": 1,
1160 "output_ids": [1],
1161 "primary_output_id": 1,
1162 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1163 "unresolved_symlink_target": "symlink/target"
1164 }],
1165 "path_fragments": [
1166 { "id": 1, "label": "path/to/symlink" }
1167 ]
1168}
1169`
1170 data, err := JsonToActionGraphContainer(inputString)
1171 if err != nil {
1172 t.Error(err)
1173 return
1174 }
1175 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
1176 if err != nil {
1177 t.Errorf("Unexpected error %q", err)
1178 return
1179 }
1180 assertBuildStatements(t, []*BuildStatement{{
1181 Command: "mkdir -p path/to && rm -f path/to/symlink && ln -sf symlink/target path/to/symlink",
1182 OutputPaths: []string{"path/to/symlink"},
1183 Mnemonic: "UnresolvedSymlink",
1184 SymlinkPaths: []string{"path/to/symlink"},
1185 }}, actual)
1186}
1187
1188func TestUnresolvedSymlinkBazelSandwich(t *testing.T) {
1189 const inputString = `
1190{
1191 "artifacts": [
1192 { "id": 1, "path_fragment_id": 1 }
1193 ],
1194 "actions": [{
1195 "target_id": 1,
1196 "action_key": "x",
1197 "mnemonic": "UnresolvedSymlink",
1198 "configuration_id": 1,
1199 "output_ids": [1],
1200 "primary_output_id": 1,
1201 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1202 "unresolved_symlink_target": "bazel_sandwich:{\"target\":\"target/product/emulator_x86_64/system\"}"
1203 }],
1204 "path_fragments": [
1205 { "id": 1, "label": "path/to/symlink" }
1206 ]
1207}
1208`
1209 data, err := JsonToActionGraphContainer(inputString)
1210 if err != nil {
1211 t.Error(err)
1212 return
1213 }
1214 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
1215 if err != nil {
1216 t.Errorf("Unexpected error %q", err)
1217 return
1218 }
1219 assertBuildStatements(t, []*BuildStatement{{
1220 Command: "mkdir -p path/to && rm -f path/to/symlink && ln -sf {DOTDOTS_TO_OUTPUT_ROOT}../../target/product/emulator_x86_64/system path/to/symlink",
1221 OutputPaths: []string{"path/to/symlink"},
1222 Mnemonic: "UnresolvedSymlink",
1223 SymlinkPaths: []string{"path/to/symlink"},
1224 ImplicitDeps: []string{"target/product/emulator_x86_64/system"},
1225 }}, actual)
1226}
1227
1228func TestUnresolvedSymlinkBazelSandwichWithAlternativeDeps(t *testing.T) {
1229 const inputString = `
1230{
1231 "artifacts": [
1232 { "id": 1, "path_fragment_id": 1 }
1233 ],
1234 "actions": [{
1235 "target_id": 1,
1236 "action_key": "x",
1237 "mnemonic": "UnresolvedSymlink",
1238 "configuration_id": 1,
1239 "output_ids": [1],
1240 "primary_output_id": 1,
1241 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1242 "unresolved_symlink_target": "bazel_sandwich:{\"depend_on_target\":false,\"implicit_deps\":[\"target/product/emulator_x86_64/obj/PACKAGING/systemimage_intermediates/staging_dir.stamp\"],\"target\":\"target/product/emulator_x86_64/system\"}"
1243 }],
1244 "path_fragments": [
1245 { "id": 1, "label": "path/to/symlink" }
1246 ]
1247}
1248`
1249 data, err := JsonToActionGraphContainer(inputString)
1250 if err != nil {
1251 t.Error(err)
1252 return
1253 }
1254 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
1255 if err != nil {
1256 t.Errorf("Unexpected error %q", err)
1257 return
1258 }
1259 assertBuildStatements(t, []*BuildStatement{{
1260 Command: "mkdir -p path/to && rm -f path/to/symlink && ln -sf {DOTDOTS_TO_OUTPUT_ROOT}../../target/product/emulator_x86_64/system path/to/symlink",
1261 OutputPaths: []string{"path/to/symlink"},
1262 Mnemonic: "UnresolvedSymlink",
1263 SymlinkPaths: []string{"path/to/symlink"},
1264 // Note that the target of the symlink, target/product/emulator_x86_64/system, is not listed here
1265 ImplicitDeps: []string{"target/product/emulator_x86_64/obj/PACKAGING/systemimage_intermediates/staging_dir.stamp"},
1266 }}, actual)
1267}
1268
Chris Parsons4f069892021-01-15 12:22:41 -05001269func assertError(t *testing.T, err error, expected string) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001270 t.Helper()
Chris Parsons943f2432021-01-19 11:36:50 -05001271 if err == nil {
1272 t.Errorf("expected error '%s', but got no error", expected)
1273 } else if err.Error() != expected {
Liz Kammerc49e6822021-06-08 15:04:11 -04001274 t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
Chris Parsons4f069892021-01-15 12:22:41 -05001275 }
1276}
1277
1278// Asserts that the given actual build statements match the given expected build statements.
1279// Build statement equivalence is determined using buildStatementEquals.
Liz Kammera4655a92023-02-10 17:17:28 -05001280func assertBuildStatements(t *testing.T, expected []*BuildStatement, actual []*BuildStatement) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001281 t.Helper()
Chris Parsons4f069892021-01-15 12:22:41 -05001282 if len(expected) != len(actual) {
Liz Kammerc3192992021-11-16 17:01:11 -05001283 t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
Chris Parsons4f069892021-01-15 12:22:41 -05001284 len(expected), len(actual), expected, actual)
1285 return
1286 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001287 type compareFn = func(i int, j int) bool
Liz Kammera4655a92023-02-10 17:17:28 -05001288 byCommand := func(slice []*BuildStatement) compareFn {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001289 return func(i int, j int) bool {
Liz Kammera4655a92023-02-10 17:17:28 -05001290 if slice[i] == nil {
1291 return false
1292 } else if slice[j] == nil {
1293 return false
1294 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001295 return slice[i].Command < slice[j].Command
Chris Parsons4f069892021-01-15 12:22:41 -05001296 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001297 }
1298 sort.SliceStable(expected, byCommand(expected))
1299 sort.SliceStable(actual, byCommand(actual))
1300 for i, actualStatement := range actual {
1301 expectedStatement := expected[i]
1302 if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
1303 t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
Usta Shresthaef922252022-06-02 14:23:02 -04001304 differingField, actualStatement, expectedStatement)
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001305 return
1306 }
Chris Parsons4f069892021-01-15 12:22:41 -05001307 }
1308}
1309
Liz Kammera4655a92023-02-10 17:17:28 -05001310func buildStatementEquals(first *BuildStatement, second *BuildStatement) string {
1311 if (first == nil) != (second == nil) {
1312 return "Nil"
1313 }
Chris Parsons4f069892021-01-15 12:22:41 -05001314 if first.Mnemonic != second.Mnemonic {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001315 return "Mnemonic"
Chris Parsons4f069892021-01-15 12:22:41 -05001316 }
1317 if first.Command != second.Command {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001318 return "Command"
Chris Parsons4f069892021-01-15 12:22:41 -05001319 }
1320 // Ordering is significant for environment variables.
1321 if !reflect.DeepEqual(first.Env, second.Env) {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001322 return "Env"
Chris Parsons4f069892021-01-15 12:22:41 -05001323 }
1324 // Ordering is irrelevant for input and output paths, so compare sets.
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001325 if !reflect.DeepEqual(sortedStrings(first.InputPaths), sortedStrings(second.InputPaths)) {
1326 return "InputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001327 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001328 if !reflect.DeepEqual(sortedStrings(first.OutputPaths), sortedStrings(second.OutputPaths)) {
1329 return "OutputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001330 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001331 if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
1332 return "SymlinkPaths"
Liz Kammerc49e6822021-06-08 15:04:11 -04001333 }
Cole Faustbc65a3f2023-08-01 16:38:55 +00001334 if !reflect.DeepEqual(sortedStrings(first.ImplicitDeps), sortedStrings(second.ImplicitDeps)) {
1335 return "ImplicitDeps"
1336 }
Liz Kammerc49e6822021-06-08 15:04:11 -04001337 if first.Depfile != second.Depfile {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001338 return "Depfile"
Liz Kammerc49e6822021-06-08 15:04:11 -04001339 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001340 return ""
Chris Parsons4f069892021-01-15 12:22:41 -05001341}
1342
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001343func sortedStrings(stringSlice []string) []string {
1344 sorted := make([]string, len(stringSlice))
1345 copy(sorted, stringSlice)
1346 sort.Strings(sorted)
1347 return sorted
Chris Parsons4f069892021-01-15 12:22:41 -05001348}
Jason Wu118fd2b2022-10-27 18:41:15 +00001349
1350// Transform the json format to ActionGraphContainer
1351func JsonToActionGraphContainer(inputString string) ([]byte, error) {
1352 var aqueryProtoResult analysis_v2_proto.ActionGraphContainer
1353 err := json.Unmarshal([]byte(inputString), &aqueryProtoResult)
1354 if err != nil {
1355 return []byte(""), err
1356 }
1357 data, _ := proto.Marshal(&aqueryProtoResult)
1358 return data, err
1359}