blob: c6b139e078a33d5ac41da7ef6591ef9fe0d9a43c [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{})
Usta Shrestha16ac1352022-06-22 11:01:55 -0400142 var expectedBuildStatements []BuildStatement
Chris Parsons4f069892021-01-15 12:22:41 -0500143 for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
144 expectedBuildStatements = append(expectedBuildStatements,
145 BuildStatement{
146 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 },
Chris Parsons4f069892021-01-15 12:22:41 -0500152 Env: []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)
360 }
361 if expected := 1; len(actual) != expected {
362 t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
363 }
364
365 bs := actual[0]
366 expectedDepfile := "two.d"
367 if bs.Depfile == nil {
368 t.Errorf("Expected depfile %q, but there was none found", expectedDepfile)
369 } else if *bs.Depfile != expectedDepfile {
370 t.Errorf("Expected depfile %q, but got %q", expectedDepfile, *bs.Depfile)
371 }
372}
373
374func TestMultipleDepfiles(t *testing.T) {
375 const inputString = `
376{
Jason Wu118fd2b2022-10-27 18:41:15 +0000377 "artifacts": [
378 { "id": 1, "path_fragment_id": 1 },
379 { "id": 2, "path_fragment_id": 2 },
380 { "id": 3, "path_fragment_id": 3 },
381 { "id": 4, "path_fragment_id": 4 }],
382 "actions": [{
383 "target_id": 1,
384 "action_key": "x",
385 "mnemonic": "x",
386 "arguments": ["touch", "foo"],
387 "input_dep_set_ids": [1],
388 "output_ids": [2,3,4],
389 "primary_output_id": 2
390 }],
391 "dep_set_of_files": [{
392 "id": 1,
393 "direct_artifact_ids": [1, 2, 3, 4]
394 }],
395 "path_fragments": [
396 { "id": 1, "label": "one" },
397 { "id": 2, "label": "two" },
398 { "id": 3, "label": "two.d" },
399 { "id": 4, "label": "other.d" }]
Liz Kammerde116852021-03-25 16:42:37 -0400400}`
401
Jason Wu118fd2b2022-10-27 18:41:15 +0000402 data, err := JsonToActionGraphContainer(inputString)
403 if err != nil {
404 t.Error(err)
405 return
406 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500407 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerde116852021-03-25 16:42:37 -0400408 assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
409}
410
Chris Parsons943f2432021-01-19 11:36:50 -0500411func TestTransitiveInputDepsets(t *testing.T) {
412 // The input aquery for this test comes from a proof-of-concept starlark rule which registers
413 // a single action with many inputs given via a deep depset.
414 const inputString = `
415{
Jason Wu118fd2b2022-10-27 18:41:15 +0000416 "artifacts": [
417 { "id": 1, "path_fragment_id": 1 },
418 { "id": 2, "path_fragment_id": 7 },
419 { "id": 3, "path_fragment_id": 8 },
420 { "id": 4, "path_fragment_id": 9 },
421 { "id": 5, "path_fragment_id": 10 },
422 { "id": 6, "path_fragment_id": 11 },
423 { "id": 7, "path_fragment_id": 12 },
424 { "id": 8, "path_fragment_id": 13 },
425 { "id": 9, "path_fragment_id": 14 },
426 { "id": 10, "path_fragment_id": 15 },
427 { "id": 11, "path_fragment_id": 16 },
428 { "id": 12, "path_fragment_id": 17 },
429 { "id": 13, "path_fragment_id": 18 },
430 { "id": 14, "path_fragment_id": 19 },
431 { "id": 15, "path_fragment_id": 20 },
432 { "id": 16, "path_fragment_id": 21 },
433 { "id": 17, "path_fragment_id": 22 },
434 { "id": 18, "path_fragment_id": 23 },
435 { "id": 19, "path_fragment_id": 24 },
436 { "id": 20, "path_fragment_id": 25 },
437 { "id": 21, "path_fragment_id": 26 }],
438 "actions": [{
439 "target_id": 1,
440 "action_key": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
441 "mnemonic": "Action",
442 "configuration_id": 1,
443 "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"],
444 "input_dep_set_ids": [1],
445 "output_ids": [21],
446 "primary_output_id": 21
447 }],
448 "dep_set_of_files": [
449 { "id": 3, "direct_artifact_ids": [1, 2, 3, 4, 5] },
450 { "id": 4, "direct_artifact_ids": [6, 7, 8, 9, 10] },
451 { "id": 2, "transitive_dep_set_ids": [3, 4], "direct_artifact_ids": [11, 12, 13, 14, 15] },
452 { "id": 5, "direct_artifact_ids": [16, 17, 18, 19] },
453 { "id": 1, "transitive_dep_set_ids": [2, 5], "direct_artifact_ids": [20] }],
454 "path_fragments": [
455 { "id": 6, "label": "bazel-out" },
456 { "id": 5, "label": "sourceroot", "parent_id": 6 },
457 { "id": 4, "label": "k8-fastbuild", "parent_id": 5 },
458 { "id": 3, "label": "bin", "parent_id": 4 },
459 { "id": 2, "label": "testpkg", "parent_id": 3 },
460 { "id": 1, "label": "test_1", "parent_id": 2 },
461 { "id": 7, "label": "test_2", "parent_id": 2 },
462 { "id": 8, "label": "test_3", "parent_id": 2 },
463 { "id": 9, "label": "test_4", "parent_id": 2 },
464 { "id": 10, "label": "test_5", "parent_id": 2 },
465 { "id": 11, "label": "test_6", "parent_id": 2 },
466 { "id": 12, "label": "test_7", "parent_id": 2 },
467 { "id": 13, "label": "test_8", "parent_id": 2 },
468 { "id": 14, "label": "test_9", "parent_id": 2 },
469 { "id": 15, "label": "test_10", "parent_id": 2 },
470 { "id": 16, "label": "test_11", "parent_id": 2 },
471 { "id": 17, "label": "test_12", "parent_id": 2 },
472 { "id": 18, "label": "test_13", "parent_id": 2 },
473 { "id": 19, "label": "test_14", "parent_id": 2 },
474 { "id": 20, "label": "test_15", "parent_id": 2 },
475 { "id": 21, "label": "test_16", "parent_id": 2 },
476 { "id": 22, "label": "test_17", "parent_id": 2 },
477 { "id": 23, "label": "test_18", "parent_id": 2 },
478 { "id": 24, "label": "test_19", "parent_id": 2 },
479 { "id": 25, "label": "test_root", "parent_id": 2 },
480 { "id": 26,"label": "test_out", "parent_id": 2 }]
Chris Parsons943f2432021-01-19 11:36:50 -0500481}`
482
Jason Wu118fd2b2022-10-27 18:41:15 +0000483 data, err := JsonToActionGraphContainer(inputString)
484 if err != nil {
485 t.Error(err)
486 return
487 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500488 actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
Chris Parsons1a7aca02022-04-25 22:35:15 -0400489
Chris Parsons943f2432021-01-19 11:36:50 -0500490 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400491 {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400492 Command: "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
493 OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
494 Mnemonic: "Action",
Chris Parsons943f2432021-01-19 11:36:50 -0500495 },
496 }
497 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400498
499 // Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
500 // are given via a deep depset, but the depset is flattened when returned as a
501 // BuildStatement slice.
Usta Shrestha16ac1352022-06-22 11:01:55 -0400502 var expectedFlattenedInputs []string
Chris Parsons1a7aca02022-04-25 22:35:15 -0400503 for i := 1; i < 20; i++ {
504 expectedFlattenedInputs = append(expectedFlattenedInputs, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
505 }
506 expectedFlattenedInputs = append(expectedFlattenedInputs, "bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root")
507
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400508 actualDepsetHashes := actualbuildStatements[0].InputDepsetHashes
509 actualFlattenedInputs := flattenDepsets(actualDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400510 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
511 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
512 }
Chris Parsons943f2432021-01-19 11:36:50 -0500513}
514
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700515func TestSymlinkTree(t *testing.T) {
516 const inputString = `
517{
Jason Wu118fd2b2022-10-27 18:41:15 +0000518 "artifacts": [
519 { "id": 1, "path_fragment_id": 1 },
520 { "id": 2, "path_fragment_id": 2 }],
521 "actions": [{
522 "target_id": 1,
523 "action_key": "x",
524 "mnemonic": "SymlinkTree",
525 "configuration_id": 1,
526 "input_dep_set_ids": [1],
527 "output_ids": [2],
528 "primary_output_id": 2,
529 "execution_platform": "//build/bazel/platforms:linux_x86_64"
530 }],
531 "path_fragments": [
532 { "id": 1, "label": "foo.manifest" },
533 { "id": 2, "label": "foo.runfiles/MANIFEST" }],
534 "dep_set_of_files": [
535 { "id": 1, "direct_artifact_ids": [1] }]
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700536}
537`
Jason Wu118fd2b2022-10-27 18:41:15 +0000538 data, err := JsonToActionGraphContainer(inputString)
539 if err != nil {
540 t.Error(err)
541 return
542 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500543 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700544 if err != nil {
545 t.Errorf("Unexpected error %q", err)
546 }
547 assertBuildStatements(t, []BuildStatement{
548 {
549 Command: "",
550 OutputPaths: []string{"foo.runfiles/MANIFEST"},
551 Mnemonic: "SymlinkTree",
552 InputPaths: []string{"foo.manifest"},
553 },
554 }, actual)
555}
556
Usta Shresthaef922252022-06-02 14:23:02 -0400557func TestBazelOutRemovalFromInputDepsets(t *testing.T) {
558 const inputString = `{
Jason Wu118fd2b2022-10-27 18:41:15 +0000559 "artifacts": [
560 { "id": 1, "path_fragment_id": 10 },
561 { "id": 2, "path_fragment_id": 20 },
562 { "id": 3, "path_fragment_id": 30 },
563 { "id": 4, "path_fragment_id": 40 }],
564 "dep_set_of_files": [{
565 "id": 1111,
566 "direct_artifact_ids": [3 , 4]
567 }, {
568 "id": 2222,
569 "direct_artifact_ids": [3]
570 }],
571 "actions": [{
572 "target_id": 100,
573 "action_key": "x",
574 "input_dep_set_ids": [1111, 2222],
575 "mnemonic": "x",
576 "arguments": ["bogus", "command"],
577 "output_ids": [2],
578 "primary_output_id": 1
579 }],
580 "path_fragments": [
581 { "id": 10, "label": "input" },
582 { "id": 20, "label": "output" },
583 { "id": 30, "label": "dep1", "parent_id": 50 },
584 { "id": 40, "label": "dep2", "parent_id": 60 },
585 { "id": 50, "label": "bazel_tools", "parent_id": 60 },
586 { "id": 60, "label": ".."}
587 ]
Usta Shresthaef922252022-06-02 14:23:02 -0400588}`
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500589 /* depsets
590 1111 2222
591 / \ |
592 ../dep2 ../bazel_tools/dep1
593 */
Jason Wu118fd2b2022-10-27 18:41:15 +0000594 data, err := JsonToActionGraphContainer(inputString)
595 if err != nil {
596 t.Error(err)
597 return
598 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500599 actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500600 if len(actualDepsets) != 1 {
Usta Shresthaef922252022-06-02 14:23:02 -0400601 t.Errorf("expected 1 depset but found %#v", actualDepsets)
602 return
603 }
604 dep2Found := false
605 for _, dep := range flattenDepsets([]string{actualDepsets[0].ContentHash}, actualDepsets) {
606 if dep == "../bazel_tools/dep1" {
607 t.Errorf("dependency %s expected to be removed but still exists", dep)
608 } else if dep == "../dep2" {
609 dep2Found = true
610 }
611 }
612 if !dep2Found {
613 t.Errorf("dependency ../dep2 expected but not found")
614 }
615
616 expectedBuildStatement := BuildStatement{
617 Command: "bogus command",
618 OutputPaths: []string{"output"},
619 Mnemonic: "x",
620 }
621 buildStatementFound := false
622 for _, actualBuildStatement := range actualBuildStatements {
623 if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
624 buildStatementFound = true
625 break
626 }
627 }
628 if !buildStatementFound {
629 t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
630 return
631 }
632}
633
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500634func TestBazelOutRemovalFromTransitiveInputDepsets(t *testing.T) {
635 const inputString = `{
636 "artifacts": [
637 { "id": 1, "path_fragment_id": 10 },
638 { "id": 2, "path_fragment_id": 20 },
639 { "id": 3, "path_fragment_id": 30 }],
640 "dep_set_of_files": [{
641 "id": 1111,
642 "transitive_dep_set_ids": [2222]
643 }, {
644 "id": 2222,
645 "direct_artifact_ids": [3]
646 }, {
647 "id": 3333,
648 "direct_artifact_ids": [3]
649 }, {
650 "id": 4444,
651 "transitive_dep_set_ids": [3333]
652 }],
653 "actions": [{
654 "target_id": 100,
655 "action_key": "x",
656 "input_dep_set_ids": [1111, 4444],
657 "mnemonic": "x",
658 "arguments": ["bogus", "command"],
659 "output_ids": [2],
660 "primary_output_id": 1
661 }],
662 "path_fragments": [
663 { "id": 10, "label": "input" },
664 { "id": 20, "label": "output" },
665 { "id": 30, "label": "dep", "parent_id": 50 },
666 { "id": 50, "label": "bazel_tools", "parent_id": 60 },
667 { "id": 60, "label": ".."}
668 ]
669}`
670 /* depsets
671 1111 4444
672 || ||
673 2222 3333
674 | |
675 ../bazel_tools/dep
676 Note: in dep_set_of_files:
677 1111 appears BEFORE its dependency,2222 while
678 4444 appears AFTER its dependency 3333
679 and this test shows that that order doesn't affect empty depset pruning
680 */
681 data, err := JsonToActionGraphContainer(inputString)
682 if err != nil {
683 t.Error(err)
684 return
685 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500686 actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500687 if len(actualDepsets) != 0 {
688 t.Errorf("expected 0 depsets but found %#v", actualDepsets)
689 return
690 }
691
692 expectedBuildStatement := BuildStatement{
693 Command: "bogus command",
694 OutputPaths: []string{"output"},
695 Mnemonic: "x",
696 }
697 buildStatementFound := false
698 for _, actualBuildStatement := range actualBuildStatements {
699 if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
700 buildStatementFound = true
701 break
702 }
703 }
704 if !buildStatementFound {
705 t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
706 return
707 }
708}
709
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400710func TestMiddlemenAction(t *testing.T) {
711 const inputString = `
712{
Jason Wu118fd2b2022-10-27 18:41:15 +0000713 "artifacts": [
714 { "id": 1, "path_fragment_id": 1 },
715 { "id": 2, "path_fragment_id": 2 },
716 { "id": 3, "path_fragment_id": 3 },
717 { "id": 4, "path_fragment_id": 4 },
718 { "id": 5, "path_fragment_id": 5 },
719 { "id": 6, "path_fragment_id": 6 }],
720 "path_fragments": [
721 { "id": 1, "label": "middleinput_one" },
722 { "id": 2, "label": "middleinput_two" },
723 { "id": 3, "label": "middleman_artifact" },
724 { "id": 4, "label": "maininput_one" },
725 { "id": 5, "label": "maininput_two" },
726 { "id": 6, "label": "output" }],
727 "dep_set_of_files": [
728 { "id": 1, "direct_artifact_ids": [1, 2] },
729 { "id": 2, "direct_artifact_ids": [3, 4, 5] }],
730 "actions": [{
731 "target_id": 1,
732 "action_key": "x",
733 "mnemonic": "Middleman",
734 "arguments": ["touch", "foo"],
735 "input_dep_set_ids": [1],
736 "output_ids": [3],
737 "primary_output_id": 3
738 }, {
739 "target_id": 2,
740 "action_key": "y",
741 "mnemonic": "Main action",
742 "arguments": ["touch", "foo"],
743 "input_dep_set_ids": [2],
744 "output_ids": [6],
745 "primary_output_id": 6
746 }]
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400747}`
Jason Wu118fd2b2022-10-27 18:41:15 +0000748 data, err := JsonToActionGraphContainer(inputString)
749 if err != nil {
750 t.Error(err)
751 return
752 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500753 actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400754 if err != nil {
755 t.Errorf("Unexpected error %q", err)
756 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400757 if expected := 1; len(actualBuildStatements) != expected {
758 t.Fatalf("Expected %d build statements, got %d", expected, len(actualBuildStatements))
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400759 }
760
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400761 expectedDepsetFiles := [][]string{
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400762 {"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"},
763 {"middleinput_one", "middleinput_two"},
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400764 }
765 assertFlattenedDepsets(t, actualDepsets, expectedDepsetFiles)
766
Chris Parsons1a7aca02022-04-25 22:35:15 -0400767 bs := actualBuildStatements[0]
768 if len(bs.InputPaths) > 0 {
769 t.Errorf("Expected main action raw inputs to be empty, but got %q", bs.InputPaths)
770 }
771
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400772 expectedOutputs := []string{"output"}
773 if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) {
774 t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths)
775 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400776
Chris Parsons1a7aca02022-04-25 22:35:15 -0400777 expectedFlattenedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400778 actualFlattenedInputs := flattenDepsets(bs.InputDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400779
780 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
781 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
782 }
783}
784
785// Returns the contents of given depsets in concatenated post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400786func flattenDepsets(depsetHashesToFlatten []string, allDepsets []AqueryDepset) []string {
787 depsetsByHash := map[string]AqueryDepset{}
Chris Parsons1a7aca02022-04-25 22:35:15 -0400788 for _, depset := range allDepsets {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400789 depsetsByHash[depset.ContentHash] = depset
Chris Parsons1a7aca02022-04-25 22:35:15 -0400790 }
Usta Shrestha16ac1352022-06-22 11:01:55 -0400791 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400792 for _, depsetId := range depsetHashesToFlatten {
793 result = append(result, flattenDepset(depsetId, depsetsByHash)...)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400794 }
795 return result
796}
797
798// Returns the contents of a given depset in post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400799func flattenDepset(depsetHashToFlatten string, allDepsets map[string]AqueryDepset) []string {
800 depset := allDepsets[depsetHashToFlatten]
Usta Shrestha16ac1352022-06-22 11:01:55 -0400801 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400802 for _, depsetId := range depset.TransitiveDepSetHashes {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400803 result = append(result, flattenDepset(depsetId, allDepsets)...)
804 }
805 result = append(result, depset.DirectArtifacts...)
806 return result
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400807}
808
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400809func assertFlattenedDepsets(t *testing.T, actualDepsets []AqueryDepset, expectedDepsetFiles [][]string) {
810 t.Helper()
811 if len(actualDepsets) != len(expectedDepsetFiles) {
Sasha Smundakf10c3ac2022-06-08 11:46:31 -0700812 t.Errorf("Expected %d depsets, but got %d depsets", len(expectedDepsetFiles), len(actualDepsets))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400813 }
814 for i, actualDepset := range actualDepsets {
815 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
816 if !reflect.DeepEqual(actualFlattenedInputs, expectedDepsetFiles[i]) {
817 t.Errorf("Expected depset files: %v, but got %v", expectedDepsetFiles[i], actualFlattenedInputs)
818 }
819 }
820}
821
Liz Kammerc49e6822021-06-08 15:04:11 -0400822func TestSimpleSymlink(t *testing.T) {
823 const inputString = `
824{
Jason Wu118fd2b2022-10-27 18:41:15 +0000825 "artifacts": [
826 { "id": 1, "path_fragment_id": 3 },
827 { "id": 2, "path_fragment_id": 5 }],
828 "actions": [{
829 "target_id": 1,
830 "action_key": "x",
831 "mnemonic": "Symlink",
832 "input_dep_set_ids": [1],
833 "output_ids": [2],
834 "primary_output_id": 2
835 }],
836 "dep_set_of_files": [
837 { "id": 1, "direct_artifact_ids": [1] }],
838 "path_fragments": [
839 { "id": 1, "label": "one" },
840 { "id": 2, "label": "file_subdir", "parent_id": 1 },
841 { "id": 3, "label": "file", "parent_id": 2 },
842 { "id": 4, "label": "symlink_subdir", "parent_id": 1 },
843 { "id": 5, "label": "symlink", "parent_id": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400844}`
Jason Wu118fd2b2022-10-27 18:41:15 +0000845 data, err := JsonToActionGraphContainer(inputString)
846 if err != nil {
847 t.Error(err)
848 return
849 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500850 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerc49e6822021-06-08 15:04:11 -0400851
852 if err != nil {
853 t.Errorf("Unexpected error %q", err)
854 }
855
856 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400857 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400858 Command: "mkdir -p one/symlink_subdir && " +
859 "rm -f one/symlink_subdir/symlink && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400860 "ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
Liz Kammerc49e6822021-06-08 15:04:11 -0400861 InputPaths: []string{"one/file_subdir/file"},
862 OutputPaths: []string{"one/symlink_subdir/symlink"},
863 SymlinkPaths: []string{"one/symlink_subdir/symlink"},
864 Mnemonic: "Symlink",
865 },
866 }
867 assertBuildStatements(t, actual, expectedBuildStatements)
868}
869
870func TestSymlinkQuotesPaths(t *testing.T) {
871 const inputString = `
872{
Jason Wu118fd2b2022-10-27 18:41:15 +0000873 "artifacts": [
874 { "id": 1, "path_fragment_id": 3 },
875 { "id": 2, "path_fragment_id": 5 }],
876 "actions": [{
877 "target_id": 1,
878 "action_key": "x",
879 "mnemonic": "SolibSymlink",
880 "input_dep_set_ids": [1],
881 "output_ids": [2],
882 "primary_output_id": 2
883 }],
884 "dep_set_of_files": [
885 { "id": 1, "direct_artifact_ids": [1] }],
886 "path_fragments": [
887 { "id": 1, "label": "one" },
888 { "id": 2, "label": "file subdir", "parent_id": 1 },
889 { "id": 3, "label": "file", "parent_id": 2 },
890 { "id": 4, "label": "symlink subdir", "parent_id": 1 },
891 { "id": 5, "label": "symlink", "parent_id": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400892}`
893
Jason Wu118fd2b2022-10-27 18:41:15 +0000894 data, err := JsonToActionGraphContainer(inputString)
895 if err != nil {
896 t.Error(err)
897 return
898 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500899 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerc49e6822021-06-08 15:04:11 -0400900 if err != nil {
901 t.Errorf("Unexpected error %q", err)
902 }
903
904 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400905 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400906 Command: "mkdir -p 'one/symlink subdir' && " +
907 "rm -f 'one/symlink subdir/symlink' && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400908 "ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
Liz Kammerc49e6822021-06-08 15:04:11 -0400909 InputPaths: []string{"one/file subdir/file"},
910 OutputPaths: []string{"one/symlink subdir/symlink"},
911 SymlinkPaths: []string{"one/symlink subdir/symlink"},
912 Mnemonic: "SolibSymlink",
913 },
914 }
Liz Kammerc7737782021-11-04 10:56:13 -0400915 assertBuildStatements(t, expectedBuildStatements, actual)
Liz Kammerc49e6822021-06-08 15:04:11 -0400916}
917
918func TestSymlinkMultipleInputs(t *testing.T) {
919 const inputString = `
920{
Jason Wu118fd2b2022-10-27 18:41:15 +0000921 "artifacts": [
922 { "id": 1, "path_fragment_id": 1 },
923 { "id": 2, "path_fragment_id": 2 },
924 { "id": 3, "path_fragment_id": 3 }],
925 "actions": [{
926 "target_id": 1,
927 "action_key": "x",
928 "mnemonic": "Symlink",
929 "input_dep_set_ids": [1],
930 "output_ids": [3],
931 "primary_output_id": 3
932 }],
933 "dep_set_of_files": [{ "id": 1, "direct_artifact_ids": [1,2] }],
934 "path_fragments": [
935 { "id": 1, "label": "file" },
936 { "id": 2, "label": "other_file" },
937 { "id": 3, "label": "symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400938}`
939
Jason Wu118fd2b2022-10-27 18:41:15 +0000940 data, err := JsonToActionGraphContainer(inputString)
941 if err != nil {
942 t.Error(err)
943 return
944 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500945 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerc49e6822021-06-08 15:04:11 -0400946 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
947}
948
949func TestSymlinkMultipleOutputs(t *testing.T) {
950 const inputString = `
951{
Jason Wu118fd2b2022-10-27 18:41:15 +0000952 "artifacts": [
953 { "id": 1, "path_fragment_id": 1 },
954 { "id": 3, "path_fragment_id": 3 }],
955 "actions": [{
956 "target_id": 1,
957 "action_key": "x",
958 "mnemonic": "Symlink",
959 "input_dep_set_ids": [1],
960 "output_ids": [2,3],
961 "primary_output_id": 2
962 }],
963 "dep_set_of_files": [
964 { "id": 1, "direct_artifact_ids": [1] }],
965 "path_fragments": [
966 { "id": 1, "label": "file" },
967 { "id": 2, "label": "symlink" },
968 { "id": 3, "label": "other_symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400969}`
970
Jason Wu118fd2b2022-10-27 18:41:15 +0000971 data, err := JsonToActionGraphContainer(inputString)
972 if err != nil {
973 t.Error(err)
974 return
975 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500976 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Jason Wu118fd2b2022-10-27 18:41:15 +0000977 assertError(t, err, "undefined outputId 2")
Liz Kammerc49e6822021-06-08 15:04:11 -0400978}
979
Wei Li455ba832021-11-04 22:58:12 +0000980func TestTemplateExpandActionSubstitutions(t *testing.T) {
981 const inputString = `
982{
Jason Wu118fd2b2022-10-27 18:41:15 +0000983 "artifacts": [{
984 "id": 1,
985 "path_fragment_id": 1
986 }],
987 "actions": [{
988 "target_id": 1,
989 "action_key": "x",
990 "mnemonic": "TemplateExpand",
991 "configuration_id": 1,
992 "output_ids": [1],
993 "primary_output_id": 1,
994 "execution_platform": "//build/bazel/platforms:linux_x86_64",
995 "template_content": "Test template substitutions: %token1%, %python_binary%",
996 "substitutions": [
997 { "key": "%token1%", "value": "abcd" },
998 { "key": "%python_binary%", "value": "python3" }]
999 }],
1000 "path_fragments": [
1001 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +00001002}`
1003
Jason Wu118fd2b2022-10-27 18:41:15 +00001004 data, err := JsonToActionGraphContainer(inputString)
1005 if err != nil {
1006 t.Error(err)
1007 return
1008 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001009 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Wei Li455ba832021-11-04 22:58:12 +00001010 if err != nil {
1011 t.Errorf("Unexpected error %q", err)
1012 }
1013
1014 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -04001015 {
Wei Li455ba832021-11-04 22:58:12 +00001016 Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
1017 "chmod a+x template_file'",
1018 OutputPaths: []string{"template_file"},
1019 Mnemonic: "TemplateExpand",
1020 },
1021 }
1022 assertBuildStatements(t, expectedBuildStatements, actual)
1023}
1024
1025func TestTemplateExpandActionNoOutput(t *testing.T) {
1026 const inputString = `
1027{
Jason Wu118fd2b2022-10-27 18:41:15 +00001028 "artifacts": [
1029 { "id": 1, "path_fragment_id": 1 }],
1030 "actions": [{
1031 "target_id": 1,
1032 "action_key": "x",
1033 "mnemonic": "TemplateExpand",
1034 "configuration_id": 1,
1035 "primary_output_id": 1,
1036 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1037 "templateContent": "Test template substitutions: %token1%, %python_binary%",
1038 "substitutions": [
1039 { "key": "%token1%", "value": "abcd" },
1040 { "key": "%python_binary%", "value": "python3" }]
1041 }],
1042 "path_fragments": [
1043 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +00001044}`
1045
Jason Wu118fd2b2022-10-27 18:41:15 +00001046 data, err := JsonToActionGraphContainer(inputString)
1047 if err != nil {
1048 t.Error(err)
1049 return
1050 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001051 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Wei Li455ba832021-11-04 22:58:12 +00001052 assertError(t, err, `Expect 1 output to template expand action, got: output []`)
1053}
1054
Sasha Smundak1da064c2022-06-08 16:36:16 -07001055func TestFileWrite(t *testing.T) {
1056 const inputString = `
1057{
Jason Wu118fd2b2022-10-27 18:41:15 +00001058 "artifacts": [
1059 { "id": 1, "path_fragment_id": 1 }],
1060 "actions": [{
1061 "target_id": 1,
1062 "action_key": "x",
1063 "mnemonic": "FileWrite",
1064 "configuration_id": 1,
1065 "output_ids": [1],
1066 "primary_output_id": 1,
1067 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1068 "file_contents": "file data\n"
1069 }],
1070 "path_fragments": [
1071 { "id": 1, "label": "foo.manifest" }]
Sasha Smundak1da064c2022-06-08 16:36:16 -07001072}
1073`
Jason Wu118fd2b2022-10-27 18:41:15 +00001074 data, err := JsonToActionGraphContainer(inputString)
1075 if err != nil {
1076 t.Error(err)
1077 return
1078 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001079 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Sasha Smundak1da064c2022-06-08 16:36:16 -07001080 if err != nil {
1081 t.Errorf("Unexpected error %q", err)
1082 }
1083 assertBuildStatements(t, []BuildStatement{
1084 {
1085 OutputPaths: []string{"foo.manifest"},
1086 Mnemonic: "FileWrite",
1087 FileContents: "file data\n",
1088 },
1089 }, actual)
1090}
1091
1092func TestSourceSymlinkManifest(t *testing.T) {
1093 const inputString = `
1094{
Jason Wu118fd2b2022-10-27 18:41:15 +00001095 "artifacts": [
1096 { "id": 1, "path_fragment_id": 1 }],
1097 "actions": [{
1098 "target_id": 1,
1099 "action_key": "x",
1100 "mnemonic": "SourceSymlinkManifest",
1101 "configuration_id": 1,
1102 "output_ids": [1],
1103 "primary_output_id": 1,
1104 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1105 "file_contents": "symlink target\n"
1106 }],
1107 "path_fragments": [
1108 { "id": 1, "label": "foo.manifest" }]
Sasha Smundak1da064c2022-06-08 16:36:16 -07001109}
1110`
Jason Wu118fd2b2022-10-27 18:41:15 +00001111 data, err := JsonToActionGraphContainer(inputString)
1112 if err != nil {
1113 t.Error(err)
1114 return
1115 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001116 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Sasha Smundak1da064c2022-06-08 16:36:16 -07001117 if err != nil {
1118 t.Errorf("Unexpected error %q", err)
1119 }
1120 assertBuildStatements(t, []BuildStatement{
1121 {
1122 OutputPaths: []string{"foo.manifest"},
1123 Mnemonic: "SourceSymlinkManifest",
1124 },
1125 }, actual)
1126}
1127
Chris Parsons4f069892021-01-15 12:22:41 -05001128func assertError(t *testing.T, err error, expected string) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001129 t.Helper()
Chris Parsons943f2432021-01-19 11:36:50 -05001130 if err == nil {
1131 t.Errorf("expected error '%s', but got no error", expected)
1132 } else if err.Error() != expected {
Liz Kammerc49e6822021-06-08 15:04:11 -04001133 t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
Chris Parsons4f069892021-01-15 12:22:41 -05001134 }
1135}
1136
1137// Asserts that the given actual build statements match the given expected build statements.
1138// Build statement equivalence is determined using buildStatementEquals.
1139func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001140 t.Helper()
Chris Parsons4f069892021-01-15 12:22:41 -05001141 if len(expected) != len(actual) {
Liz Kammerc3192992021-11-16 17:01:11 -05001142 t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
Chris Parsons4f069892021-01-15 12:22:41 -05001143 len(expected), len(actual), expected, actual)
1144 return
1145 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001146 type compareFn = func(i int, j int) bool
1147 byCommand := func(slice []BuildStatement) compareFn {
1148 return func(i int, j int) bool {
1149 return slice[i].Command < slice[j].Command
Chris Parsons4f069892021-01-15 12:22:41 -05001150 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001151 }
1152 sort.SliceStable(expected, byCommand(expected))
1153 sort.SliceStable(actual, byCommand(actual))
1154 for i, actualStatement := range actual {
1155 expectedStatement := expected[i]
1156 if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
1157 t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
Usta Shresthaef922252022-06-02 14:23:02 -04001158 differingField, actualStatement, expectedStatement)
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001159 return
1160 }
Chris Parsons4f069892021-01-15 12:22:41 -05001161 }
1162}
1163
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001164func buildStatementEquals(first BuildStatement, second BuildStatement) string {
Chris Parsons4f069892021-01-15 12:22:41 -05001165 if first.Mnemonic != second.Mnemonic {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001166 return "Mnemonic"
Chris Parsons4f069892021-01-15 12:22:41 -05001167 }
1168 if first.Command != second.Command {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001169 return "Command"
Chris Parsons4f069892021-01-15 12:22:41 -05001170 }
1171 // Ordering is significant for environment variables.
1172 if !reflect.DeepEqual(first.Env, second.Env) {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001173 return "Env"
Chris Parsons4f069892021-01-15 12:22:41 -05001174 }
1175 // Ordering is irrelevant for input and output paths, so compare sets.
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001176 if !reflect.DeepEqual(sortedStrings(first.InputPaths), sortedStrings(second.InputPaths)) {
1177 return "InputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001178 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001179 if !reflect.DeepEqual(sortedStrings(first.OutputPaths), sortedStrings(second.OutputPaths)) {
1180 return "OutputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001181 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001182 if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
1183 return "SymlinkPaths"
Liz Kammerc49e6822021-06-08 15:04:11 -04001184 }
1185 if first.Depfile != second.Depfile {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001186 return "Depfile"
Liz Kammerc49e6822021-06-08 15:04:11 -04001187 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001188 return ""
Chris Parsons4f069892021-01-15 12:22:41 -05001189}
1190
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001191func sortedStrings(stringSlice []string) []string {
1192 sorted := make([]string, len(stringSlice))
1193 copy(sorted, stringSlice)
1194 sort.Strings(sorted)
1195 return sorted
Chris Parsons4f069892021-01-15 12:22:41 -05001196}
Jason Wu118fd2b2022-10-27 18:41:15 +00001197
1198// Transform the json format to ActionGraphContainer
1199func JsonToActionGraphContainer(inputString string) ([]byte, error) {
1200 var aqueryProtoResult analysis_v2_proto.ActionGraphContainer
1201 err := json.Unmarshal([]byte(inputString), &aqueryProtoResult)
1202 if err != nil {
1203 return []byte(""), err
1204 }
1205 data, _ := proto.Marshal(&aqueryProtoResult)
1206 return data, err
1207}