blob: 68fdd687e6cbdfd89c6d879c1767b5772d47b2d9 [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 },
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)
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 {
Liz Kammer00629db2023-02-09 14:28:15 -0500492 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",
495 SymlinkPaths: []string{},
Chris Parsons943f2432021-01-19 11:36:50 -0500496 },
497 }
498 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400499
500 // Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
501 // are given via a deep depset, but the depset is flattened when returned as a
502 // BuildStatement slice.
Usta Shrestha16ac1352022-06-22 11:01:55 -0400503 var expectedFlattenedInputs []string
Chris Parsons1a7aca02022-04-25 22:35:15 -0400504 for i := 1; i < 20; i++ {
505 expectedFlattenedInputs = append(expectedFlattenedInputs, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
506 }
507 expectedFlattenedInputs = append(expectedFlattenedInputs, "bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root")
508
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400509 actualDepsetHashes := actualbuildStatements[0].InputDepsetHashes
510 actualFlattenedInputs := flattenDepsets(actualDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400511 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
512 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
513 }
Chris Parsons943f2432021-01-19 11:36:50 -0500514}
515
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700516func TestSymlinkTree(t *testing.T) {
517 const inputString = `
518{
Jason Wu118fd2b2022-10-27 18:41:15 +0000519 "artifacts": [
520 { "id": 1, "path_fragment_id": 1 },
521 { "id": 2, "path_fragment_id": 2 }],
522 "actions": [{
523 "target_id": 1,
524 "action_key": "x",
525 "mnemonic": "SymlinkTree",
526 "configuration_id": 1,
527 "input_dep_set_ids": [1],
528 "output_ids": [2],
529 "primary_output_id": 2,
530 "execution_platform": "//build/bazel/platforms:linux_x86_64"
531 }],
532 "path_fragments": [
533 { "id": 1, "label": "foo.manifest" },
534 { "id": 2, "label": "foo.runfiles/MANIFEST" }],
535 "dep_set_of_files": [
536 { "id": 1, "direct_artifact_ids": [1] }]
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700537}
538`
Jason Wu118fd2b2022-10-27 18:41:15 +0000539 data, err := JsonToActionGraphContainer(inputString)
540 if err != nil {
541 t.Error(err)
542 return
543 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500544 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700545 if err != nil {
546 t.Errorf("Unexpected error %q", err)
547 }
548 assertBuildStatements(t, []BuildStatement{
549 {
Liz Kammer00629db2023-02-09 14:28:15 -0500550 Command: "",
551 OutputPaths: []string{"foo.runfiles/MANIFEST"},
552 Mnemonic: "SymlinkTree",
553 InputPaths: []string{"foo.manifest"},
554 SymlinkPaths: []string{},
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700555 },
556 }, actual)
557}
558
Usta Shresthaef922252022-06-02 14:23:02 -0400559func TestBazelOutRemovalFromInputDepsets(t *testing.T) {
560 const inputString = `{
Jason Wu118fd2b2022-10-27 18:41:15 +0000561 "artifacts": [
562 { "id": 1, "path_fragment_id": 10 },
563 { "id": 2, "path_fragment_id": 20 },
564 { "id": 3, "path_fragment_id": 30 },
565 { "id": 4, "path_fragment_id": 40 }],
566 "dep_set_of_files": [{
567 "id": 1111,
568 "direct_artifact_ids": [3 , 4]
569 }, {
570 "id": 2222,
571 "direct_artifact_ids": [3]
572 }],
573 "actions": [{
574 "target_id": 100,
575 "action_key": "x",
576 "input_dep_set_ids": [1111, 2222],
577 "mnemonic": "x",
578 "arguments": ["bogus", "command"],
579 "output_ids": [2],
580 "primary_output_id": 1
581 }],
582 "path_fragments": [
583 { "id": 10, "label": "input" },
584 { "id": 20, "label": "output" },
585 { "id": 30, "label": "dep1", "parent_id": 50 },
586 { "id": 40, "label": "dep2", "parent_id": 60 },
587 { "id": 50, "label": "bazel_tools", "parent_id": 60 },
588 { "id": 60, "label": ".."}
589 ]
Usta Shresthaef922252022-06-02 14:23:02 -0400590}`
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500591 /* depsets
592 1111 2222
593 / \ |
594 ../dep2 ../bazel_tools/dep1
595 */
Jason Wu118fd2b2022-10-27 18:41:15 +0000596 data, err := JsonToActionGraphContainer(inputString)
597 if err != nil {
598 t.Error(err)
599 return
600 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500601 actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500602 if len(actualDepsets) != 1 {
Usta Shresthaef922252022-06-02 14:23:02 -0400603 t.Errorf("expected 1 depset but found %#v", actualDepsets)
604 return
605 }
606 dep2Found := false
607 for _, dep := range flattenDepsets([]string{actualDepsets[0].ContentHash}, actualDepsets) {
608 if dep == "../bazel_tools/dep1" {
609 t.Errorf("dependency %s expected to be removed but still exists", dep)
610 } else if dep == "../dep2" {
611 dep2Found = true
612 }
613 }
614 if !dep2Found {
615 t.Errorf("dependency ../dep2 expected but not found")
616 }
617
618 expectedBuildStatement := BuildStatement{
Liz Kammer00629db2023-02-09 14:28:15 -0500619 Command: "bogus command",
620 OutputPaths: []string{"output"},
621 Mnemonic: "x",
622 SymlinkPaths: []string{},
Usta Shresthaef922252022-06-02 14:23:02 -0400623 }
624 buildStatementFound := false
625 for _, actualBuildStatement := range actualBuildStatements {
626 if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
627 buildStatementFound = true
628 break
629 }
630 }
631 if !buildStatementFound {
632 t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
633 return
634 }
635}
636
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500637func TestBazelOutRemovalFromTransitiveInputDepsets(t *testing.T) {
638 const inputString = `{
639 "artifacts": [
640 { "id": 1, "path_fragment_id": 10 },
641 { "id": 2, "path_fragment_id": 20 },
642 { "id": 3, "path_fragment_id": 30 }],
643 "dep_set_of_files": [{
644 "id": 1111,
645 "transitive_dep_set_ids": [2222]
646 }, {
647 "id": 2222,
648 "direct_artifact_ids": [3]
649 }, {
650 "id": 3333,
651 "direct_artifact_ids": [3]
652 }, {
653 "id": 4444,
654 "transitive_dep_set_ids": [3333]
655 }],
656 "actions": [{
657 "target_id": 100,
658 "action_key": "x",
659 "input_dep_set_ids": [1111, 4444],
660 "mnemonic": "x",
661 "arguments": ["bogus", "command"],
662 "output_ids": [2],
663 "primary_output_id": 1
664 }],
665 "path_fragments": [
666 { "id": 10, "label": "input" },
667 { "id": 20, "label": "output" },
668 { "id": 30, "label": "dep", "parent_id": 50 },
669 { "id": 50, "label": "bazel_tools", "parent_id": 60 },
670 { "id": 60, "label": ".."}
671 ]
672}`
673 /* depsets
674 1111 4444
675 || ||
676 2222 3333
677 | |
678 ../bazel_tools/dep
679 Note: in dep_set_of_files:
680 1111 appears BEFORE its dependency,2222 while
681 4444 appears AFTER its dependency 3333
682 and this test shows that that order doesn't affect empty depset pruning
683 */
684 data, err := JsonToActionGraphContainer(inputString)
685 if err != nil {
686 t.Error(err)
687 return
688 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500689 actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500690 if len(actualDepsets) != 0 {
691 t.Errorf("expected 0 depsets but found %#v", actualDepsets)
692 return
693 }
694
695 expectedBuildStatement := BuildStatement{
696 Command: "bogus command",
697 OutputPaths: []string{"output"},
698 Mnemonic: "x",
699 }
700 buildStatementFound := false
701 for _, actualBuildStatement := range actualBuildStatements {
702 if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
703 buildStatementFound = true
704 break
705 }
706 }
707 if !buildStatementFound {
708 t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
709 return
710 }
711}
712
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400713func TestMiddlemenAction(t *testing.T) {
714 const inputString = `
715{
Jason Wu118fd2b2022-10-27 18:41:15 +0000716 "artifacts": [
717 { "id": 1, "path_fragment_id": 1 },
718 { "id": 2, "path_fragment_id": 2 },
719 { "id": 3, "path_fragment_id": 3 },
720 { "id": 4, "path_fragment_id": 4 },
721 { "id": 5, "path_fragment_id": 5 },
722 { "id": 6, "path_fragment_id": 6 }],
723 "path_fragments": [
724 { "id": 1, "label": "middleinput_one" },
725 { "id": 2, "label": "middleinput_two" },
726 { "id": 3, "label": "middleman_artifact" },
727 { "id": 4, "label": "maininput_one" },
728 { "id": 5, "label": "maininput_two" },
729 { "id": 6, "label": "output" }],
730 "dep_set_of_files": [
731 { "id": 1, "direct_artifact_ids": [1, 2] },
732 { "id": 2, "direct_artifact_ids": [3, 4, 5] }],
733 "actions": [{
734 "target_id": 1,
735 "action_key": "x",
736 "mnemonic": "Middleman",
737 "arguments": ["touch", "foo"],
738 "input_dep_set_ids": [1],
739 "output_ids": [3],
740 "primary_output_id": 3
741 }, {
742 "target_id": 2,
743 "action_key": "y",
744 "mnemonic": "Main action",
745 "arguments": ["touch", "foo"],
746 "input_dep_set_ids": [2],
747 "output_ids": [6],
748 "primary_output_id": 6
749 }]
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400750}`
Jason Wu118fd2b2022-10-27 18:41:15 +0000751 data, err := JsonToActionGraphContainer(inputString)
752 if err != nil {
753 t.Error(err)
754 return
755 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500756 actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400757 if err != nil {
758 t.Errorf("Unexpected error %q", err)
759 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400760 if expected := 1; len(actualBuildStatements) != expected {
761 t.Fatalf("Expected %d build statements, got %d", expected, len(actualBuildStatements))
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400762 }
763
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400764 expectedDepsetFiles := [][]string{
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400765 {"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"},
766 {"middleinput_one", "middleinput_two"},
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400767 }
768 assertFlattenedDepsets(t, actualDepsets, expectedDepsetFiles)
769
Chris Parsons1a7aca02022-04-25 22:35:15 -0400770 bs := actualBuildStatements[0]
771 if len(bs.InputPaths) > 0 {
772 t.Errorf("Expected main action raw inputs to be empty, but got %q", bs.InputPaths)
773 }
774
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400775 expectedOutputs := []string{"output"}
776 if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) {
777 t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths)
778 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400779
Chris Parsons1a7aca02022-04-25 22:35:15 -0400780 expectedFlattenedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400781 actualFlattenedInputs := flattenDepsets(bs.InputDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400782
783 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
784 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
785 }
786}
787
788// Returns the contents of given depsets in concatenated post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400789func flattenDepsets(depsetHashesToFlatten []string, allDepsets []AqueryDepset) []string {
790 depsetsByHash := map[string]AqueryDepset{}
Chris Parsons1a7aca02022-04-25 22:35:15 -0400791 for _, depset := range allDepsets {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400792 depsetsByHash[depset.ContentHash] = depset
Chris Parsons1a7aca02022-04-25 22:35:15 -0400793 }
Usta Shrestha16ac1352022-06-22 11:01:55 -0400794 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400795 for _, depsetId := range depsetHashesToFlatten {
796 result = append(result, flattenDepset(depsetId, depsetsByHash)...)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400797 }
798 return result
799}
800
801// Returns the contents of a given depset in post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400802func flattenDepset(depsetHashToFlatten string, allDepsets map[string]AqueryDepset) []string {
803 depset := allDepsets[depsetHashToFlatten]
Usta Shrestha16ac1352022-06-22 11:01:55 -0400804 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400805 for _, depsetId := range depset.TransitiveDepSetHashes {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400806 result = append(result, flattenDepset(depsetId, allDepsets)...)
807 }
808 result = append(result, depset.DirectArtifacts...)
809 return result
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400810}
811
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400812func assertFlattenedDepsets(t *testing.T, actualDepsets []AqueryDepset, expectedDepsetFiles [][]string) {
813 t.Helper()
814 if len(actualDepsets) != len(expectedDepsetFiles) {
Sasha Smundakf10c3ac2022-06-08 11:46:31 -0700815 t.Errorf("Expected %d depsets, but got %d depsets", len(expectedDepsetFiles), len(actualDepsets))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400816 }
817 for i, actualDepset := range actualDepsets {
818 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
819 if !reflect.DeepEqual(actualFlattenedInputs, expectedDepsetFiles[i]) {
820 t.Errorf("Expected depset files: %v, but got %v", expectedDepsetFiles[i], actualFlattenedInputs)
821 }
822 }
823}
824
Liz Kammerc49e6822021-06-08 15:04:11 -0400825func TestSimpleSymlink(t *testing.T) {
826 const inputString = `
827{
Jason Wu118fd2b2022-10-27 18:41:15 +0000828 "artifacts": [
829 { "id": 1, "path_fragment_id": 3 },
830 { "id": 2, "path_fragment_id": 5 }],
831 "actions": [{
832 "target_id": 1,
833 "action_key": "x",
834 "mnemonic": "Symlink",
835 "input_dep_set_ids": [1],
836 "output_ids": [2],
837 "primary_output_id": 2
838 }],
839 "dep_set_of_files": [
840 { "id": 1, "direct_artifact_ids": [1] }],
841 "path_fragments": [
842 { "id": 1, "label": "one" },
843 { "id": 2, "label": "file_subdir", "parent_id": 1 },
844 { "id": 3, "label": "file", "parent_id": 2 },
845 { "id": 4, "label": "symlink_subdir", "parent_id": 1 },
846 { "id": 5, "label": "symlink", "parent_id": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400847}`
Jason Wu118fd2b2022-10-27 18:41:15 +0000848 data, err := JsonToActionGraphContainer(inputString)
849 if err != nil {
850 t.Error(err)
851 return
852 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500853 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerc49e6822021-06-08 15:04:11 -0400854
855 if err != nil {
856 t.Errorf("Unexpected error %q", err)
857 }
858
859 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400860 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400861 Command: "mkdir -p one/symlink_subdir && " +
862 "rm -f one/symlink_subdir/symlink && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400863 "ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
Liz Kammerc49e6822021-06-08 15:04:11 -0400864 InputPaths: []string{"one/file_subdir/file"},
865 OutputPaths: []string{"one/symlink_subdir/symlink"},
866 SymlinkPaths: []string{"one/symlink_subdir/symlink"},
867 Mnemonic: "Symlink",
868 },
869 }
870 assertBuildStatements(t, actual, expectedBuildStatements)
871}
872
873func TestSymlinkQuotesPaths(t *testing.T) {
874 const inputString = `
875{
Jason Wu118fd2b2022-10-27 18:41:15 +0000876 "artifacts": [
877 { "id": 1, "path_fragment_id": 3 },
878 { "id": 2, "path_fragment_id": 5 }],
879 "actions": [{
880 "target_id": 1,
881 "action_key": "x",
882 "mnemonic": "SolibSymlink",
883 "input_dep_set_ids": [1],
884 "output_ids": [2],
885 "primary_output_id": 2
886 }],
887 "dep_set_of_files": [
888 { "id": 1, "direct_artifact_ids": [1] }],
889 "path_fragments": [
890 { "id": 1, "label": "one" },
891 { "id": 2, "label": "file subdir", "parent_id": 1 },
892 { "id": 3, "label": "file", "parent_id": 2 },
893 { "id": 4, "label": "symlink subdir", "parent_id": 1 },
894 { "id": 5, "label": "symlink", "parent_id": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400895}`
896
Jason Wu118fd2b2022-10-27 18:41:15 +0000897 data, err := JsonToActionGraphContainer(inputString)
898 if err != nil {
899 t.Error(err)
900 return
901 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500902 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerc49e6822021-06-08 15:04:11 -0400903 if err != nil {
904 t.Errorf("Unexpected error %q", err)
905 }
906
907 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400908 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400909 Command: "mkdir -p 'one/symlink subdir' && " +
910 "rm -f 'one/symlink subdir/symlink' && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400911 "ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
Liz Kammerc49e6822021-06-08 15:04:11 -0400912 InputPaths: []string{"one/file subdir/file"},
913 OutputPaths: []string{"one/symlink subdir/symlink"},
914 SymlinkPaths: []string{"one/symlink subdir/symlink"},
915 Mnemonic: "SolibSymlink",
916 },
917 }
Liz Kammerc7737782021-11-04 10:56:13 -0400918 assertBuildStatements(t, expectedBuildStatements, actual)
Liz Kammerc49e6822021-06-08 15:04:11 -0400919}
920
921func TestSymlinkMultipleInputs(t *testing.T) {
922 const inputString = `
923{
Jason Wu118fd2b2022-10-27 18:41:15 +0000924 "artifacts": [
925 { "id": 1, "path_fragment_id": 1 },
926 { "id": 2, "path_fragment_id": 2 },
927 { "id": 3, "path_fragment_id": 3 }],
928 "actions": [{
929 "target_id": 1,
930 "action_key": "x",
931 "mnemonic": "Symlink",
932 "input_dep_set_ids": [1],
933 "output_ids": [3],
934 "primary_output_id": 3
935 }],
936 "dep_set_of_files": [{ "id": 1, "direct_artifact_ids": [1,2] }],
937 "path_fragments": [
938 { "id": 1, "label": "file" },
939 { "id": 2, "label": "other_file" },
940 { "id": 3, "label": "symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400941}`
942
Jason Wu118fd2b2022-10-27 18:41:15 +0000943 data, err := JsonToActionGraphContainer(inputString)
944 if err != nil {
945 t.Error(err)
946 return
947 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500948 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerc49e6822021-06-08 15:04:11 -0400949 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
950}
951
952func TestSymlinkMultipleOutputs(t *testing.T) {
953 const inputString = `
954{
Jason Wu118fd2b2022-10-27 18:41:15 +0000955 "artifacts": [
956 { "id": 1, "path_fragment_id": 1 },
957 { "id": 3, "path_fragment_id": 3 }],
958 "actions": [{
959 "target_id": 1,
960 "action_key": "x",
961 "mnemonic": "Symlink",
962 "input_dep_set_ids": [1],
963 "output_ids": [2,3],
964 "primary_output_id": 2
965 }],
966 "dep_set_of_files": [
967 { "id": 1, "direct_artifact_ids": [1] }],
968 "path_fragments": [
969 { "id": 1, "label": "file" },
970 { "id": 2, "label": "symlink" },
971 { "id": 3, "label": "other_symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400972}`
973
Jason Wu118fd2b2022-10-27 18:41:15 +0000974 data, err := JsonToActionGraphContainer(inputString)
975 if err != nil {
976 t.Error(err)
977 return
978 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500979 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Jason Wu118fd2b2022-10-27 18:41:15 +0000980 assertError(t, err, "undefined outputId 2")
Liz Kammerc49e6822021-06-08 15:04:11 -0400981}
982
Wei Li455ba832021-11-04 22:58:12 +0000983func TestTemplateExpandActionSubstitutions(t *testing.T) {
984 const inputString = `
985{
Jason Wu118fd2b2022-10-27 18:41:15 +0000986 "artifacts": [{
987 "id": 1,
988 "path_fragment_id": 1
989 }],
990 "actions": [{
991 "target_id": 1,
992 "action_key": "x",
993 "mnemonic": "TemplateExpand",
994 "configuration_id": 1,
995 "output_ids": [1],
996 "primary_output_id": 1,
997 "execution_platform": "//build/bazel/platforms:linux_x86_64",
998 "template_content": "Test template substitutions: %token1%, %python_binary%",
999 "substitutions": [
1000 { "key": "%token1%", "value": "abcd" },
1001 { "key": "%python_binary%", "value": "python3" }]
1002 }],
1003 "path_fragments": [
1004 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +00001005}`
1006
Jason Wu118fd2b2022-10-27 18:41:15 +00001007 data, err := JsonToActionGraphContainer(inputString)
1008 if err != nil {
1009 t.Error(err)
1010 return
1011 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001012 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Wei Li455ba832021-11-04 22:58:12 +00001013 if err != nil {
1014 t.Errorf("Unexpected error %q", err)
1015 }
1016
1017 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -04001018 {
Wei Li455ba832021-11-04 22:58:12 +00001019 Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
1020 "chmod a+x template_file'",
Liz Kammer00629db2023-02-09 14:28:15 -05001021 OutputPaths: []string{"template_file"},
1022 Mnemonic: "TemplateExpand",
1023 SymlinkPaths: []string{},
Wei Li455ba832021-11-04 22:58:12 +00001024 },
1025 }
1026 assertBuildStatements(t, expectedBuildStatements, actual)
1027}
1028
1029func TestTemplateExpandActionNoOutput(t *testing.T) {
1030 const inputString = `
1031{
Jason Wu118fd2b2022-10-27 18:41:15 +00001032 "artifacts": [
1033 { "id": 1, "path_fragment_id": 1 }],
1034 "actions": [{
1035 "target_id": 1,
1036 "action_key": "x",
1037 "mnemonic": "TemplateExpand",
1038 "configuration_id": 1,
1039 "primary_output_id": 1,
1040 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1041 "templateContent": "Test template substitutions: %token1%, %python_binary%",
1042 "substitutions": [
1043 { "key": "%token1%", "value": "abcd" },
1044 { "key": "%python_binary%", "value": "python3" }]
1045 }],
1046 "path_fragments": [
1047 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +00001048}`
1049
Jason Wu118fd2b2022-10-27 18:41:15 +00001050 data, err := JsonToActionGraphContainer(inputString)
1051 if err != nil {
1052 t.Error(err)
1053 return
1054 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001055 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Wei Li455ba832021-11-04 22:58:12 +00001056 assertError(t, err, `Expect 1 output to template expand action, got: output []`)
1057}
1058
Sasha Smundak1da064c2022-06-08 16:36:16 -07001059func TestFileWrite(t *testing.T) {
1060 const inputString = `
1061{
Jason Wu118fd2b2022-10-27 18:41:15 +00001062 "artifacts": [
1063 { "id": 1, "path_fragment_id": 1 }],
1064 "actions": [{
1065 "target_id": 1,
1066 "action_key": "x",
1067 "mnemonic": "FileWrite",
1068 "configuration_id": 1,
1069 "output_ids": [1],
1070 "primary_output_id": 1,
1071 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1072 "file_contents": "file data\n"
1073 }],
1074 "path_fragments": [
1075 { "id": 1, "label": "foo.manifest" }]
Sasha Smundak1da064c2022-06-08 16:36:16 -07001076}
1077`
Jason Wu118fd2b2022-10-27 18:41:15 +00001078 data, err := JsonToActionGraphContainer(inputString)
1079 if err != nil {
1080 t.Error(err)
1081 return
1082 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001083 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Sasha Smundak1da064c2022-06-08 16:36:16 -07001084 if err != nil {
1085 t.Errorf("Unexpected error %q", err)
1086 }
1087 assertBuildStatements(t, []BuildStatement{
1088 {
1089 OutputPaths: []string{"foo.manifest"},
1090 Mnemonic: "FileWrite",
1091 FileContents: "file data\n",
Liz Kammer00629db2023-02-09 14:28:15 -05001092 SymlinkPaths: []string{},
Sasha Smundak1da064c2022-06-08 16:36:16 -07001093 },
1094 }, actual)
1095}
1096
1097func TestSourceSymlinkManifest(t *testing.T) {
1098 const inputString = `
1099{
Jason Wu118fd2b2022-10-27 18:41:15 +00001100 "artifacts": [
1101 { "id": 1, "path_fragment_id": 1 }],
1102 "actions": [{
1103 "target_id": 1,
1104 "action_key": "x",
1105 "mnemonic": "SourceSymlinkManifest",
1106 "configuration_id": 1,
1107 "output_ids": [1],
1108 "primary_output_id": 1,
1109 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1110 "file_contents": "symlink target\n"
1111 }],
1112 "path_fragments": [
1113 { "id": 1, "label": "foo.manifest" }]
Sasha Smundak1da064c2022-06-08 16:36:16 -07001114}
1115`
Jason Wu118fd2b2022-10-27 18:41:15 +00001116 data, err := JsonToActionGraphContainer(inputString)
1117 if err != nil {
1118 t.Error(err)
1119 return
1120 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001121 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Sasha Smundak1da064c2022-06-08 16:36:16 -07001122 if err != nil {
1123 t.Errorf("Unexpected error %q", err)
1124 }
1125 assertBuildStatements(t, []BuildStatement{
1126 {
Liz Kammer00629db2023-02-09 14:28:15 -05001127 OutputPaths: []string{"foo.manifest"},
1128 Mnemonic: "SourceSymlinkManifest",
1129 SymlinkPaths: []string{},
Sasha Smundak1da064c2022-06-08 16:36:16 -07001130 },
1131 }, actual)
1132}
1133
Chris Parsons4f069892021-01-15 12:22:41 -05001134func assertError(t *testing.T, err error, expected string) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001135 t.Helper()
Chris Parsons943f2432021-01-19 11:36:50 -05001136 if err == nil {
1137 t.Errorf("expected error '%s', but got no error", expected)
1138 } else if err.Error() != expected {
Liz Kammerc49e6822021-06-08 15:04:11 -04001139 t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
Chris Parsons4f069892021-01-15 12:22:41 -05001140 }
1141}
1142
1143// Asserts that the given actual build statements match the given expected build statements.
1144// Build statement equivalence is determined using buildStatementEquals.
1145func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001146 t.Helper()
Chris Parsons4f069892021-01-15 12:22:41 -05001147 if len(expected) != len(actual) {
Liz Kammerc3192992021-11-16 17:01:11 -05001148 t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
Chris Parsons4f069892021-01-15 12:22:41 -05001149 len(expected), len(actual), expected, actual)
1150 return
1151 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001152 type compareFn = func(i int, j int) bool
1153 byCommand := func(slice []BuildStatement) compareFn {
1154 return func(i int, j int) bool {
1155 return slice[i].Command < slice[j].Command
Chris Parsons4f069892021-01-15 12:22:41 -05001156 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001157 }
1158 sort.SliceStable(expected, byCommand(expected))
1159 sort.SliceStable(actual, byCommand(actual))
1160 for i, actualStatement := range actual {
1161 expectedStatement := expected[i]
1162 if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
1163 t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
Usta Shresthaef922252022-06-02 14:23:02 -04001164 differingField, actualStatement, expectedStatement)
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001165 return
1166 }
Chris Parsons4f069892021-01-15 12:22:41 -05001167 }
1168}
1169
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001170func buildStatementEquals(first BuildStatement, second BuildStatement) string {
Chris Parsons4f069892021-01-15 12:22:41 -05001171 if first.Mnemonic != second.Mnemonic {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001172 return "Mnemonic"
Chris Parsons4f069892021-01-15 12:22:41 -05001173 }
1174 if first.Command != second.Command {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001175 return "Command"
Chris Parsons4f069892021-01-15 12:22:41 -05001176 }
1177 // Ordering is significant for environment variables.
1178 if !reflect.DeepEqual(first.Env, second.Env) {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001179 return "Env"
Chris Parsons4f069892021-01-15 12:22:41 -05001180 }
1181 // Ordering is irrelevant for input and output paths, so compare sets.
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001182 if !reflect.DeepEqual(sortedStrings(first.InputPaths), sortedStrings(second.InputPaths)) {
1183 return "InputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001184 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001185 if !reflect.DeepEqual(sortedStrings(first.OutputPaths), sortedStrings(second.OutputPaths)) {
1186 return "OutputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001187 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001188 if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
1189 return "SymlinkPaths"
Liz Kammerc49e6822021-06-08 15:04:11 -04001190 }
1191 if first.Depfile != second.Depfile {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001192 return "Depfile"
Liz Kammerc49e6822021-06-08 15:04:11 -04001193 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001194 return ""
Chris Parsons4f069892021-01-15 12:22:41 -05001195}
1196
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001197func sortedStrings(stringSlice []string) []string {
1198 sorted := make([]string, len(stringSlice))
1199 copy(sorted, stringSlice)
1200 sort.Strings(sorted)
1201 return sorted
Chris Parsons4f069892021-01-15 12:22:41 -05001202}
Jason Wu118fd2b2022-10-27 18:41:15 +00001203
1204// Transform the json format to ActionGraphContainer
1205func JsonToActionGraphContainer(inputString string) ([]byte, error) {
1206 var aqueryProtoResult analysis_v2_proto.ActionGraphContainer
1207 err := json.Unmarshal([]byte(inputString), &aqueryProtoResult)
1208 if err != nil {
1209 return []byte(""), err
1210 }
1211 data, _ := proto.Marshal(&aqueryProtoResult)
1212 return data, err
1213}