blob: 19a584f23352515422a832446f95eeb40f1e2ea1 [file] [log] [blame]
Chris Parsons4f069892021-01-15 12:22:41 -05001// Copyright 2020 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package bazel
16
17import (
Jason Wu118fd2b2022-10-27 18:41:15 +000018 "encoding/json"
Chris Parsons4f069892021-01-15 12:22:41 -050019 "fmt"
20 "reflect"
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -040021 "sort"
Chris Parsons4f069892021-01-15 12:22:41 -050022 "testing"
Jason Wu118fd2b2022-10-27 18:41:15 +000023
Jason Wu118fd2b2022-10-27 18:41:15 +000024 analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
Liz Kammer690fbac2023-02-10 11:11:17 -050025
26 "github.com/google/blueprint/metrics"
27 "google.golang.org/protobuf/proto"
Chris Parsons4f069892021-01-15 12:22:41 -050028)
29
30func TestAqueryMultiArchGenrule(t *testing.T) {
31 // This input string is retrieved from a real build of bionic-related genrules.
32 const inputString = `
33{
Jason Wu118fd2b2022-10-27 18:41:15 +000034 "Artifacts": [
35 { "Id": 1, "path_fragment_id": 1 },
36 { "Id": 2, "path_fragment_id": 6 },
37 { "Id": 3, "path_fragment_id": 8 },
38 { "Id": 4, "path_fragment_id": 12 },
39 { "Id": 5, "path_fragment_id": 19 },
40 { "Id": 6, "path_fragment_id": 20 },
41 { "Id": 7, "path_fragment_id": 21 }],
42 "Actions": [{
43 "target_id": 1,
44 "action_key": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
45 "Mnemonic": "Genrule",
46 "configuration_id": 1,
47 "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm.S"],
48 "environment_variables": [{
49 "Key": "PATH",
50 "Value": "/bin:/usr/bin:/usr/local/bin"
51 }],
52 "input_dep_set_ids": [1],
53 "output_ids": [4],
54 "primary_output_id": 4
55 }, {
56 "target_id": 2,
57 "action_key": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
58 "Mnemonic": "Genrule",
59 "configuration_id": 1,
60 "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86.S"],
61 "environment_variables": [{
62 "Key": "PATH",
63 "Value": "/bin:/usr/bin:/usr/local/bin"
64 }],
65 "input_dep_set_ids": [2],
66 "output_ids": [5],
67 "primary_output_id": 5
68 }, {
69 "target_id": 3,
70 "action_key": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
71 "Mnemonic": "Genrule",
72 "configuration_id": 1,
73 "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86_64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86_64.S"],
74 "environment_variables": [{
75 "Key": "PATH",
76 "Value": "/bin:/usr/bin:/usr/local/bin"
77 }],
78 "input_dep_set_ids": [3],
79 "output_ids": [6],
80 "primary_output_id": 6
81 }, {
82 "target_id": 4,
83 "action_key": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
84 "Mnemonic": "Genrule",
85 "configuration_id": 1,
86 "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm64.S"],
87 "environment_variables": [{
88 "Key": "PATH",
89 "Value": "/bin:/usr/bin:/usr/local/bin"
90 }],
91 "input_dep_set_ids": [4],
92 "output_ids": [7],
93 "primary_output_id": 7
94 }],
95 "Targets": [
96 { "Id": 1, "Label": "@sourceroot//bionic/libc:syscalls-arm", "rule_class_id": 1 },
97 { "Id": 2, "Label": "@sourceroot//bionic/libc:syscalls-x86", "rule_class_id": 1 },
98 { "Id": 3, "Label": "@sourceroot//bionic/libc:syscalls-x86_64", "rule_class_id": 1 },
99 { "Id": 4, "Label": "@sourceroot//bionic/libc:syscalls-arm64", "rule_class_id": 1 }],
100 "dep_set_of_files": [
101 { "Id": 1, "direct_artifact_ids": [1, 2, 3] },
102 { "Id": 2, "direct_artifact_ids": [1, 2, 3] },
103 { "Id": 3, "direct_artifact_ids": [1, 2, 3] },
104 { "Id": 4, "direct_artifact_ids": [1, 2, 3] }],
105 "Configuration": [{
106 "Id": 1,
107 "Mnemonic": "k8-fastbuild",
108 "platform_name": "k8",
109 "Checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
110 }],
111 "rule_classes": [{ "Id": 1, "Name": "genrule"}],
112 "path_fragments": [
113 { "Id": 5, "Label": ".." },
114 { "Id": 4, "Label": "sourceroot", "parent_id": 5 },
115 { "Id": 3, "Label": "bionic", "parent_id": 4 },
116 { "Id": 2, "Label": "libc", "parent_id": 3 },
117 { "Id": 1, "Label": "SYSCALLS.TXT", "parent_id": 2 },
118 { "Id": 7, "Label": "tools", "parent_id": 2 },
119 { "Id": 6, "Label": "gensyscalls.py", "parent_id": 7 },
120 { "Id": 11, "Label": "bazel_tools", "parent_id": 5 },
121 { "Id": 10, "Label": "tools", "parent_id": 11 },
122 { "Id": 9, "Label": "genrule", "parent_id": 10 },
123 { "Id": 8, "Label": "genrule-setup.sh", "parent_id": 9 },
124 { "Id": 18, "Label": "bazel-out" },
125 { "Id": 17, "Label": "sourceroot", "parent_id": 18 },
126 { "Id": 16, "Label": "k8-fastbuild", "parent_id": 17 },
127 { "Id": 15, "Label": "bin", "parent_id": 16 },
128 { "Id": 14, "Label": "bionic", "parent_id": 15 },
129 { "Id": 13, "Label": "libc", "parent_id": 14 },
130 { "Id": 12, "Label": "syscalls-arm.S", "parent_id": 13 },
131 { "Id": 19, "Label": "syscalls-x86.S", "parent_id": 13 },
132 { "Id": 20, "Label": "syscalls-x86_64.S", "parent_id": 13 },
133 { "Id": 21, "Label": "syscalls-arm64.S", "parent_id": 13 }]
134}
135`
136 data, err := JsonToActionGraphContainer(inputString)
137 if err != nil {
138 t.Error(err)
139 return
140 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500141 actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammera4655a92023-02-10 17:17:28 -0500142 var expectedBuildStatements []*BuildStatement
Chris Parsons4f069892021-01-15 12:22:41 -0500143 for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
144 expectedBuildStatements = append(expectedBuildStatements,
Liz Kammera4655a92023-02-10 17:17:28 -0500145 &BuildStatement{
Chris Parsons4f069892021-01-15 12:22:41 -0500146 Command: fmt.Sprintf(
147 "/bin/bash -c 'source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py %s ../sourceroot/bionic/libc/SYSCALLS.TXT > bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S'",
148 arch, arch),
149 OutputPaths: []string{
150 fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
151 },
Liz Kammer00629db2023-02-09 14:28:15 -0500152 Env: []*analysis_v2_proto.KeyValuePair{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400153 {Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
Chris Parsons4f069892021-01-15 12:22:41 -0500154 },
155 Mnemonic: "Genrule",
156 })
157 }
158 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400159
160 expectedFlattenedInputs := []string{
161 "../sourceroot/bionic/libc/SYSCALLS.TXT",
162 "../sourceroot/bionic/libc/tools/gensyscalls.py",
Chris Parsons1a7aca02022-04-25 22:35:15 -0400163 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400164 // In this example, each depset should have the same expected inputs.
165 for _, actualDepset := range actualDepsets {
166 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
167 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
168 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
169 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400170 }
Chris Parsons4f069892021-01-15 12:22:41 -0500171}
172
173func TestInvalidOutputId(t *testing.T) {
174 const inputString = `
175{
Jason Wu118fd2b2022-10-27 18:41:15 +0000176 "artifacts": [
177 { "id": 1, "path_fragment_id": 1 },
178 { "id": 2, "path_fragment_id": 2 }],
179 "actions": [{
180 "target_id": 1,
181 "action_key": "x",
182 "mnemonic": "x",
183 "arguments": ["touch", "foo"],
184 "input_dep_set_ids": [1],
185 "output_ids": [3],
186 "primary_output_id": 3
187 }],
188 "dep_set_of_files": [
189 { "id": 1, "direct_artifact_ids": [1, 2] }],
190 "path_fragments": [
191 { "id": 1, "label": "one" },
192 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500193}`
194
Jason Wu118fd2b2022-10-27 18:41:15 +0000195 data, err := JsonToActionGraphContainer(inputString)
196 if err != nil {
197 t.Error(err)
198 return
199 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500200 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Chris Parsons4f069892021-01-15 12:22:41 -0500201 assertError(t, err, "undefined outputId 3")
202}
203
Chris Parsons1a7aca02022-04-25 22:35:15 -0400204func TestInvalidInputDepsetIdFromAction(t *testing.T) {
Chris Parsons4f069892021-01-15 12:22:41 -0500205 const inputString = `
206{
Jason Wu118fd2b2022-10-27 18:41:15 +0000207 "artifacts": [
208 { "id": 1, "path_fragment_id": 1 },
209 { "id": 2, "path_fragment_id": 2 }],
210 "actions": [{
211 "target_id": 1,
212 "action_key": "x",
213 "mnemonic": "x",
214 "arguments": ["touch", "foo"],
215 "input_dep_set_ids": [2],
216 "output_ids": [1],
217 "primary_output_id": 1
218 }],
219 "dep_set_of_files": [
220 { "id": 1, "direct_artifact_ids": [1, 2] }],
221 "path_fragments": [
222 { "id": 1, "label": "one" },
223 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500224}`
225
Jason Wu118fd2b2022-10-27 18:41:15 +0000226 data, err := JsonToActionGraphContainer(inputString)
227 if err != nil {
228 t.Error(err)
229 return
230 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500231 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500232 assertError(t, err, "undefined (not even empty) input depsetId 2")
Chris Parsons4f069892021-01-15 12:22:41 -0500233}
234
Chris Parsons1a7aca02022-04-25 22:35:15 -0400235func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
236 const inputString = `
237{
Jason Wu118fd2b2022-10-27 18:41:15 +0000238 "artifacts": [
239 { "id": 1, "path_fragment_id": 1 },
240 { "id": 2, "path_fragment_id": 2 }],
241 "actions": [{
242 "target_id": 1,
243 "action_key": "x",
244 "mnemonic": "x",
245 "arguments": ["touch", "foo"],
246 "input_dep_set_ids": [1],
247 "output_ids": [1],
248 "primary_output_id": 1
249 }],
250 "dep_set_of_files": [
251 { "id": 1, "direct_artifact_ids": [1, 2], "transitive_dep_set_ids": [42] }],
252 "path_fragments": [
253 { "id": 1, "label": "one"},
254 { "id": 2, "label": "two" }]
Chris Parsons1a7aca02022-04-25 22:35:15 -0400255}`
256
Jason Wu118fd2b2022-10-27 18:41:15 +0000257 data, err := JsonToActionGraphContainer(inputString)
258 if err != nil {
259 t.Error(err)
260 return
261 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500262 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Chris Parsons1a7aca02022-04-25 22:35:15 -0400263 assertError(t, err, "undefined input depsetId 42 (referenced by depsetId 1)")
264}
265
Chris Parsons4f069892021-01-15 12:22:41 -0500266func TestInvalidInputArtifactId(t *testing.T) {
267 const inputString = `
268{
Jason Wu118fd2b2022-10-27 18:41:15 +0000269 "artifacts": [
270 { "id": 1, "path_fragment_id": 1 },
271 { "id": 2, "path_fragment_id": 2 }],
272 "actions": [{
273 "target_id": 1,
274 "action_key": "x",
275 "mnemonic": "x",
276 "arguments": ["touch", "foo"],
277 "input_dep_set_ids": [1],
278 "output_ids": [1],
279 "primary_output_id": 1
280 }],
281 "dep_set_of_files": [
282 { "id": 1, "direct_artifact_ids": [1, 3] }],
283 "path_fragments": [
284 { "id": 1, "label": "one" },
285 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500286}`
287
Jason Wu118fd2b2022-10-27 18:41:15 +0000288 data, err := JsonToActionGraphContainer(inputString)
289 if err != nil {
290 t.Error(err)
291 return
292 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500293 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Chris Parsons4f069892021-01-15 12:22:41 -0500294 assertError(t, err, "undefined input artifactId 3")
295}
296
297func TestInvalidPathFragmentId(t *testing.T) {
298 const inputString = `
299{
Jason Wu118fd2b2022-10-27 18:41:15 +0000300 "artifacts": [
301 { "id": 1, "path_fragment_id": 1 },
302 { "id": 2, "path_fragment_id": 2 }],
303 "actions": [{
304 "target_id": 1,
305 "action_key": "x",
306 "mnemonic": "x",
307 "arguments": ["touch", "foo"],
308 "input_dep_set_ids": [1],
309 "output_ids": [1],
310 "primary_output_id": 1
311 }],
312 "dep_set_of_files": [
313 { "id": 1, "direct_artifact_ids": [1, 2] }],
314 "path_fragments": [
315 { "id": 1, "label": "one" },
316 { "id": 2, "label": "two", "parent_id": 3 }]
Chris Parsons4f069892021-01-15 12:22:41 -0500317}`
318
Jason Wu118fd2b2022-10-27 18:41:15 +0000319 data, err := JsonToActionGraphContainer(inputString)
320 if err != nil {
321 t.Error(err)
322 return
323 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500324 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Chris Parsons4f069892021-01-15 12:22:41 -0500325 assertError(t, err, "undefined path fragment id 3")
326}
327
Liz Kammerde116852021-03-25 16:42:37 -0400328func TestDepfiles(t *testing.T) {
329 const inputString = `
330{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700331 "artifacts": [
Jason Wu118fd2b2022-10-27 18:41:15 +0000332 { "id": 1, "path_fragment_id": 1 },
333 { "id": 2, "path_fragment_id": 2 },
334 { "id": 3, "path_fragment_id": 3 }],
Liz Kammerde116852021-03-25 16:42:37 -0400335 "actions": [{
Jason Wu118fd2b2022-10-27 18:41:15 +0000336 "target_Id": 1,
337 "action_Key": "x",
Liz Kammerde116852021-03-25 16:42:37 -0400338 "mnemonic": "x",
339 "arguments": ["touch", "foo"],
Jason Wu118fd2b2022-10-27 18:41:15 +0000340 "input_dep_set_ids": [1],
341 "output_ids": [2, 3],
342 "primary_output_id": 2
Liz Kammerde116852021-03-25 16:42:37 -0400343 }],
Jason Wu118fd2b2022-10-27 18:41:15 +0000344 "dep_set_of_files": [
345 { "id": 1, "direct_Artifact_Ids": [1, 2, 3] }],
346 "path_fragments": [
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700347 { "id": 1, "label": "one" },
348 { "id": 2, "label": "two" },
349 { "id": 3, "label": "two.d" }]
Liz Kammerde116852021-03-25 16:42:37 -0400350}`
351
Jason Wu118fd2b2022-10-27 18:41:15 +0000352 data, err := JsonToActionGraphContainer(inputString)
353 if err != nil {
354 t.Error(err)
355 return
356 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500357 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerde116852021-03-25 16:42:37 -0400358 if err != nil {
359 t.Errorf("Unexpected error %q", err)
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
Liz Kammera4655a92023-02-10 17:17:28 -0500490 expectedBuildStatements := []*BuildStatement{
491 &BuildStatement{
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 }
Liz Kammera4655a92023-02-10 17:17:28 -0500548 assertBuildStatements(t, []*BuildStatement{
549 &BuildStatement{
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
Liz Kammera4655a92023-02-10 17:17:28 -0500618 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
Liz Kammera4655a92023-02-10 17:17:28 -0500695 expectedBuildStatement := &BuildStatement{
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500696 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 }
Liz Kammera4655a92023-02-10 17:17:28 -0500760 if expected := 2; len(actualBuildStatements) != expected {
761 t.Fatalf("Expected %d build statements, got %d %#v", expected, len(actualBuildStatements), 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 }
Liz Kammera4655a92023-02-10 17:17:28 -0500786
787 bs = actualBuildStatements[1]
788 if bs != nil {
789 t.Errorf("Expected nil action for skipped")
790 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400791}
792
793// Returns the contents of given depsets in concatenated post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400794func flattenDepsets(depsetHashesToFlatten []string, allDepsets []AqueryDepset) []string {
795 depsetsByHash := map[string]AqueryDepset{}
Chris Parsons1a7aca02022-04-25 22:35:15 -0400796 for _, depset := range allDepsets {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400797 depsetsByHash[depset.ContentHash] = depset
Chris Parsons1a7aca02022-04-25 22:35:15 -0400798 }
Usta Shrestha16ac1352022-06-22 11:01:55 -0400799 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400800 for _, depsetId := range depsetHashesToFlatten {
801 result = append(result, flattenDepset(depsetId, depsetsByHash)...)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400802 }
803 return result
804}
805
806// Returns the contents of a given depset in post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400807func flattenDepset(depsetHashToFlatten string, allDepsets map[string]AqueryDepset) []string {
808 depset := allDepsets[depsetHashToFlatten]
Usta Shrestha16ac1352022-06-22 11:01:55 -0400809 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400810 for _, depsetId := range depset.TransitiveDepSetHashes {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400811 result = append(result, flattenDepset(depsetId, allDepsets)...)
812 }
813 result = append(result, depset.DirectArtifacts...)
814 return result
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400815}
816
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400817func assertFlattenedDepsets(t *testing.T, actualDepsets []AqueryDepset, expectedDepsetFiles [][]string) {
818 t.Helper()
819 if len(actualDepsets) != len(expectedDepsetFiles) {
Sasha Smundakf10c3ac2022-06-08 11:46:31 -0700820 t.Errorf("Expected %d depsets, but got %d depsets", len(expectedDepsetFiles), len(actualDepsets))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400821 }
822 for i, actualDepset := range actualDepsets {
823 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
824 if !reflect.DeepEqual(actualFlattenedInputs, expectedDepsetFiles[i]) {
825 t.Errorf("Expected depset files: %v, but got %v", expectedDepsetFiles[i], actualFlattenedInputs)
826 }
827 }
828}
829
Liz Kammerc49e6822021-06-08 15:04:11 -0400830func TestSimpleSymlink(t *testing.T) {
831 const inputString = `
832{
Jason Wu118fd2b2022-10-27 18:41:15 +0000833 "artifacts": [
834 { "id": 1, "path_fragment_id": 3 },
835 { "id": 2, "path_fragment_id": 5 }],
836 "actions": [{
837 "target_id": 1,
838 "action_key": "x",
839 "mnemonic": "Symlink",
840 "input_dep_set_ids": [1],
841 "output_ids": [2],
842 "primary_output_id": 2
843 }],
844 "dep_set_of_files": [
845 { "id": 1, "direct_artifact_ids": [1] }],
846 "path_fragments": [
847 { "id": 1, "label": "one" },
848 { "id": 2, "label": "file_subdir", "parent_id": 1 },
849 { "id": 3, "label": "file", "parent_id": 2 },
850 { "id": 4, "label": "symlink_subdir", "parent_id": 1 },
851 { "id": 5, "label": "symlink", "parent_id": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400852}`
Jason Wu118fd2b2022-10-27 18:41:15 +0000853 data, err := JsonToActionGraphContainer(inputString)
854 if err != nil {
855 t.Error(err)
856 return
857 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500858 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerc49e6822021-06-08 15:04:11 -0400859
860 if err != nil {
861 t.Errorf("Unexpected error %q", err)
862 }
863
Liz Kammera4655a92023-02-10 17:17:28 -0500864 expectedBuildStatements := []*BuildStatement{
865 &BuildStatement{
Liz Kammerc49e6822021-06-08 15:04:11 -0400866 Command: "mkdir -p one/symlink_subdir && " +
867 "rm -f one/symlink_subdir/symlink && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400868 "ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
Liz Kammerc49e6822021-06-08 15:04:11 -0400869 InputPaths: []string{"one/file_subdir/file"},
870 OutputPaths: []string{"one/symlink_subdir/symlink"},
871 SymlinkPaths: []string{"one/symlink_subdir/symlink"},
872 Mnemonic: "Symlink",
873 },
874 }
875 assertBuildStatements(t, actual, expectedBuildStatements)
876}
877
878func TestSymlinkQuotesPaths(t *testing.T) {
879 const inputString = `
880{
Jason Wu118fd2b2022-10-27 18:41:15 +0000881 "artifacts": [
882 { "id": 1, "path_fragment_id": 3 },
883 { "id": 2, "path_fragment_id": 5 }],
884 "actions": [{
885 "target_id": 1,
886 "action_key": "x",
887 "mnemonic": "SolibSymlink",
888 "input_dep_set_ids": [1],
889 "output_ids": [2],
890 "primary_output_id": 2
891 }],
892 "dep_set_of_files": [
893 { "id": 1, "direct_artifact_ids": [1] }],
894 "path_fragments": [
895 { "id": 1, "label": "one" },
896 { "id": 2, "label": "file subdir", "parent_id": 1 },
897 { "id": 3, "label": "file", "parent_id": 2 },
898 { "id": 4, "label": "symlink subdir", "parent_id": 1 },
899 { "id": 5, "label": "symlink", "parent_id": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400900}`
901
Jason Wu118fd2b2022-10-27 18:41:15 +0000902 data, err := JsonToActionGraphContainer(inputString)
903 if err != nil {
904 t.Error(err)
905 return
906 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500907 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerc49e6822021-06-08 15:04:11 -0400908 if err != nil {
909 t.Errorf("Unexpected error %q", err)
910 }
911
Liz Kammera4655a92023-02-10 17:17:28 -0500912 expectedBuildStatements := []*BuildStatement{
913 &BuildStatement{
Liz Kammerc49e6822021-06-08 15:04:11 -0400914 Command: "mkdir -p 'one/symlink subdir' && " +
915 "rm -f 'one/symlink subdir/symlink' && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400916 "ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
Liz Kammerc49e6822021-06-08 15:04:11 -0400917 InputPaths: []string{"one/file subdir/file"},
918 OutputPaths: []string{"one/symlink subdir/symlink"},
919 SymlinkPaths: []string{"one/symlink subdir/symlink"},
920 Mnemonic: "SolibSymlink",
921 },
922 }
Liz Kammerc7737782021-11-04 10:56:13 -0400923 assertBuildStatements(t, expectedBuildStatements, actual)
Liz Kammerc49e6822021-06-08 15:04:11 -0400924}
925
926func TestSymlinkMultipleInputs(t *testing.T) {
927 const inputString = `
928{
Jason Wu118fd2b2022-10-27 18:41:15 +0000929 "artifacts": [
930 { "id": 1, "path_fragment_id": 1 },
931 { "id": 2, "path_fragment_id": 2 },
932 { "id": 3, "path_fragment_id": 3 }],
933 "actions": [{
934 "target_id": 1,
935 "action_key": "x",
936 "mnemonic": "Symlink",
937 "input_dep_set_ids": [1],
938 "output_ids": [3],
939 "primary_output_id": 3
940 }],
941 "dep_set_of_files": [{ "id": 1, "direct_artifact_ids": [1,2] }],
942 "path_fragments": [
943 { "id": 1, "label": "file" },
944 { "id": 2, "label": "other_file" },
945 { "id": 3, "label": "symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400946}`
947
Jason Wu118fd2b2022-10-27 18:41:15 +0000948 data, err := JsonToActionGraphContainer(inputString)
949 if err != nil {
950 t.Error(err)
951 return
952 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500953 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Liz Kammerc49e6822021-06-08 15:04:11 -0400954 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
955}
956
957func TestSymlinkMultipleOutputs(t *testing.T) {
958 const inputString = `
959{
Jason Wu118fd2b2022-10-27 18:41:15 +0000960 "artifacts": [
961 { "id": 1, "path_fragment_id": 1 },
962 { "id": 3, "path_fragment_id": 3 }],
963 "actions": [{
964 "target_id": 1,
965 "action_key": "x",
966 "mnemonic": "Symlink",
967 "input_dep_set_ids": [1],
968 "output_ids": [2,3],
969 "primary_output_id": 2
970 }],
971 "dep_set_of_files": [
972 { "id": 1, "direct_artifact_ids": [1] }],
973 "path_fragments": [
974 { "id": 1, "label": "file" },
975 { "id": 2, "label": "symlink" },
976 { "id": 3, "label": "other_symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400977}`
978
Jason Wu118fd2b2022-10-27 18:41:15 +0000979 data, err := JsonToActionGraphContainer(inputString)
980 if err != nil {
981 t.Error(err)
982 return
983 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500984 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Jason Wu118fd2b2022-10-27 18:41:15 +0000985 assertError(t, err, "undefined outputId 2")
Liz Kammerc49e6822021-06-08 15:04:11 -0400986}
987
Wei Li455ba832021-11-04 22:58:12 +0000988func TestTemplateExpandActionSubstitutions(t *testing.T) {
989 const inputString = `
990{
Jason Wu118fd2b2022-10-27 18:41:15 +0000991 "artifacts": [{
992 "id": 1,
993 "path_fragment_id": 1
994 }],
995 "actions": [{
996 "target_id": 1,
997 "action_key": "x",
998 "mnemonic": "TemplateExpand",
999 "configuration_id": 1,
1000 "output_ids": [1],
1001 "primary_output_id": 1,
1002 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1003 "template_content": "Test template substitutions: %token1%, %python_binary%",
1004 "substitutions": [
1005 { "key": "%token1%", "value": "abcd" },
1006 { "key": "%python_binary%", "value": "python3" }]
1007 }],
1008 "path_fragments": [
1009 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +00001010}`
1011
Jason Wu118fd2b2022-10-27 18:41:15 +00001012 data, err := JsonToActionGraphContainer(inputString)
1013 if err != nil {
1014 t.Error(err)
1015 return
1016 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001017 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Wei Li455ba832021-11-04 22:58:12 +00001018 if err != nil {
1019 t.Errorf("Unexpected error %q", err)
1020 }
1021
Liz Kammera4655a92023-02-10 17:17:28 -05001022 expectedBuildStatements := []*BuildStatement{
1023 &BuildStatement{
Wei Li455ba832021-11-04 22:58:12 +00001024 Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
1025 "chmod a+x template_file'",
Liz Kammer00629db2023-02-09 14:28:15 -05001026 OutputPaths: []string{"template_file"},
1027 Mnemonic: "TemplateExpand",
1028 SymlinkPaths: []string{},
Wei Li455ba832021-11-04 22:58:12 +00001029 },
1030 }
1031 assertBuildStatements(t, expectedBuildStatements, actual)
1032}
1033
1034func TestTemplateExpandActionNoOutput(t *testing.T) {
1035 const inputString = `
1036{
Jason Wu118fd2b2022-10-27 18:41:15 +00001037 "artifacts": [
1038 { "id": 1, "path_fragment_id": 1 }],
1039 "actions": [{
1040 "target_id": 1,
1041 "action_key": "x",
1042 "mnemonic": "TemplateExpand",
1043 "configuration_id": 1,
1044 "primary_output_id": 1,
1045 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1046 "templateContent": "Test template substitutions: %token1%, %python_binary%",
1047 "substitutions": [
1048 { "key": "%token1%", "value": "abcd" },
1049 { "key": "%python_binary%", "value": "python3" }]
1050 }],
1051 "path_fragments": [
1052 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +00001053}`
1054
Jason Wu118fd2b2022-10-27 18:41:15 +00001055 data, err := JsonToActionGraphContainer(inputString)
1056 if err != nil {
1057 t.Error(err)
1058 return
1059 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001060 _, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
Wei Li455ba832021-11-04 22:58:12 +00001061 assertError(t, err, `Expect 1 output to template expand action, got: output []`)
1062}
1063
Sasha Smundak1da064c2022-06-08 16:36:16 -07001064func TestFileWrite(t *testing.T) {
1065 const inputString = `
1066{
Jason Wu118fd2b2022-10-27 18:41:15 +00001067 "artifacts": [
1068 { "id": 1, "path_fragment_id": 1 }],
1069 "actions": [{
1070 "target_id": 1,
1071 "action_key": "x",
1072 "mnemonic": "FileWrite",
1073 "configuration_id": 1,
1074 "output_ids": [1],
1075 "primary_output_id": 1,
1076 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1077 "file_contents": "file data\n"
1078 }],
1079 "path_fragments": [
1080 { "id": 1, "label": "foo.manifest" }]
Sasha Smundak1da064c2022-06-08 16:36:16 -07001081}
1082`
Jason Wu118fd2b2022-10-27 18:41:15 +00001083 data, err := JsonToActionGraphContainer(inputString)
1084 if err != nil {
1085 t.Error(err)
1086 return
1087 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001088 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Sasha Smundak1da064c2022-06-08 16:36:16 -07001089 if err != nil {
1090 t.Errorf("Unexpected error %q", err)
1091 }
Liz Kammera4655a92023-02-10 17:17:28 -05001092 assertBuildStatements(t, []*BuildStatement{
1093 &BuildStatement{
Sasha Smundak1da064c2022-06-08 16:36:16 -07001094 OutputPaths: []string{"foo.manifest"},
1095 Mnemonic: "FileWrite",
1096 FileContents: "file data\n",
Liz Kammer00629db2023-02-09 14:28:15 -05001097 SymlinkPaths: []string{},
Sasha Smundak1da064c2022-06-08 16:36:16 -07001098 },
1099 }, actual)
1100}
1101
1102func TestSourceSymlinkManifest(t *testing.T) {
1103 const inputString = `
1104{
Jason Wu118fd2b2022-10-27 18:41:15 +00001105 "artifacts": [
1106 { "id": 1, "path_fragment_id": 1 }],
1107 "actions": [{
1108 "target_id": 1,
1109 "action_key": "x",
1110 "mnemonic": "SourceSymlinkManifest",
1111 "configuration_id": 1,
1112 "output_ids": [1],
1113 "primary_output_id": 1,
1114 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1115 "file_contents": "symlink target\n"
1116 }],
1117 "path_fragments": [
1118 { "id": 1, "label": "foo.manifest" }]
Sasha Smundak1da064c2022-06-08 16:36:16 -07001119}
1120`
Jason Wu118fd2b2022-10-27 18:41:15 +00001121 data, err := JsonToActionGraphContainer(inputString)
1122 if err != nil {
1123 t.Error(err)
1124 return
1125 }
Liz Kammer690fbac2023-02-10 11:11:17 -05001126 actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
Sasha Smundak1da064c2022-06-08 16:36:16 -07001127 if err != nil {
1128 t.Errorf("Unexpected error %q", err)
1129 }
Liz Kammera4655a92023-02-10 17:17:28 -05001130 assertBuildStatements(t, []*BuildStatement{
1131 &BuildStatement{
Liz Kammer00629db2023-02-09 14:28:15 -05001132 OutputPaths: []string{"foo.manifest"},
1133 Mnemonic: "SourceSymlinkManifest",
1134 SymlinkPaths: []string{},
Sasha Smundak1da064c2022-06-08 16:36:16 -07001135 },
1136 }, actual)
1137}
1138
Chris Parsons4f069892021-01-15 12:22:41 -05001139func assertError(t *testing.T, err error, expected string) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001140 t.Helper()
Chris Parsons943f2432021-01-19 11:36:50 -05001141 if err == nil {
1142 t.Errorf("expected error '%s', but got no error", expected)
1143 } else if err.Error() != expected {
Liz Kammerc49e6822021-06-08 15:04:11 -04001144 t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
Chris Parsons4f069892021-01-15 12:22:41 -05001145 }
1146}
1147
1148// Asserts that the given actual build statements match the given expected build statements.
1149// Build statement equivalence is determined using buildStatementEquals.
Liz Kammera4655a92023-02-10 17:17:28 -05001150func assertBuildStatements(t *testing.T, expected []*BuildStatement, actual []*BuildStatement) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001151 t.Helper()
Chris Parsons4f069892021-01-15 12:22:41 -05001152 if len(expected) != len(actual) {
Liz Kammerc3192992021-11-16 17:01:11 -05001153 t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
Chris Parsons4f069892021-01-15 12:22:41 -05001154 len(expected), len(actual), expected, actual)
1155 return
1156 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001157 type compareFn = func(i int, j int) bool
Liz Kammera4655a92023-02-10 17:17:28 -05001158 byCommand := func(slice []*BuildStatement) compareFn {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001159 return func(i int, j int) bool {
Liz Kammera4655a92023-02-10 17:17:28 -05001160 if slice[i] == nil {
1161 return false
1162 } else if slice[j] == nil {
1163 return false
1164 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001165 return slice[i].Command < slice[j].Command
Chris Parsons4f069892021-01-15 12:22:41 -05001166 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001167 }
1168 sort.SliceStable(expected, byCommand(expected))
1169 sort.SliceStable(actual, byCommand(actual))
1170 for i, actualStatement := range actual {
1171 expectedStatement := expected[i]
1172 if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
1173 t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
Usta Shresthaef922252022-06-02 14:23:02 -04001174 differingField, actualStatement, expectedStatement)
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001175 return
1176 }
Chris Parsons4f069892021-01-15 12:22:41 -05001177 }
1178}
1179
Liz Kammera4655a92023-02-10 17:17:28 -05001180func buildStatementEquals(first *BuildStatement, second *BuildStatement) string {
1181 if (first == nil) != (second == nil) {
1182 return "Nil"
1183 }
Chris Parsons4f069892021-01-15 12:22:41 -05001184 if first.Mnemonic != second.Mnemonic {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001185 return "Mnemonic"
Chris Parsons4f069892021-01-15 12:22:41 -05001186 }
1187 if first.Command != second.Command {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001188 return "Command"
Chris Parsons4f069892021-01-15 12:22:41 -05001189 }
1190 // Ordering is significant for environment variables.
1191 if !reflect.DeepEqual(first.Env, second.Env) {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001192 return "Env"
Chris Parsons4f069892021-01-15 12:22:41 -05001193 }
1194 // Ordering is irrelevant for input and output paths, so compare sets.
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001195 if !reflect.DeepEqual(sortedStrings(first.InputPaths), sortedStrings(second.InputPaths)) {
1196 return "InputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001197 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001198 if !reflect.DeepEqual(sortedStrings(first.OutputPaths), sortedStrings(second.OutputPaths)) {
1199 return "OutputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001200 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001201 if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
1202 return "SymlinkPaths"
Liz Kammerc49e6822021-06-08 15:04:11 -04001203 }
1204 if first.Depfile != second.Depfile {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001205 return "Depfile"
Liz Kammerc49e6822021-06-08 15:04:11 -04001206 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001207 return ""
Chris Parsons4f069892021-01-15 12:22:41 -05001208}
1209
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001210func sortedStrings(stringSlice []string) []string {
1211 sorted := make([]string, len(stringSlice))
1212 copy(sorted, stringSlice)
1213 sort.Strings(sorted)
1214 return sorted
Chris Parsons4f069892021-01-15 12:22:41 -05001215}
Jason Wu118fd2b2022-10-27 18:41:15 +00001216
1217// Transform the json format to ActionGraphContainer
1218func JsonToActionGraphContainer(inputString string) ([]byte, error) {
1219 var aqueryProtoResult analysis_v2_proto.ActionGraphContainer
1220 err := json.Unmarshal([]byte(inputString), &aqueryProtoResult)
1221 if err != nil {
1222 return []byte(""), err
1223 }
1224 data, _ := proto.Marshal(&aqueryProtoResult)
1225 return data, err
1226}