blob: 2eacafa64103760ab26903ffede529d7e6f191fa [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 Wu6fe87212022-10-21 02:57:41 -040018 "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 Wu6fe87212022-10-21 02:57:41 -040023
24 "google.golang.org/protobuf/proto"
25 analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
Chris Parsons4f069892021-01-15 12:22:41 -050026)
27
28func TestAqueryMultiArchGenrule(t *testing.T) {
29 // This input string is retrieved from a real build of bionic-related genrules.
30 const inputString = `
31{
Jason Wu6fe87212022-10-21 02:57:41 -040032 "Artifacts": [
33 { "Id": 1, "path_fragment_id": 1 },
34 { "Id": 2, "path_fragment_id": 6 },
35 { "Id": 3, "path_fragment_id": 8 },
36 { "Id": 4, "path_fragment_id": 12 },
37 { "Id": 5, "path_fragment_id": 19 },
38 { "Id": 6, "path_fragment_id": 20 },
39 { "Id": 7, "path_fragment_id": 21 }],
40 "Actions": [{
41 "target_id": 1,
42 "action_key": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
43 "Mnemonic": "Genrule",
44 "configuration_id": 1,
45 "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm.S"],
46 "environment_variables": [{
47 "Key": "PATH",
48 "Value": "/bin:/usr/bin:/usr/local/bin"
49 }],
50 "input_dep_set_ids": [1],
51 "output_ids": [4],
52 "primary_output_id": 4
53 }, {
54 "target_id": 2,
55 "action_key": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
56 "Mnemonic": "Genrule",
57 "configuration_id": 1,
58 "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86.S"],
59 "environment_variables": [{
60 "Key": "PATH",
61 "Value": "/bin:/usr/bin:/usr/local/bin"
62 }],
63 "input_dep_set_ids": [2],
64 "output_ids": [5],
65 "primary_output_id": 5
66 }, {
67 "target_id": 3,
68 "action_key": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
69 "Mnemonic": "Genrule",
70 "configuration_id": 1,
71 "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86_64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86_64.S"],
72 "environment_variables": [{
73 "Key": "PATH",
74 "Value": "/bin:/usr/bin:/usr/local/bin"
75 }],
76 "input_dep_set_ids": [3],
77 "output_ids": [6],
78 "primary_output_id": 6
79 }, {
80 "target_id": 4,
81 "action_key": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
82 "Mnemonic": "Genrule",
83 "configuration_id": 1,
84 "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm64.S"],
85 "environment_variables": [{
86 "Key": "PATH",
87 "Value": "/bin:/usr/bin:/usr/local/bin"
88 }],
89 "input_dep_set_ids": [4],
90 "output_ids": [7],
91 "primary_output_id": 7
92 }],
93 "Targets": [
94 { "Id": 1, "Label": "@sourceroot//bionic/libc:syscalls-arm", "rule_class_id": 1 },
95 { "Id": 2, "Label": "@sourceroot//bionic/libc:syscalls-x86", "rule_class_id": 1 },
96 { "Id": 3, "Label": "@sourceroot//bionic/libc:syscalls-x86_64", "rule_class_id": 1 },
97 { "Id": 4, "Label": "@sourceroot//bionic/libc:syscalls-arm64", "rule_class_id": 1 }],
98 "dep_set_of_files": [
99 { "Id": 1, "direct_artifact_ids": [1, 2, 3] },
100 { "Id": 2, "direct_artifact_ids": [1, 2, 3] },
101 { "Id": 3, "direct_artifact_ids": [1, 2, 3] },
102 { "Id": 4, "direct_artifact_ids": [1, 2, 3] }],
103 "Configuration": [{
104 "Id": 1,
105 "Mnemonic": "k8-fastbuild",
106 "platform_name": "k8",
107 "Checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
108 }],
109 "rule_classes": [{ "Id": 1, "Name": "genrule"}],
110 "path_fragments": [
111 { "Id": 5, "Label": ".." },
112 { "Id": 4, "Label": "sourceroot", "parent_id": 5 },
113 { "Id": 3, "Label": "bionic", "parent_id": 4 },
114 { "Id": 2, "Label": "libc", "parent_id": 3 },
115 { "Id": 1, "Label": "SYSCALLS.TXT", "parent_id": 2 },
116 { "Id": 7, "Label": "tools", "parent_id": 2 },
117 { "Id": 6, "Label": "gensyscalls.py", "parent_id": 7 },
118 { "Id": 11, "Label": "bazel_tools", "parent_id": 5 },
119 { "Id": 10, "Label": "tools", "parent_id": 11 },
120 { "Id": 9, "Label": "genrule", "parent_id": 10 },
121 { "Id": 8, "Label": "genrule-setup.sh", "parent_id": 9 },
122 { "Id": 18, "Label": "bazel-out" },
123 { "Id": 17, "Label": "sourceroot", "parent_id": 18 },
124 { "Id": 16, "Label": "k8-fastbuild", "parent_id": 17 },
125 { "Id": 15, "Label": "bin", "parent_id": 16 },
126 { "Id": 14, "Label": "bionic", "parent_id": 15 },
127 { "Id": 13, "Label": "libc", "parent_id": 14 },
128 { "Id": 12, "Label": "syscalls-arm.S", "parent_id": 13 },
129 { "Id": 19, "Label": "syscalls-x86.S", "parent_id": 13 },
130 { "Id": 20, "Label": "syscalls-x86_64.S", "parent_id": 13 },
131 { "Id": 21, "Label": "syscalls-arm64.S", "parent_id": 13 }]
132}
133`
134 data, err := JsonToActionGraphContainer(inputString)
135 if err != nil {
136 t.Error(err)
137 return
138 }
139 actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
Usta Shrestha16ac1352022-06-22 11:01:55 -0400140 var expectedBuildStatements []BuildStatement
Chris Parsons4f069892021-01-15 12:22:41 -0500141 for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
142 expectedBuildStatements = append(expectedBuildStatements,
143 BuildStatement{
144 Command: fmt.Sprintf(
145 "/bin/bash -c 'source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py %s ../sourceroot/bionic/libc/SYSCALLS.TXT > bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S'",
146 arch, arch),
147 OutputPaths: []string{
148 fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
149 },
Chris Parsons4f069892021-01-15 12:22:41 -0500150 Env: []KeyValuePair{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400151 {Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
Chris Parsons4f069892021-01-15 12:22:41 -0500152 },
153 Mnemonic: "Genrule",
154 })
155 }
156 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400157
158 expectedFlattenedInputs := []string{
159 "../sourceroot/bionic/libc/SYSCALLS.TXT",
160 "../sourceroot/bionic/libc/tools/gensyscalls.py",
Chris Parsons1a7aca02022-04-25 22:35:15 -0400161 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400162 // In this example, each depset should have the same expected inputs.
163 for _, actualDepset := range actualDepsets {
164 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
165 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
166 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
167 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400168 }
Chris Parsons4f069892021-01-15 12:22:41 -0500169}
170
171func TestInvalidOutputId(t *testing.T) {
172 const inputString = `
173{
Jason Wu6fe87212022-10-21 02:57:41 -0400174 "artifacts": [
175 { "id": 1, "path_fragment_id": 1 },
176 { "id": 2, "path_fragment_id": 2 }],
177 "actions": [{
178 "target_id": 1,
179 "action_key": "x",
180 "mnemonic": "x",
181 "arguments": ["touch", "foo"],
182 "input_dep_set_ids": [1],
183 "output_ids": [3],
184 "primary_output_id": 3
185 }],
186 "dep_set_of_files": [
187 { "id": 1, "direct_artifact_ids": [1, 2] }],
188 "path_fragments": [
189 { "id": 1, "label": "one" },
190 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500191}`
192
Jason Wu6fe87212022-10-21 02:57:41 -0400193 data, err := JsonToActionGraphContainer(inputString)
194 if err != nil {
195 t.Error(err)
196 return
197 }
198 _, _, err = AqueryBuildStatements(data)
Chris Parsons4f069892021-01-15 12:22:41 -0500199 assertError(t, err, "undefined outputId 3")
200}
201
Chris Parsons1a7aca02022-04-25 22:35:15 -0400202func TestInvalidInputDepsetIdFromAction(t *testing.T) {
Chris Parsons4f069892021-01-15 12:22:41 -0500203 const inputString = `
204{
Jason Wu6fe87212022-10-21 02:57:41 -0400205 "artifacts": [
206 { "id": 1, "path_fragment_id": 1 },
207 { "id": 2, "path_fragment_id": 2 }],
208 "actions": [{
209 "target_id": 1,
210 "action_key": "x",
211 "mnemonic": "x",
212 "arguments": ["touch", "foo"],
213 "input_dep_set_ids": [2],
214 "output_ids": [1],
215 "primary_output_id": 1
216 }],
217 "dep_set_of_files": [
218 { "id": 1, "direct_artifact_ids": [1, 2] }],
219 "path_fragments": [
220 { "id": 1, "label": "one" },
221 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500222}`
223
Jason Wu6fe87212022-10-21 02:57:41 -0400224 data, err := JsonToActionGraphContainer(inputString)
225 if err != nil {
226 t.Error(err)
227 return
228 }
229 _, _, err = AqueryBuildStatements(data)
Chris Parsons4f069892021-01-15 12:22:41 -0500230 assertError(t, err, "undefined input depsetId 2")
231}
232
Chris Parsons1a7aca02022-04-25 22:35:15 -0400233func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
234 const inputString = `
235{
Jason Wu6fe87212022-10-21 02:57:41 -0400236 "artifacts": [
237 { "id": 1, "path_fragment_id": 1 },
238 { "id": 2, "path_fragment_id": 2 }],
239 "actions": [{
240 "target_id": 1,
241 "action_key": "x",
242 "mnemonic": "x",
243 "arguments": ["touch", "foo"],
244 "input_dep_set_ids": [1],
245 "output_ids": [1],
246 "primary_output_id": 1
247 }],
248 "dep_set_of_files": [
249 { "id": 1, "direct_artifact_ids": [1, 2], "transitive_dep_set_ids": [42] }],
250 "path_fragments": [
251 { "id": 1, "label": "one"},
252 { "id": 2, "label": "two" }]
Chris Parsons1a7aca02022-04-25 22:35:15 -0400253}`
254
Jason Wu6fe87212022-10-21 02:57:41 -0400255 data, err := JsonToActionGraphContainer(inputString)
256 if err != nil {
257 t.Error(err)
258 return
259 }
260 _, _, err = AqueryBuildStatements(data)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400261 assertError(t, err, "undefined input depsetId 42 (referenced by depsetId 1)")
262}
263
Chris Parsons4f069892021-01-15 12:22:41 -0500264func TestInvalidInputArtifactId(t *testing.T) {
265 const inputString = `
266{
Jason Wu6fe87212022-10-21 02:57:41 -0400267 "artifacts": [
268 { "id": 1, "path_fragment_id": 1 },
269 { "id": 2, "path_fragment_id": 2 }],
270 "actions": [{
271 "target_id": 1,
272 "action_key": "x",
273 "mnemonic": "x",
274 "arguments": ["touch", "foo"],
275 "input_dep_set_ids": [1],
276 "output_ids": [1],
277 "primary_output_id": 1
278 }],
279 "dep_set_of_files": [
280 { "id": 1, "direct_artifact_ids": [1, 3] }],
281 "path_fragments": [
282 { "id": 1, "label": "one" },
283 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500284}`
285
Jason Wu6fe87212022-10-21 02:57:41 -0400286 data, err := JsonToActionGraphContainer(inputString)
287 if err != nil {
288 t.Error(err)
289 return
290 }
291 _, _, err = AqueryBuildStatements(data)
Chris Parsons4f069892021-01-15 12:22:41 -0500292 assertError(t, err, "undefined input artifactId 3")
293}
294
295func TestInvalidPathFragmentId(t *testing.T) {
296 const inputString = `
297{
Jason Wu6fe87212022-10-21 02:57:41 -0400298 "artifacts": [
299 { "id": 1, "path_fragment_id": 1 },
300 { "id": 2, "path_fragment_id": 2 }],
301 "actions": [{
302 "target_id": 1,
303 "action_key": "x",
304 "mnemonic": "x",
305 "arguments": ["touch", "foo"],
306 "input_dep_set_ids": [1],
307 "output_ids": [1],
308 "primary_output_id": 1
309 }],
310 "dep_set_of_files": [
311 { "id": 1, "direct_artifact_ids": [1, 2] }],
312 "path_fragments": [
313 { "id": 1, "label": "one" },
314 { "id": 2, "label": "two", "parent_id": 3 }]
Chris Parsons4f069892021-01-15 12:22:41 -0500315}`
316
Jason Wu6fe87212022-10-21 02:57:41 -0400317 data, err := JsonToActionGraphContainer(inputString)
318 if err != nil {
319 t.Error(err)
320 return
321 }
322 _, _, err = AqueryBuildStatements(data)
Chris Parsons4f069892021-01-15 12:22:41 -0500323 assertError(t, err, "undefined path fragment id 3")
324}
325
Liz Kammerde116852021-03-25 16:42:37 -0400326func TestDepfiles(t *testing.T) {
327 const inputString = `
328{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700329 "artifacts": [
Jason Wu6fe87212022-10-21 02:57:41 -0400330 { "id": 1, "path_fragment_id": 1 },
331 { "id": 2, "path_fragment_id": 2 },
332 { "id": 3, "path_fragment_id": 3 }],
Liz Kammerde116852021-03-25 16:42:37 -0400333 "actions": [{
Jason Wu6fe87212022-10-21 02:57:41 -0400334 "target_Id": 1,
335 "action_Key": "x",
Liz Kammerde116852021-03-25 16:42:37 -0400336 "mnemonic": "x",
337 "arguments": ["touch", "foo"],
Jason Wu6fe87212022-10-21 02:57:41 -0400338 "input_dep_set_ids": [1],
339 "output_ids": [2, 3],
340 "primary_output_id": 2
Liz Kammerde116852021-03-25 16:42:37 -0400341 }],
Jason Wu6fe87212022-10-21 02:57:41 -0400342 "dep_set_of_files": [
343 { "id": 1, "direct_Artifact_Ids": [1, 2, 3] }],
344 "path_fragments": [
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700345 { "id": 1, "label": "one" },
346 { "id": 2, "label": "two" },
347 { "id": 3, "label": "two.d" }]
Liz Kammerde116852021-03-25 16:42:37 -0400348}`
349
Jason Wu6fe87212022-10-21 02:57:41 -0400350 data, err := JsonToActionGraphContainer(inputString)
351 if err != nil {
352 t.Error(err)
353 return
354 }
355 actual, _, err := AqueryBuildStatements(data)
Liz Kammerde116852021-03-25 16:42:37 -0400356 if err != nil {
357 t.Errorf("Unexpected error %q", err)
358 }
359 if expected := 1; len(actual) != expected {
360 t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
361 }
362
363 bs := actual[0]
364 expectedDepfile := "two.d"
365 if bs.Depfile == nil {
366 t.Errorf("Expected depfile %q, but there was none found", expectedDepfile)
367 } else if *bs.Depfile != expectedDepfile {
368 t.Errorf("Expected depfile %q, but got %q", expectedDepfile, *bs.Depfile)
369 }
370}
371
372func TestMultipleDepfiles(t *testing.T) {
373 const inputString = `
374{
Jason Wu6fe87212022-10-21 02:57:41 -0400375 "artifacts": [
376 { "id": 1, "path_fragment_id": 1 },
377 { "id": 2, "path_fragment_id": 2 },
378 { "id": 3, "path_fragment_id": 3 },
379 { "id": 4, "path_fragment_id": 4 }],
380 "actions": [{
381 "target_id": 1,
382 "action_key": "x",
383 "mnemonic": "x",
384 "arguments": ["touch", "foo"],
385 "input_dep_set_ids": [1],
386 "output_ids": [2,3,4],
387 "primary_output_id": 2
388 }],
389 "dep_set_of_files": [{
390 "id": 1,
391 "direct_artifact_ids": [1, 2, 3, 4]
392 }],
393 "path_fragments": [
394 { "id": 1, "label": "one" },
395 { "id": 2, "label": "two" },
396 { "id": 3, "label": "two.d" },
397 { "id": 4, "label": "other.d" }]
Liz Kammerde116852021-03-25 16:42:37 -0400398}`
399
Jason Wu6fe87212022-10-21 02:57:41 -0400400 data, err := JsonToActionGraphContainer(inputString)
401 if err != nil {
402 t.Error(err)
403 return
404 }
405 _, _, err = AqueryBuildStatements(data)
Liz Kammerde116852021-03-25 16:42:37 -0400406 assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
407}
408
Chris Parsons943f2432021-01-19 11:36:50 -0500409func TestTransitiveInputDepsets(t *testing.T) {
410 // The input aquery for this test comes from a proof-of-concept starlark rule which registers
411 // a single action with many inputs given via a deep depset.
412 const inputString = `
413{
Jason Wu6fe87212022-10-21 02:57:41 -0400414 "artifacts": [
415 { "id": 1, "path_fragment_id": 1 },
416 { "id": 2, "path_fragment_id": 7 },
417 { "id": 3, "path_fragment_id": 8 },
418 { "id": 4, "path_fragment_id": 9 },
419 { "id": 5, "path_fragment_id": 10 },
420 { "id": 6, "path_fragment_id": 11 },
421 { "id": 7, "path_fragment_id": 12 },
422 { "id": 8, "path_fragment_id": 13 },
423 { "id": 9, "path_fragment_id": 14 },
424 { "id": 10, "path_fragment_id": 15 },
425 { "id": 11, "path_fragment_id": 16 },
426 { "id": 12, "path_fragment_id": 17 },
427 { "id": 13, "path_fragment_id": 18 },
428 { "id": 14, "path_fragment_id": 19 },
429 { "id": 15, "path_fragment_id": 20 },
430 { "id": 16, "path_fragment_id": 21 },
431 { "id": 17, "path_fragment_id": 22 },
432 { "id": 18, "path_fragment_id": 23 },
433 { "id": 19, "path_fragment_id": 24 },
434 { "id": 20, "path_fragment_id": 25 },
435 { "id": 21, "path_fragment_id": 26 }],
436 "actions": [{
437 "target_id": 1,
438 "action_key": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
439 "mnemonic": "Action",
440 "configuration_id": 1,
441 "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"],
442 "input_dep_set_ids": [1],
443 "output_ids": [21],
444 "primary_output_id": 21
445 }],
446 "dep_set_of_files": [
447 { "id": 3, "direct_artifact_ids": [1, 2, 3, 4, 5] },
448 { "id": 4, "direct_artifact_ids": [6, 7, 8, 9, 10] },
449 { "id": 2, "transitive_dep_set_ids": [3, 4], "direct_artifact_ids": [11, 12, 13, 14, 15] },
450 { "id": 5, "direct_artifact_ids": [16, 17, 18, 19] },
451 { "id": 1, "transitive_dep_set_ids": [2, 5], "direct_artifact_ids": [20] }],
452 "path_fragments": [
453 { "id": 6, "label": "bazel-out" },
454 { "id": 5, "label": "sourceroot", "parent_id": 6 },
455 { "id": 4, "label": "k8-fastbuild", "parent_id": 5 },
456 { "id": 3, "label": "bin", "parent_id": 4 },
457 { "id": 2, "label": "testpkg", "parent_id": 3 },
458 { "id": 1, "label": "test_1", "parent_id": 2 },
459 { "id": 7, "label": "test_2", "parent_id": 2 },
460 { "id": 8, "label": "test_3", "parent_id": 2 },
461 { "id": 9, "label": "test_4", "parent_id": 2 },
462 { "id": 10, "label": "test_5", "parent_id": 2 },
463 { "id": 11, "label": "test_6", "parent_id": 2 },
464 { "id": 12, "label": "test_7", "parent_id": 2 },
465 { "id": 13, "label": "test_8", "parent_id": 2 },
466 { "id": 14, "label": "test_9", "parent_id": 2 },
467 { "id": 15, "label": "test_10", "parent_id": 2 },
468 { "id": 16, "label": "test_11", "parent_id": 2 },
469 { "id": 17, "label": "test_12", "parent_id": 2 },
470 { "id": 18, "label": "test_13", "parent_id": 2 },
471 { "id": 19, "label": "test_14", "parent_id": 2 },
472 { "id": 20, "label": "test_15", "parent_id": 2 },
473 { "id": 21, "label": "test_16", "parent_id": 2 },
474 { "id": 22, "label": "test_17", "parent_id": 2 },
475 { "id": 23, "label": "test_18", "parent_id": 2 },
476 { "id": 24, "label": "test_19", "parent_id": 2 },
477 { "id": 25, "label": "test_root", "parent_id": 2 },
478 { "id": 26,"label": "test_out", "parent_id": 2 }]
Chris Parsons943f2432021-01-19 11:36:50 -0500479}`
480
Jason Wu6fe87212022-10-21 02:57:41 -0400481 data, err := JsonToActionGraphContainer(inputString)
482 if err != nil {
483 t.Error(err)
484 return
485 }
486 actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400487
Chris Parsons943f2432021-01-19 11:36:50 -0500488 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400489 {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400490 Command: "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
491 OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
492 Mnemonic: "Action",
Chris Parsons943f2432021-01-19 11:36:50 -0500493 },
494 }
495 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400496
497 // Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
498 // are given via a deep depset, but the depset is flattened when returned as a
499 // BuildStatement slice.
Usta Shrestha16ac1352022-06-22 11:01:55 -0400500 var expectedFlattenedInputs []string
Chris Parsons1a7aca02022-04-25 22:35:15 -0400501 for i := 1; i < 20; i++ {
502 expectedFlattenedInputs = append(expectedFlattenedInputs, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
503 }
504 expectedFlattenedInputs = append(expectedFlattenedInputs, "bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root")
505
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400506 actualDepsetHashes := actualbuildStatements[0].InputDepsetHashes
507 actualFlattenedInputs := flattenDepsets(actualDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400508 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
509 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
510 }
Chris Parsons943f2432021-01-19 11:36:50 -0500511}
512
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700513func TestSymlinkTree(t *testing.T) {
514 const inputString = `
515{
Jason Wu6fe87212022-10-21 02:57:41 -0400516 "artifacts": [
517 { "id": 1, "path_fragment_id": 1 },
518 { "id": 2, "path_fragment_id": 2 }],
519 "actions": [{
520 "target_id": 1,
521 "action_key": "x",
522 "mnemonic": "SymlinkTree",
523 "configuration_id": 1,
524 "input_dep_set_ids": [1],
525 "output_ids": [2],
526 "primary_output_id": 2,
527 "execution_platform": "//build/bazel/platforms:linux_x86_64"
528 }],
529 "path_fragments": [
530 { "id": 1, "label": "foo.manifest" },
531 { "id": 2, "label": "foo.runfiles/MANIFEST" }],
532 "dep_set_of_files": [
533 { "id": 1, "direct_artifact_ids": [1] }]
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700534}
535`
Jason Wu6fe87212022-10-21 02:57:41 -0400536 data, err := JsonToActionGraphContainer(inputString)
537 if err != nil {
538 t.Error(err)
539 return
540 }
541 actual, _, err := AqueryBuildStatements(data)
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700542 if err != nil {
543 t.Errorf("Unexpected error %q", err)
544 }
545 assertBuildStatements(t, []BuildStatement{
546 {
547 Command: "",
548 OutputPaths: []string{"foo.runfiles/MANIFEST"},
549 Mnemonic: "SymlinkTree",
550 InputPaths: []string{"foo.manifest"},
551 },
552 }, actual)
553}
554
Usta Shresthaef922252022-06-02 14:23:02 -0400555func TestBazelOutRemovalFromInputDepsets(t *testing.T) {
556 const inputString = `{
Jason Wu6fe87212022-10-21 02:57:41 -0400557 "artifacts": [
558 { "id": 1, "path_fragment_id": 10 },
559 { "id": 2, "path_fragment_id": 20 },
560 { "id": 3, "path_fragment_id": 30 },
561 { "id": 4, "path_fragment_id": 40 }],
562 "dep_set_of_files": [{
563 "id": 1111,
564 "direct_artifact_ids": [3 , 4]
565 }, {
566 "id": 2222,
567 "direct_artifact_ids": [3]
568 }],
569 "actions": [{
570 "target_id": 100,
571 "action_key": "x",
572 "input_dep_set_ids": [1111, 2222],
573 "mnemonic": "x",
574 "arguments": ["bogus", "command"],
575 "output_ids": [2],
576 "primary_output_id": 1
577 }],
578 "path_fragments": [
579 { "id": 10, "label": "input" },
580 { "id": 20, "label": "output" },
581 { "id": 30, "label": "dep1", "parent_id": 50 },
582 { "id": 40, "label": "dep2", "parent_id": 60 },
583 { "id": 50, "label": "bazel_tools", "parent_id": 60 },
584 { "id": 60, "label": ".."}
585 ]
Usta Shresthaef922252022-06-02 14:23:02 -0400586}`
Jason Wu6fe87212022-10-21 02:57:41 -0400587 data, err := JsonToActionGraphContainer(inputString)
588 if err != nil {
589 t.Error(err)
590 return
591 }
592 actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data)
Usta Shresthae70f28c2022-08-08 22:48:13 -0400593 if len(actualDepsets) != 2 {
Usta Shresthaef922252022-06-02 14:23:02 -0400594 t.Errorf("expected 1 depset but found %#v", actualDepsets)
595 return
596 }
597 dep2Found := false
598 for _, dep := range flattenDepsets([]string{actualDepsets[0].ContentHash}, actualDepsets) {
599 if dep == "../bazel_tools/dep1" {
600 t.Errorf("dependency %s expected to be removed but still exists", dep)
601 } else if dep == "../dep2" {
602 dep2Found = true
603 }
604 }
605 if !dep2Found {
606 t.Errorf("dependency ../dep2 expected but not found")
607 }
608
609 expectedBuildStatement := BuildStatement{
610 Command: "bogus command",
611 OutputPaths: []string{"output"},
612 Mnemonic: "x",
613 }
614 buildStatementFound := false
615 for _, actualBuildStatement := range actualBuildStatements {
616 if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
617 buildStatementFound = true
618 break
619 }
620 }
621 if !buildStatementFound {
622 t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
623 return
624 }
625}
626
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400627func TestMiddlemenAction(t *testing.T) {
628 const inputString = `
629{
Jason Wu6fe87212022-10-21 02:57:41 -0400630 "artifacts": [
631 { "id": 1, "path_fragment_id": 1 },
632 { "id": 2, "path_fragment_id": 2 },
633 { "id": 3, "path_fragment_id": 3 },
634 { "id": 4, "path_fragment_id": 4 },
635 { "id": 5, "path_fragment_id": 5 },
636 { "id": 6, "path_fragment_id": 6 }],
637 "path_fragments": [
638 { "id": 1, "label": "middleinput_one" },
639 { "id": 2, "label": "middleinput_two" },
640 { "id": 3, "label": "middleman_artifact" },
641 { "id": 4, "label": "maininput_one" },
642 { "id": 5, "label": "maininput_two" },
643 { "id": 6, "label": "output" }],
644 "dep_set_of_files": [
645 { "id": 1, "direct_artifact_ids": [1, 2] },
646 { "id": 2, "direct_artifact_ids": [3, 4, 5] }],
647 "actions": [{
648 "target_id": 1,
649 "action_key": "x",
650 "mnemonic": "Middleman",
651 "arguments": ["touch", "foo"],
652 "input_dep_set_ids": [1],
653 "output_ids": [3],
654 "primary_output_id": 3
655 }, {
656 "target_id": 2,
657 "action_key": "y",
658 "mnemonic": "Main action",
659 "arguments": ["touch", "foo"],
660 "input_dep_set_ids": [2],
661 "output_ids": [6],
662 "primary_output_id": 6
663 }]
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400664}`
Jason Wu6fe87212022-10-21 02:57:41 -0400665 data, err := JsonToActionGraphContainer(inputString)
666 if err != nil {
667 t.Error(err)
668 return
669 }
670 actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400671 if err != nil {
672 t.Errorf("Unexpected error %q", err)
673 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400674 if expected := 1; len(actualBuildStatements) != expected {
675 t.Fatalf("Expected %d build statements, got %d", expected, len(actualBuildStatements))
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400676 }
677
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400678 expectedDepsetFiles := [][]string{
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400679 {"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"},
680 {"middleinput_one", "middleinput_two"},
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400681 }
682 assertFlattenedDepsets(t, actualDepsets, expectedDepsetFiles)
683
Chris Parsons1a7aca02022-04-25 22:35:15 -0400684 bs := actualBuildStatements[0]
685 if len(bs.InputPaths) > 0 {
686 t.Errorf("Expected main action raw inputs to be empty, but got %q", bs.InputPaths)
687 }
688
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400689 expectedOutputs := []string{"output"}
690 if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) {
691 t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths)
692 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400693
Chris Parsons1a7aca02022-04-25 22:35:15 -0400694 expectedFlattenedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400695 actualFlattenedInputs := flattenDepsets(bs.InputDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400696
697 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
698 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
699 }
700}
701
702// Returns the contents of given depsets in concatenated post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400703func flattenDepsets(depsetHashesToFlatten []string, allDepsets []AqueryDepset) []string {
704 depsetsByHash := map[string]AqueryDepset{}
Chris Parsons1a7aca02022-04-25 22:35:15 -0400705 for _, depset := range allDepsets {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400706 depsetsByHash[depset.ContentHash] = depset
Chris Parsons1a7aca02022-04-25 22:35:15 -0400707 }
Usta Shrestha16ac1352022-06-22 11:01:55 -0400708 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400709 for _, depsetId := range depsetHashesToFlatten {
710 result = append(result, flattenDepset(depsetId, depsetsByHash)...)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400711 }
712 return result
713}
714
715// Returns the contents of a given depset in post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400716func flattenDepset(depsetHashToFlatten string, allDepsets map[string]AqueryDepset) []string {
717 depset := allDepsets[depsetHashToFlatten]
Usta Shrestha16ac1352022-06-22 11:01:55 -0400718 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400719 for _, depsetId := range depset.TransitiveDepSetHashes {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400720 result = append(result, flattenDepset(depsetId, allDepsets)...)
721 }
722 result = append(result, depset.DirectArtifacts...)
723 return result
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400724}
725
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400726func assertFlattenedDepsets(t *testing.T, actualDepsets []AqueryDepset, expectedDepsetFiles [][]string) {
727 t.Helper()
728 if len(actualDepsets) != len(expectedDepsetFiles) {
Sasha Smundakf10c3ac2022-06-08 11:46:31 -0700729 t.Errorf("Expected %d depsets, but got %d depsets", len(expectedDepsetFiles), len(actualDepsets))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400730 }
731 for i, actualDepset := range actualDepsets {
732 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
733 if !reflect.DeepEqual(actualFlattenedInputs, expectedDepsetFiles[i]) {
734 t.Errorf("Expected depset files: %v, but got %v", expectedDepsetFiles[i], actualFlattenedInputs)
735 }
736 }
737}
738
Liz Kammerc49e6822021-06-08 15:04:11 -0400739func TestSimpleSymlink(t *testing.T) {
740 const inputString = `
741{
Jason Wu6fe87212022-10-21 02:57:41 -0400742 "artifacts": [
743 { "id": 1, "path_fragment_id": 3 },
744 { "id": 2, "path_fragment_id": 5 }],
745 "actions": [{
746 "target_id": 1,
747 "action_key": "x",
748 "mnemonic": "Symlink",
749 "input_dep_set_ids": [1],
750 "output_ids": [2],
751 "primary_output_id": 2
752 }],
753 "dep_set_of_files": [
754 { "id": 1, "direct_artifact_ids": [1] }],
755 "path_fragments": [
756 { "id": 1, "label": "one" },
757 { "id": 2, "label": "file_subdir", "parent_id": 1 },
758 { "id": 3, "label": "file", "parent_id": 2 },
759 { "id": 4, "label": "symlink_subdir", "parent_id": 1 },
760 { "id": 5, "label": "symlink", "parent_id": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400761}`
Jason Wu6fe87212022-10-21 02:57:41 -0400762 data, err := JsonToActionGraphContainer(inputString)
763 if err != nil {
764 t.Error(err)
765 return
766 }
767 actual, _, err := AqueryBuildStatements(data)
Liz Kammerc49e6822021-06-08 15:04:11 -0400768
769 if err != nil {
770 t.Errorf("Unexpected error %q", err)
771 }
772
773 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400774 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400775 Command: "mkdir -p one/symlink_subdir && " +
776 "rm -f one/symlink_subdir/symlink && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400777 "ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
Liz Kammerc49e6822021-06-08 15:04:11 -0400778 InputPaths: []string{"one/file_subdir/file"},
779 OutputPaths: []string{"one/symlink_subdir/symlink"},
780 SymlinkPaths: []string{"one/symlink_subdir/symlink"},
781 Mnemonic: "Symlink",
782 },
783 }
784 assertBuildStatements(t, actual, expectedBuildStatements)
785}
786
787func TestSymlinkQuotesPaths(t *testing.T) {
788 const inputString = `
789{
Jason Wu6fe87212022-10-21 02:57:41 -0400790 "artifacts": [
791 { "id": 1, "path_fragment_id": 3 },
792 { "id": 2, "path_fragment_id": 5 }],
793 "actions": [{
794 "target_id": 1,
795 "action_key": "x",
796 "mnemonic": "SolibSymlink",
797 "input_dep_set_ids": [1],
798 "output_ids": [2],
799 "primary_output_id": 2
800 }],
801 "dep_set_of_files": [
802 { "id": 1, "direct_artifact_ids": [1] }],
803 "path_fragments": [
804 { "id": 1, "label": "one" },
805 { "id": 2, "label": "file subdir", "parent_id": 1 },
806 { "id": 3, "label": "file", "parent_id": 2 },
807 { "id": 4, "label": "symlink subdir", "parent_id": 1 },
808 { "id": 5, "label": "symlink", "parent_id": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400809}`
810
Jason Wu6fe87212022-10-21 02:57:41 -0400811 data, err := JsonToActionGraphContainer(inputString)
812 if err != nil {
813 t.Error(err)
814 return
815 }
816 actual, _, err := AqueryBuildStatements(data)
Liz Kammerc49e6822021-06-08 15:04:11 -0400817 if err != nil {
818 t.Errorf("Unexpected error %q", err)
819 }
820
821 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400822 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400823 Command: "mkdir -p 'one/symlink subdir' && " +
824 "rm -f 'one/symlink subdir/symlink' && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400825 "ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
Liz Kammerc49e6822021-06-08 15:04:11 -0400826 InputPaths: []string{"one/file subdir/file"},
827 OutputPaths: []string{"one/symlink subdir/symlink"},
828 SymlinkPaths: []string{"one/symlink subdir/symlink"},
829 Mnemonic: "SolibSymlink",
830 },
831 }
Liz Kammerc7737782021-11-04 10:56:13 -0400832 assertBuildStatements(t, expectedBuildStatements, actual)
Liz Kammerc49e6822021-06-08 15:04:11 -0400833}
834
835func TestSymlinkMultipleInputs(t *testing.T) {
836 const inputString = `
837{
Jason Wu6fe87212022-10-21 02:57:41 -0400838 "artifacts": [
839 { "id": 1, "path_fragment_id": 1 },
840 { "id": 2, "path_fragment_id": 2 },
841 { "id": 3, "path_fragment_id": 3 }],
842 "actions": [{
843 "target_id": 1,
844 "action_key": "x",
845 "mnemonic": "Symlink",
846 "input_dep_set_ids": [1],
847 "output_ids": [3],
848 "primary_output_id": 3
849 }],
850 "dep_set_of_files": [{ "id": 1, "direct_artifact_ids": [1,2] }],
851 "path_fragments": [
852 { "id": 1, "label": "file" },
853 { "id": 2, "label": "other_file" },
854 { "id": 3, "label": "symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400855}`
856
Jason Wu6fe87212022-10-21 02:57:41 -0400857 data, err := JsonToActionGraphContainer(inputString)
858 if err != nil {
859 t.Error(err)
860 return
861 }
862 _, _, err = AqueryBuildStatements(data)
Liz Kammerc49e6822021-06-08 15:04:11 -0400863 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
864}
865
866func TestSymlinkMultipleOutputs(t *testing.T) {
867 const inputString = `
868{
Jason Wu6fe87212022-10-21 02:57:41 -0400869 "artifacts": [
870 { "id": 1, "path_fragment_id": 1 },
871 { "id": 3, "path_fragment_id": 3 }],
872 "actions": [{
873 "target_id": 1,
874 "action_key": "x",
875 "mnemonic": "Symlink",
876 "input_dep_set_ids": [1],
877 "output_ids": [2,3],
878 "primary_output_id": 2
879 }],
880 "dep_set_of_files": [
881 { "id": 1, "direct_artifact_ids": [1] }],
882 "path_fragments": [
883 { "id": 1, "label": "file" },
884 { "id": 2, "label": "symlink" },
885 { "id": 3, "label": "other_symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400886}`
887
Jason Wu6fe87212022-10-21 02:57:41 -0400888 data, err := JsonToActionGraphContainer(inputString)
889 if err != nil {
890 t.Error(err)
891 return
892 }
893 _, _, err = AqueryBuildStatements(data)
894 assertError(t, err, "undefined outputId 2")
Liz Kammerc49e6822021-06-08 15:04:11 -0400895}
896
Wei Li455ba832021-11-04 22:58:12 +0000897func TestTemplateExpandActionSubstitutions(t *testing.T) {
898 const inputString = `
899{
Jason Wu6fe87212022-10-21 02:57:41 -0400900 "artifacts": [{
901 "id": 1,
902 "path_fragment_id": 1
903 }],
904 "actions": [{
905 "target_id": 1,
906 "action_key": "x",
907 "mnemonic": "TemplateExpand",
908 "configuration_id": 1,
909 "output_ids": [1],
910 "primary_output_id": 1,
911 "execution_platform": "//build/bazel/platforms:linux_x86_64",
912 "template_content": "Test template substitutions: %token1%, %python_binary%",
913 "substitutions": [
914 { "key": "%token1%", "value": "abcd" },
915 { "key": "%python_binary%", "value": "python3" }]
916 }],
917 "path_fragments": [
918 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +0000919}`
920
Jason Wu6fe87212022-10-21 02:57:41 -0400921 data, err := JsonToActionGraphContainer(inputString)
922 if err != nil {
923 t.Error(err)
924 return
925 }
926 actual, _, err := AqueryBuildStatements(data)
Wei Li455ba832021-11-04 22:58:12 +0000927 if err != nil {
928 t.Errorf("Unexpected error %q", err)
929 }
930
931 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400932 {
Wei Li455ba832021-11-04 22:58:12 +0000933 Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
934 "chmod a+x template_file'",
935 OutputPaths: []string{"template_file"},
936 Mnemonic: "TemplateExpand",
937 },
938 }
939 assertBuildStatements(t, expectedBuildStatements, actual)
940}
941
942func TestTemplateExpandActionNoOutput(t *testing.T) {
943 const inputString = `
944{
Jason Wu6fe87212022-10-21 02:57:41 -0400945 "artifacts": [
946 { "id": 1, "path_fragment_id": 1 }],
947 "actions": [{
948 "target_id": 1,
949 "action_key": "x",
950 "mnemonic": "TemplateExpand",
951 "configuration_id": 1,
952 "primary_output_id": 1,
953 "execution_platform": "//build/bazel/platforms:linux_x86_64",
954 "templateContent": "Test template substitutions: %token1%, %python_binary%",
955 "substitutions": [
956 { "key": "%token1%", "value": "abcd" },
957 { "key": "%python_binary%", "value": "python3" }]
958 }],
959 "path_fragments": [
960 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +0000961}`
962
Jason Wu6fe87212022-10-21 02:57:41 -0400963 data, err := JsonToActionGraphContainer(inputString)
964 if err != nil {
965 t.Error(err)
966 return
967 }
968 _, _, err = AqueryBuildStatements(data)
Wei Li455ba832021-11-04 22:58:12 +0000969 assertError(t, err, `Expect 1 output to template expand action, got: output []`)
970}
971
Sasha Smundak1da064c2022-06-08 16:36:16 -0700972func TestFileWrite(t *testing.T) {
973 const inputString = `
974{
Jason Wu6fe87212022-10-21 02:57:41 -0400975 "artifacts": [
976 { "id": 1, "path_fragment_id": 1 }],
977 "actions": [{
978 "target_id": 1,
979 "action_key": "x",
980 "mnemonic": "FileWrite",
981 "configuration_id": 1,
982 "output_ids": [1],
983 "primary_output_id": 1,
984 "execution_platform": "//build/bazel/platforms:linux_x86_64",
985 "file_contents": "file data\n"
986 }],
987 "path_fragments": [
988 { "id": 1, "label": "foo.manifest" }]
Sasha Smundak1da064c2022-06-08 16:36:16 -0700989}
990`
Jason Wu6fe87212022-10-21 02:57:41 -0400991 data, err := JsonToActionGraphContainer(inputString)
992 if err != nil {
993 t.Error(err)
994 return
995 }
996 actual, _, err := AqueryBuildStatements(data)
Sasha Smundak1da064c2022-06-08 16:36:16 -0700997 if err != nil {
998 t.Errorf("Unexpected error %q", err)
999 }
1000 assertBuildStatements(t, []BuildStatement{
1001 {
1002 OutputPaths: []string{"foo.manifest"},
1003 Mnemonic: "FileWrite",
1004 FileContents: "file data\n",
1005 },
1006 }, actual)
1007}
1008
1009func TestSourceSymlinkManifest(t *testing.T) {
1010 const inputString = `
1011{
Jason Wu6fe87212022-10-21 02:57:41 -04001012 "artifacts": [
1013 { "id": 1, "path_fragment_id": 1 }],
1014 "actions": [{
1015 "target_id": 1,
1016 "action_key": "x",
1017 "mnemonic": "SourceSymlinkManifest",
1018 "configuration_id": 1,
1019 "output_ids": [1],
1020 "primary_output_id": 1,
1021 "execution_platform": "//build/bazel/platforms:linux_x86_64",
1022 "file_contents": "symlink target\n"
1023 }],
1024 "path_fragments": [
1025 { "id": 1, "label": "foo.manifest" }]
Sasha Smundak1da064c2022-06-08 16:36:16 -07001026}
1027`
Jason Wu6fe87212022-10-21 02:57:41 -04001028 data, err := JsonToActionGraphContainer(inputString)
1029 if err != nil {
1030 t.Error(err)
1031 return
1032 }
1033 actual, _, err := AqueryBuildStatements(data)
Sasha Smundak1da064c2022-06-08 16:36:16 -07001034 if err != nil {
1035 t.Errorf("Unexpected error %q", err)
1036 }
1037 assertBuildStatements(t, []BuildStatement{
1038 {
1039 OutputPaths: []string{"foo.manifest"},
1040 Mnemonic: "SourceSymlinkManifest",
1041 },
1042 }, actual)
1043}
1044
Chris Parsons4f069892021-01-15 12:22:41 -05001045func assertError(t *testing.T, err error, expected string) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001046 t.Helper()
Chris Parsons943f2432021-01-19 11:36:50 -05001047 if err == nil {
1048 t.Errorf("expected error '%s', but got no error", expected)
1049 } else if err.Error() != expected {
Liz Kammerc49e6822021-06-08 15:04:11 -04001050 t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
Chris Parsons4f069892021-01-15 12:22:41 -05001051 }
1052}
1053
1054// Asserts that the given actual build statements match the given expected build statements.
1055// Build statement equivalence is determined using buildStatementEquals.
1056func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
Liz Kammerc49e6822021-06-08 15:04:11 -04001057 t.Helper()
Chris Parsons4f069892021-01-15 12:22:41 -05001058 if len(expected) != len(actual) {
Liz Kammerc3192992021-11-16 17:01:11 -05001059 t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
Chris Parsons4f069892021-01-15 12:22:41 -05001060 len(expected), len(actual), expected, actual)
1061 return
1062 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001063 type compareFn = func(i int, j int) bool
1064 byCommand := func(slice []BuildStatement) compareFn {
1065 return func(i int, j int) bool {
1066 return slice[i].Command < slice[j].Command
Chris Parsons4f069892021-01-15 12:22:41 -05001067 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001068 }
1069 sort.SliceStable(expected, byCommand(expected))
1070 sort.SliceStable(actual, byCommand(actual))
1071 for i, actualStatement := range actual {
1072 expectedStatement := expected[i]
1073 if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
1074 t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
Usta Shresthaef922252022-06-02 14:23:02 -04001075 differingField, actualStatement, expectedStatement)
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001076 return
1077 }
Chris Parsons4f069892021-01-15 12:22:41 -05001078 }
1079}
1080
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001081func buildStatementEquals(first BuildStatement, second BuildStatement) string {
Chris Parsons4f069892021-01-15 12:22:41 -05001082 if first.Mnemonic != second.Mnemonic {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001083 return "Mnemonic"
Chris Parsons4f069892021-01-15 12:22:41 -05001084 }
1085 if first.Command != second.Command {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001086 return "Command"
Chris Parsons4f069892021-01-15 12:22:41 -05001087 }
1088 // Ordering is significant for environment variables.
1089 if !reflect.DeepEqual(first.Env, second.Env) {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001090 return "Env"
Chris Parsons4f069892021-01-15 12:22:41 -05001091 }
1092 // Ordering is irrelevant for input and output paths, so compare sets.
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001093 if !reflect.DeepEqual(sortedStrings(first.InputPaths), sortedStrings(second.InputPaths)) {
1094 return "InputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001095 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001096 if !reflect.DeepEqual(sortedStrings(first.OutputPaths), sortedStrings(second.OutputPaths)) {
1097 return "OutputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001098 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001099 if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
1100 return "SymlinkPaths"
Liz Kammerc49e6822021-06-08 15:04:11 -04001101 }
1102 if first.Depfile != second.Depfile {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001103 return "Depfile"
Liz Kammerc49e6822021-06-08 15:04:11 -04001104 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001105 return ""
Chris Parsons4f069892021-01-15 12:22:41 -05001106}
1107
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001108func sortedStrings(stringSlice []string) []string {
1109 sorted := make([]string, len(stringSlice))
1110 copy(sorted, stringSlice)
1111 sort.Strings(sorted)
1112 return sorted
Chris Parsons4f069892021-01-15 12:22:41 -05001113}
Jason Wu6fe87212022-10-21 02:57:41 -04001114
1115// Transform the json format to ActionGraphContainer
1116func JsonToActionGraphContainer(inputString string) ([]byte, error) {
1117 var aqueryProtoResult analysis_v2_proto.ActionGraphContainer
1118 err := json.Unmarshal([]byte(inputString), &aqueryProtoResult)
1119 if err != nil {
1120 return []byte(""), err
1121 }
1122 data, _ := proto.Marshal(&aqueryProtoResult)
1123 return data, err
1124}