blob: 3a2bf0f38d8a8f716e1308013d74fb758834faad [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 (
18 "fmt"
19 "reflect"
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -040020 "sort"
Chris Parsons4f069892021-01-15 12:22:41 -050021 "testing"
22)
23
24func TestAqueryMultiArchGenrule(t *testing.T) {
25 // This input string is retrieved from a real build of bionic-related genrules.
26 const inputString = `
27{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -070028 "artifacts": [
29 { "id": 1, "pathFragmentId": 1 },
30 { "id": 2, "pathFragmentId": 6 },
31 { "id": 3, "pathFragmentId": 8 },
32 { "id": 4, "pathFragmentId": 12 },
33 { "id": 5, "pathFragmentId": 19 },
34 { "id": 6, "pathFragmentId": 20 },
35 { "id": 7, "pathFragmentId": 21 }],
Chris Parsons4f069892021-01-15 12:22:41 -050036 "actions": [{
37 "targetId": 1,
38 "actionKey": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
39 "mnemonic": "Genrule",
40 "configurationId": 1,
41 "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"],
42 "environmentVariables": [{
43 "key": "PATH",
44 "value": "/bin:/usr/bin:/usr/local/bin"
45 }],
46 "inputDepSetIds": [1],
47 "outputIds": [4],
48 "primaryOutputId": 4
49 }, {
50 "targetId": 2,
51 "actionKey": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
52 "mnemonic": "Genrule",
53 "configurationId": 1,
54 "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"],
55 "environmentVariables": [{
56 "key": "PATH",
57 "value": "/bin:/usr/bin:/usr/local/bin"
58 }],
59 "inputDepSetIds": [2],
60 "outputIds": [5],
61 "primaryOutputId": 5
62 }, {
63 "targetId": 3,
64 "actionKey": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
65 "mnemonic": "Genrule",
66 "configurationId": 1,
67 "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"],
68 "environmentVariables": [{
69 "key": "PATH",
70 "value": "/bin:/usr/bin:/usr/local/bin"
71 }],
72 "inputDepSetIds": [3],
73 "outputIds": [6],
74 "primaryOutputId": 6
75 }, {
76 "targetId": 4,
77 "actionKey": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
78 "mnemonic": "Genrule",
79 "configurationId": 1,
80 "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"],
81 "environmentVariables": [{
82 "key": "PATH",
83 "value": "/bin:/usr/bin:/usr/local/bin"
84 }],
85 "inputDepSetIds": [4],
86 "outputIds": [7],
87 "primaryOutputId": 7
88 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -070089 "targets": [
90 { "id": 1, "label": "@sourceroot//bionic/libc:syscalls-arm", "ruleClassId": 1 },
91 { "id": 2, "label": "@sourceroot//bionic/libc:syscalls-x86", "ruleClassId": 1 },
92 { "id": 3, "label": "@sourceroot//bionic/libc:syscalls-x86_64", "ruleClassId": 1 },
93 { "id": 4, "label": "@sourceroot//bionic/libc:syscalls-arm64", "ruleClassId": 1 }],
94 "depSetOfFiles": [
95 { "id": 1, "directArtifactIds": [1, 2, 3] },
96 { "id": 2, "directArtifactIds": [1, 2, 3] },
97 { "id": 3, "directArtifactIds": [1, 2, 3] },
98 { "id": 4, "directArtifactIds": [1, 2, 3] }],
Chris Parsons4f069892021-01-15 12:22:41 -050099 "configuration": [{
100 "id": 1,
101 "mnemonic": "k8-fastbuild",
102 "platformName": "k8",
103 "checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
104 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700105 "ruleClasses": [{ "id": 1, "name": "genrule"}],
106 "pathFragments": [
107 { "id": 5, "label": ".." },
108 { "id": 4, "label": "sourceroot", "parentId": 5 },
109 { "id": 3, "label": "bionic", "parentId": 4 },
110 { "id": 2, "label": "libc", "parentId": 3 },
111 { "id": 1, "label": "SYSCALLS.TXT", "parentId": 2 },
112 { "id": 7, "label": "tools", "parentId": 2 },
113 { "id": 6, "label": "gensyscalls.py", "parentId": 7 },
114 { "id": 11, "label": "bazel_tools", "parentId": 5 },
115 { "id": 10, "label": "tools", "parentId": 11 },
116 { "id": 9, "label": "genrule", "parentId": 10 },
117 { "id": 8, "label": "genrule-setup.sh", "parentId": 9 },
118 { "id": 18, "label": "bazel-out" },
119 { "id": 17, "label": "sourceroot", "parentId": 18 },
120 { "id": 16, "label": "k8-fastbuild", "parentId": 17 },
121 { "id": 15, "label": "bin", "parentId": 16 },
122 { "id": 14, "label": "bionic", "parentId": 15 },
123 { "id": 13, "label": "libc", "parentId": 14 },
124 { "id": 12, "label": "syscalls-arm.S", "parentId": 13 },
125 { "id": 19, "label": "syscalls-x86.S", "parentId": 13 },
126 { "id": 20, "label": "syscalls-x86_64.S", "parentId": 13 },
127 { "id": 21, "label": "syscalls-arm64.S", "parentId": 13 }]
Chris Parsons4f069892021-01-15 12:22:41 -0500128}`
Chris Parsons1a7aca02022-04-25 22:35:15 -0400129 actualbuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
Usta Shrestha16ac1352022-06-22 11:01:55 -0400130 var expectedBuildStatements []BuildStatement
Chris Parsons4f069892021-01-15 12:22:41 -0500131 for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
132 expectedBuildStatements = append(expectedBuildStatements,
133 BuildStatement{
134 Command: fmt.Sprintf(
135 "/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'",
136 arch, arch),
137 OutputPaths: []string{
138 fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
139 },
Chris Parsons4f069892021-01-15 12:22:41 -0500140 Env: []KeyValuePair{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400141 {Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
Chris Parsons4f069892021-01-15 12:22:41 -0500142 },
143 Mnemonic: "Genrule",
144 })
145 }
146 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400147
148 expectedFlattenedInputs := []string{
149 "../sourceroot/bionic/libc/SYSCALLS.TXT",
150 "../sourceroot/bionic/libc/tools/gensyscalls.py",
Chris Parsons1a7aca02022-04-25 22:35:15 -0400151 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400152 // In this example, each depset should have the same expected inputs.
153 for _, actualDepset := range actualDepsets {
154 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
155 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
156 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
157 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400158 }
Chris Parsons4f069892021-01-15 12:22:41 -0500159}
160
161func TestInvalidOutputId(t *testing.T) {
162 const inputString = `
163{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700164 "artifacts": [
165 { "id": 1, "pathFragmentId": 1 },
166 { "id": 2, "pathFragmentId": 2 }],
Chris Parsons4f069892021-01-15 12:22:41 -0500167 "actions": [{
168 "targetId": 1,
169 "actionKey": "x",
170 "mnemonic": "x",
171 "arguments": ["touch", "foo"],
172 "inputDepSetIds": [1],
173 "outputIds": [3],
174 "primaryOutputId": 3
175 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700176 "depSetOfFiles": [
177 { "id": 1, "directArtifactIds": [1, 2] }],
178 "pathFragments": [
179 { "id": 1, "label": "one" },
180 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500181}`
182
Chris Parsons1a7aca02022-04-25 22:35:15 -0400183 _, _, err := AqueryBuildStatements([]byte(inputString))
Chris Parsons4f069892021-01-15 12:22:41 -0500184 assertError(t, err, "undefined outputId 3")
185}
186
Chris Parsons1a7aca02022-04-25 22:35:15 -0400187func TestInvalidInputDepsetIdFromAction(t *testing.T) {
Chris Parsons4f069892021-01-15 12:22:41 -0500188 const inputString = `
189{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700190 "artifacts": [
191 { "id": 1, "pathFragmentId": 1 },
192 { "id": 2, "pathFragmentId": 2 }],
Chris Parsons4f069892021-01-15 12:22:41 -0500193 "actions": [{
194 "targetId": 1,
195 "actionKey": "x",
196 "mnemonic": "x",
197 "arguments": ["touch", "foo"],
198 "inputDepSetIds": [2],
199 "outputIds": [1],
200 "primaryOutputId": 1
201 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700202 "depSetOfFiles": [
203 { "id": 1, "directArtifactIds": [1, 2] }],
204 "pathFragments": [
205 { "id": 1, "label": "one" },
206 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500207}`
208
Chris Parsons1a7aca02022-04-25 22:35:15 -0400209 _, _, err := AqueryBuildStatements([]byte(inputString))
Chris Parsons4f069892021-01-15 12:22:41 -0500210 assertError(t, err, "undefined input depsetId 2")
211}
212
Chris Parsons1a7aca02022-04-25 22:35:15 -0400213func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
214 const inputString = `
215{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700216 "artifacts": [
217 { "id": 1, "pathFragmentId": 1 },
218 { "id": 2, "pathFragmentId": 2 }],
Chris Parsons1a7aca02022-04-25 22:35:15 -0400219 "actions": [{
220 "targetId": 1,
221 "actionKey": "x",
222 "mnemonic": "x",
223 "arguments": ["touch", "foo"],
224 "inputDepSetIds": [1],
225 "outputIds": [1],
226 "primaryOutputId": 1
227 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700228 "depSetOfFiles": [
229 { "id": 1, "directArtifactIds": [1, 2], "transitiveDepSetIds": [42] }],
230 "pathFragments": [
231 { "id": 1, "label": "one"},
232 { "id": 2, "label": "two" }]
Chris Parsons1a7aca02022-04-25 22:35:15 -0400233}`
234
235 _, _, err := AqueryBuildStatements([]byte(inputString))
236 assertError(t, err, "undefined input depsetId 42 (referenced by depsetId 1)")
237}
238
Chris Parsons4f069892021-01-15 12:22:41 -0500239func TestInvalidInputArtifactId(t *testing.T) {
240 const inputString = `
241{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700242 "artifacts": [
243 { "id": 1, "pathFragmentId": 1 },
244 { "id": 2, "pathFragmentId": 2 }],
Chris Parsons4f069892021-01-15 12:22:41 -0500245 "actions": [{
246 "targetId": 1,
247 "actionKey": "x",
248 "mnemonic": "x",
249 "arguments": ["touch", "foo"],
250 "inputDepSetIds": [1],
251 "outputIds": [1],
252 "primaryOutputId": 1
253 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700254 "depSetOfFiles": [
255 { "id": 1, "directArtifactIds": [1, 3] }],
256 "pathFragments": [
257 { "id": 1, "label": "one" },
258 { "id": 2, "label": "two" }]
Chris Parsons4f069892021-01-15 12:22:41 -0500259}`
260
Chris Parsons1a7aca02022-04-25 22:35:15 -0400261 _, _, err := AqueryBuildStatements([]byte(inputString))
Chris Parsons4f069892021-01-15 12:22:41 -0500262 assertError(t, err, "undefined input artifactId 3")
263}
264
265func TestInvalidPathFragmentId(t *testing.T) {
266 const inputString = `
267{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700268 "artifacts": [
269 { "id": 1, "pathFragmentId": 1 },
270 { "id": 2, "pathFragmentId": 2 }],
Chris Parsons4f069892021-01-15 12:22:41 -0500271 "actions": [{
272 "targetId": 1,
273 "actionKey": "x",
274 "mnemonic": "x",
275 "arguments": ["touch", "foo"],
276 "inputDepSetIds": [1],
277 "outputIds": [1],
278 "primaryOutputId": 1
279 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700280 "depSetOfFiles": [
281 { "id": 1, "directArtifactIds": [1, 2] }],
282 "pathFragments": [
283 { "id": 1, "label": "one" },
284 { "id": 2, "label": "two", "parentId": 3 }]
Chris Parsons4f069892021-01-15 12:22:41 -0500285}`
286
Chris Parsons1a7aca02022-04-25 22:35:15 -0400287 _, _, err := AqueryBuildStatements([]byte(inputString))
Chris Parsons4f069892021-01-15 12:22:41 -0500288 assertError(t, err, "undefined path fragment id 3")
289}
290
Liz Kammerde116852021-03-25 16:42:37 -0400291func TestDepfiles(t *testing.T) {
292 const inputString = `
293{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700294 "artifacts": [
295 { "id": 1, "pathFragmentId": 1 },
296 { "id": 2, "pathFragmentId": 2 },
297 { "id": 3, "pathFragmentId": 3 }],
Liz Kammerde116852021-03-25 16:42:37 -0400298 "actions": [{
299 "targetId": 1,
300 "actionKey": "x",
301 "mnemonic": "x",
302 "arguments": ["touch", "foo"],
303 "inputDepSetIds": [1],
304 "outputIds": [2, 3],
305 "primaryOutputId": 2
306 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700307 "depSetOfFiles": [
308 { "id": 1, "directArtifactIds": [1, 2, 3] }],
309 "pathFragments": [
310 { "id": 1, "label": "one" },
311 { "id": 2, "label": "two" },
312 { "id": 3, "label": "two.d" }]
Liz Kammerde116852021-03-25 16:42:37 -0400313}`
314
Chris Parsons1a7aca02022-04-25 22:35:15 -0400315 actual, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerde116852021-03-25 16:42:37 -0400316 if err != nil {
317 t.Errorf("Unexpected error %q", err)
318 }
319 if expected := 1; len(actual) != expected {
320 t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
321 }
322
323 bs := actual[0]
324 expectedDepfile := "two.d"
325 if bs.Depfile == nil {
326 t.Errorf("Expected depfile %q, but there was none found", expectedDepfile)
327 } else if *bs.Depfile != expectedDepfile {
328 t.Errorf("Expected depfile %q, but got %q", expectedDepfile, *bs.Depfile)
329 }
330}
331
332func TestMultipleDepfiles(t *testing.T) {
333 const inputString = `
334{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700335 "artifacts": [
336 { "id": 1, "pathFragmentId": 1 },
337 { "id": 2, "pathFragmentId": 2 },
338 { "id": 3, "pathFragmentId": 3 },
339 { "id": 4, "pathFragmentId": 4 }],
Liz Kammerde116852021-03-25 16:42:37 -0400340 "actions": [{
341 "targetId": 1,
342 "actionKey": "x",
343 "mnemonic": "x",
344 "arguments": ["touch", "foo"],
345 "inputDepSetIds": [1],
346 "outputIds": [2,3,4],
347 "primaryOutputId": 2
348 }],
349 "depSetOfFiles": [{
350 "id": 1,
351 "directArtifactIds": [1, 2, 3, 4]
352 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700353 "pathFragments": [
354 { "id": 1, "label": "one" },
355 { "id": 2, "label": "two" },
356 { "id": 3, "label": "two.d" },
357 { "id": 4, "label": "other.d" }]
Liz Kammerde116852021-03-25 16:42:37 -0400358}`
359
Chris Parsons1a7aca02022-04-25 22:35:15 -0400360 _, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerde116852021-03-25 16:42:37 -0400361 assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
362}
363
Chris Parsons943f2432021-01-19 11:36:50 -0500364func TestTransitiveInputDepsets(t *testing.T) {
365 // The input aquery for this test comes from a proof-of-concept starlark rule which registers
366 // a single action with many inputs given via a deep depset.
367 const inputString = `
368{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700369 "artifacts": [
370 { "id": 1, "pathFragmentId": 1 },
371 { "id": 2, "pathFragmentId": 7 },
372 { "id": 3, "pathFragmentId": 8 },
373 { "id": 4, "pathFragmentId": 9 },
374 { "id": 5, "pathFragmentId": 10 },
375 { "id": 6, "pathFragmentId": 11 },
376 { "id": 7, "pathFragmentId": 12 },
377 { "id": 8, "pathFragmentId": 13 },
378 { "id": 9, "pathFragmentId": 14 },
379 { "id": 10, "pathFragmentId": 15 },
380 { "id": 11, "pathFragmentId": 16 },
381 { "id": 12, "pathFragmentId": 17 },
382 { "id": 13, "pathFragmentId": 18 },
383 { "id": 14, "pathFragmentId": 19 },
384 { "id": 15, "pathFragmentId": 20 },
385 { "id": 16, "pathFragmentId": 21 },
386 { "id": 17, "pathFragmentId": 22 },
387 { "id": 18, "pathFragmentId": 23 },
388 { "id": 19, "pathFragmentId": 24 },
389 { "id": 20, "pathFragmentId": 25 },
390 { "id": 21, "pathFragmentId": 26 }],
Chris Parsons943f2432021-01-19 11:36:50 -0500391 "actions": [{
392 "targetId": 1,
393 "actionKey": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
394 "mnemonic": "Action",
395 "configurationId": 1,
396 "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"],
397 "inputDepSetIds": [1],
398 "outputIds": [21],
399 "primaryOutputId": 21
400 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700401 "depSetOfFiles": [
402 { "id": 3, "directArtifactIds": [1, 2, 3, 4, 5] },
403 { "id": 4, "directArtifactIds": [6, 7, 8, 9, 10] },
404 { "id": 2, "transitiveDepSetIds": [3, 4], "directArtifactIds": [11, 12, 13, 14, 15] },
405 { "id": 5, "directArtifactIds": [16, 17, 18, 19] },
406 { "id": 1, "transitiveDepSetIds": [2, 5], "directArtifactIds": [20] }],
407 "pathFragments": [
408 { "id": 6, "label": "bazel-out" },
409 { "id": 5, "label": "sourceroot", "parentId": 6 },
410 { "id": 4, "label": "k8-fastbuild", "parentId": 5 },
411 { "id": 3, "label": "bin", "parentId": 4 },
412 { "id": 2, "label": "testpkg", "parentId": 3 },
413 { "id": 1, "label": "test_1", "parentId": 2 },
414 { "id": 7, "label": "test_2", "parentId": 2 },
415 { "id": 8, "label": "test_3", "parentId": 2 },
416 { "id": 9, "label": "test_4", "parentId": 2 },
417 { "id": 10, "label": "test_5", "parentId": 2 },
418 { "id": 11, "label": "test_6", "parentId": 2 },
419 { "id": 12, "label": "test_7", "parentId": 2 },
420 { "id": 13, "label": "test_8", "parentId": 2 },
421 { "id": 14, "label": "test_9", "parentId": 2 },
422 { "id": 15, "label": "test_10", "parentId": 2 },
423 { "id": 16, "label": "test_11", "parentId": 2 },
424 { "id": 17, "label": "test_12", "parentId": 2 },
425 { "id": 18, "label": "test_13", "parentId": 2 },
426 { "id": 19, "label": "test_14", "parentId": 2 },
427 { "id": 20, "label": "test_15", "parentId": 2 },
428 { "id": 21, "label": "test_16", "parentId": 2 },
429 { "id": 22, "label": "test_17", "parentId": 2 },
430 { "id": 23, "label": "test_18", "parentId": 2 },
431 { "id": 24, "label": "test_19", "parentId": 2 },
432 { "id": 25, "label": "test_root", "parentId": 2 },
433 { "id": 26,"label": "test_out", "parentId": 2 }]
Chris Parsons943f2432021-01-19 11:36:50 -0500434}`
435
Chris Parsons1a7aca02022-04-25 22:35:15 -0400436 actualbuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
437
Chris Parsons943f2432021-01-19 11:36:50 -0500438 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400439 {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400440 Command: "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
441 OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
442 Mnemonic: "Action",
Chris Parsons943f2432021-01-19 11:36:50 -0500443 },
444 }
445 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400446
447 // Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
448 // are given via a deep depset, but the depset is flattened when returned as a
449 // BuildStatement slice.
Usta Shrestha16ac1352022-06-22 11:01:55 -0400450 var expectedFlattenedInputs []string
Chris Parsons1a7aca02022-04-25 22:35:15 -0400451 for i := 1; i < 20; i++ {
452 expectedFlattenedInputs = append(expectedFlattenedInputs, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
453 }
454 expectedFlattenedInputs = append(expectedFlattenedInputs, "bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root")
455
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400456 actualDepsetHashes := actualbuildStatements[0].InputDepsetHashes
457 actualFlattenedInputs := flattenDepsets(actualDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400458 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
459 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
460 }
Chris Parsons943f2432021-01-19 11:36:50 -0500461}
462
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700463func TestSymlinkTree(t *testing.T) {
464 const inputString = `
465{
466 "artifacts": [
467 { "id": 1, "pathFragmentId": 1 },
468 { "id": 2, "pathFragmentId": 2 }],
469 "actions": [{
470 "targetId": 1,
471 "actionKey": "x",
472 "mnemonic": "SymlinkTree",
473 "configurationId": 1,
474 "inputDepSetIds": [1],
475 "outputIds": [2],
476 "primaryOutputId": 2,
477 "executionPlatform": "//build/bazel/platforms:linux_x86_64"
478 }],
479 "pathFragments": [
480 { "id": 1, "label": "foo.manifest" },
481 { "id": 2, "label": "foo.runfiles/MANIFEST" }],
482 "depSetOfFiles": [
483 { "id": 1, "directArtifactIds": [1] }]
484}
485`
486 actual, _, err := AqueryBuildStatements([]byte(inputString))
487 if err != nil {
488 t.Errorf("Unexpected error %q", err)
489 }
490 assertBuildStatements(t, []BuildStatement{
491 {
492 Command: "",
493 OutputPaths: []string{"foo.runfiles/MANIFEST"},
494 Mnemonic: "SymlinkTree",
495 InputPaths: []string{"foo.manifest"},
496 },
497 }, actual)
498}
499
Usta Shresthaef922252022-06-02 14:23:02 -0400500func TestBazelOutRemovalFromInputDepsets(t *testing.T) {
501 const inputString = `{
502 "artifacts": [{
503 "id": 1,
504 "pathFragmentId": 10
505 }, {
506 "id": 2,
507 "pathFragmentId": 20
508 }, {
509 "id": 3,
510 "pathFragmentId": 30
511 }, {
512 "id": 4,
513 "pathFragmentId": 40
514 }],
515 "depSetOfFiles": [{
516 "id": 1111,
517 "directArtifactIds": [3 , 4]
518 }],
519 "actions": [{
520 "targetId": 100,
521 "actionKey": "x",
522 "inputDepSetIds": [1111],
523 "mnemonic": "x",
524 "arguments": ["bogus", "command"],
525 "outputIds": [2],
526 "primaryOutputId": 1
527 }],
528 "pathFragments": [{
529 "id": 10,
530 "label": "input"
531 }, {
532 "id": 20,
533 "label": "output"
534 }, {
535 "id": 30,
536 "label": "dep1",
537 "parentId": 50
538 }, {
539 "id": 40,
540 "label": "dep2",
541 "parentId": 60
542 }, {
543 "id": 50,
544 "label": "bazel_tools",
545 "parentId": 60
546 }, {
547 "id": 60,
548 "label": ".."
549 }]
550}`
551 actualBuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
552 if len(actualDepsets) != 1 {
553 t.Errorf("expected 1 depset but found %#v", actualDepsets)
554 return
555 }
556 dep2Found := false
557 for _, dep := range flattenDepsets([]string{actualDepsets[0].ContentHash}, actualDepsets) {
558 if dep == "../bazel_tools/dep1" {
559 t.Errorf("dependency %s expected to be removed but still exists", dep)
560 } else if dep == "../dep2" {
561 dep2Found = true
562 }
563 }
564 if !dep2Found {
565 t.Errorf("dependency ../dep2 expected but not found")
566 }
567
568 expectedBuildStatement := BuildStatement{
569 Command: "bogus command",
570 OutputPaths: []string{"output"},
571 Mnemonic: "x",
572 }
573 buildStatementFound := false
574 for _, actualBuildStatement := range actualBuildStatements {
575 if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
576 buildStatementFound = true
577 break
578 }
579 }
580 if !buildStatementFound {
581 t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
582 return
583 }
584}
585
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400586func TestMiddlemenAction(t *testing.T) {
587 const inputString = `
588{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700589 "artifacts": [
590 { "id": 1, "pathFragmentId": 1 },
591 { "id": 2, "pathFragmentId": 2 },
592 { "id": 3, "pathFragmentId": 3 },
593 { "id": 4, "pathFragmentId": 4 },
594 { "id": 5, "pathFragmentId": 5 },
595 { "id": 6, "pathFragmentId": 6 }],
596 "pathFragments": [
597 { "id": 1, "label": "middleinput_one" },
598 { "id": 2, "label": "middleinput_two" },
599 { "id": 3, "label": "middleman_artifact" },
600 { "id": 4, "label": "maininput_one" },
601 { "id": 5, "label": "maininput_two" },
602 { "id": 6, "label": "output" }],
603 "depSetOfFiles": [
604 { "id": 1, "directArtifactIds": [1, 2] },
605 { "id": 2, "directArtifactIds": [3, 4, 5] }],
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400606 "actions": [{
607 "targetId": 1,
608 "actionKey": "x",
609 "mnemonic": "Middleman",
610 "arguments": ["touch", "foo"],
611 "inputDepSetIds": [1],
612 "outputIds": [3],
613 "primaryOutputId": 3
614 }, {
615 "targetId": 2,
616 "actionKey": "y",
617 "mnemonic": "Main action",
618 "arguments": ["touch", "foo"],
619 "inputDepSetIds": [2],
620 "outputIds": [6],
621 "primaryOutputId": 6
622 }]
623}`
624
Chris Parsons1a7aca02022-04-25 22:35:15 -0400625 actualBuildStatements, actualDepsets, err := AqueryBuildStatements([]byte(inputString))
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400626 if err != nil {
627 t.Errorf("Unexpected error %q", err)
628 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400629 if expected := 1; len(actualBuildStatements) != expected {
630 t.Fatalf("Expected %d build statements, got %d", expected, len(actualBuildStatements))
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400631 }
632
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400633 expectedDepsetFiles := [][]string{
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400634 {"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"},
635 {"middleinput_one", "middleinput_two"},
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400636 }
637 assertFlattenedDepsets(t, actualDepsets, expectedDepsetFiles)
638
Chris Parsons1a7aca02022-04-25 22:35:15 -0400639 bs := actualBuildStatements[0]
640 if len(bs.InputPaths) > 0 {
641 t.Errorf("Expected main action raw inputs to be empty, but got %q", bs.InputPaths)
642 }
643
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400644 expectedOutputs := []string{"output"}
645 if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) {
646 t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths)
647 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400648
Chris Parsons1a7aca02022-04-25 22:35:15 -0400649 expectedFlattenedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400650 actualFlattenedInputs := flattenDepsets(bs.InputDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400651
652 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
653 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
654 }
655}
656
657// Returns the contents of given depsets in concatenated post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400658func flattenDepsets(depsetHashesToFlatten []string, allDepsets []AqueryDepset) []string {
659 depsetsByHash := map[string]AqueryDepset{}
Chris Parsons1a7aca02022-04-25 22:35:15 -0400660 for _, depset := range allDepsets {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400661 depsetsByHash[depset.ContentHash] = depset
Chris Parsons1a7aca02022-04-25 22:35:15 -0400662 }
Usta Shrestha16ac1352022-06-22 11:01:55 -0400663 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400664 for _, depsetId := range depsetHashesToFlatten {
665 result = append(result, flattenDepset(depsetId, depsetsByHash)...)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400666 }
667 return result
668}
669
670// Returns the contents of a given depset in post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400671func flattenDepset(depsetHashToFlatten string, allDepsets map[string]AqueryDepset) []string {
672 depset := allDepsets[depsetHashToFlatten]
Usta Shrestha16ac1352022-06-22 11:01:55 -0400673 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400674 for _, depsetId := range depset.TransitiveDepSetHashes {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400675 result = append(result, flattenDepset(depsetId, allDepsets)...)
676 }
677 result = append(result, depset.DirectArtifacts...)
678 return result
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400679}
680
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400681func assertFlattenedDepsets(t *testing.T, actualDepsets []AqueryDepset, expectedDepsetFiles [][]string) {
682 t.Helper()
683 if len(actualDepsets) != len(expectedDepsetFiles) {
Sasha Smundakf10c3ac2022-06-08 11:46:31 -0700684 t.Errorf("Expected %d depsets, but got %d depsets", len(expectedDepsetFiles), len(actualDepsets))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400685 }
686 for i, actualDepset := range actualDepsets {
687 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
688 if !reflect.DeepEqual(actualFlattenedInputs, expectedDepsetFiles[i]) {
689 t.Errorf("Expected depset files: %v, but got %v", expectedDepsetFiles[i], actualFlattenedInputs)
690 }
691 }
692}
693
Liz Kammerc49e6822021-06-08 15:04:11 -0400694func TestSimpleSymlink(t *testing.T) {
695 const inputString = `
696{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700697 "artifacts": [
698 { "id": 1, "pathFragmentId": 3 },
699 { "id": 2, "pathFragmentId": 5 }],
Liz Kammerc49e6822021-06-08 15:04:11 -0400700 "actions": [{
701 "targetId": 1,
702 "actionKey": "x",
703 "mnemonic": "Symlink",
704 "inputDepSetIds": [1],
705 "outputIds": [2],
706 "primaryOutputId": 2
707 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700708 "depSetOfFiles": [
709 { "id": 1, "directArtifactIds": [1] }],
710 "pathFragments": [
711 { "id": 1, "label": "one" },
712 { "id": 2, "label": "file_subdir", "parentId": 1 },
713 { "id": 3, "label": "file", "parentId": 2 },
714 { "id": 4, "label": "symlink_subdir", "parentId": 1 },
715 { "id": 5, "label": "symlink", "parentId": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400716}`
717
Chris Parsons1a7aca02022-04-25 22:35:15 -0400718 actual, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerc49e6822021-06-08 15:04:11 -0400719
720 if err != nil {
721 t.Errorf("Unexpected error %q", err)
722 }
723
724 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400725 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400726 Command: "mkdir -p one/symlink_subdir && " +
727 "rm -f one/symlink_subdir/symlink && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400728 "ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
Liz Kammerc49e6822021-06-08 15:04:11 -0400729 InputPaths: []string{"one/file_subdir/file"},
730 OutputPaths: []string{"one/symlink_subdir/symlink"},
731 SymlinkPaths: []string{"one/symlink_subdir/symlink"},
732 Mnemonic: "Symlink",
733 },
734 }
735 assertBuildStatements(t, actual, expectedBuildStatements)
736}
737
738func TestSymlinkQuotesPaths(t *testing.T) {
739 const inputString = `
740{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700741 "artifacts": [
742 { "id": 1, "pathFragmentId": 3 },
743 { "id": 2, "pathFragmentId": 5 }],
Liz Kammerc49e6822021-06-08 15:04:11 -0400744 "actions": [{
745 "targetId": 1,
746 "actionKey": "x",
747 "mnemonic": "SolibSymlink",
748 "inputDepSetIds": [1],
749 "outputIds": [2],
750 "primaryOutputId": 2
751 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700752 "depSetOfFiles": [
753 { "id": 1, "directArtifactIds": [1] }],
754 "pathFragments": [
755 { "id": 1, "label": "one" },
756 { "id": 2, "label": "file subdir", "parentId": 1 },
757 { "id": 3, "label": "file", "parentId": 2 },
758 { "id": 4, "label": "symlink subdir", "parentId": 1 },
759 { "id": 5, "label": "symlink", "parentId": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400760}`
761
Chris Parsons1a7aca02022-04-25 22:35:15 -0400762 actual, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerc49e6822021-06-08 15:04:11 -0400763
764 if err != nil {
765 t.Errorf("Unexpected error %q", err)
766 }
767
768 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400769 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400770 Command: "mkdir -p 'one/symlink subdir' && " +
771 "rm -f 'one/symlink subdir/symlink' && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400772 "ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
Liz Kammerc49e6822021-06-08 15:04:11 -0400773 InputPaths: []string{"one/file subdir/file"},
774 OutputPaths: []string{"one/symlink subdir/symlink"},
775 SymlinkPaths: []string{"one/symlink subdir/symlink"},
776 Mnemonic: "SolibSymlink",
777 },
778 }
Liz Kammerc7737782021-11-04 10:56:13 -0400779 assertBuildStatements(t, expectedBuildStatements, actual)
Liz Kammerc49e6822021-06-08 15:04:11 -0400780}
781
782func TestSymlinkMultipleInputs(t *testing.T) {
783 const inputString = `
784{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700785 "artifacts": [
786 { "id": 1, "pathFragmentId": 1 },
787 { "id": 2, "pathFragmentId": 2 },
788 { "id": 3, "pathFragmentId": 3 }],
Liz Kammerc49e6822021-06-08 15:04:11 -0400789 "actions": [{
790 "targetId": 1,
791 "actionKey": "x",
792 "mnemonic": "Symlink",
793 "inputDepSetIds": [1],
794 "outputIds": [3],
795 "primaryOutputId": 3
796 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700797 "depSetOfFiles": [{ "id": 1, "directArtifactIds": [1,2] }],
798 "pathFragments": [
799 { "id": 1, "label": "file" },
800 { "id": 2, "label": "other_file" },
801 { "id": 3, "label": "symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400802}`
803
Chris Parsons1a7aca02022-04-25 22:35:15 -0400804 _, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerc49e6822021-06-08 15:04:11 -0400805 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
806}
807
808func TestSymlinkMultipleOutputs(t *testing.T) {
809 const inputString = `
810{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700811 "artifacts": [
812 { "id": 1, "pathFragmentId": 1 },
813 { "id": 2, "pathFragmentId": 2 },
814 { "id": 3, "pathFragmentId": 3 }],
Liz Kammerc49e6822021-06-08 15:04:11 -0400815 "actions": [{
816 "targetId": 1,
817 "actionKey": "x",
818 "mnemonic": "Symlink",
819 "inputDepSetIds": [1],
820 "outputIds": [2,3],
821 "primaryOutputId": 2
822 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700823 "depSetOfFiles": [
824 { "id": 1, "directArtifactIds": [1] }],
825 "pathFragments": [
826 { "id": 1, "label": "file" },
827 { "id": 2, "label": "symlink" },
828 { "id": 3, "label": "other_symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400829}`
830
Chris Parsons1a7aca02022-04-25 22:35:15 -0400831 _, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerc49e6822021-06-08 15:04:11 -0400832 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`)
833}
834
Wei Li455ba832021-11-04 22:58:12 +0000835func TestTemplateExpandActionSubstitutions(t *testing.T) {
836 const inputString = `
837{
838 "artifacts": [{
839 "id": 1,
840 "pathFragmentId": 1
841 }],
842 "actions": [{
843 "targetId": 1,
844 "actionKey": "x",
845 "mnemonic": "TemplateExpand",
846 "configurationId": 1,
847 "outputIds": [1],
848 "primaryOutputId": 1,
849 "executionPlatform": "//build/bazel/platforms:linux_x86_64",
850 "templateContent": "Test template substitutions: %token1%, %python_binary%",
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700851 "substitutions": [
852 { "key": "%token1%", "value": "abcd" },
853 { "key": "%python_binary%", "value": "python3" }]
Wei Li455ba832021-11-04 22:58:12 +0000854 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700855 "pathFragments": [
856 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +0000857}`
858
Chris Parsons1a7aca02022-04-25 22:35:15 -0400859 actual, _, err := AqueryBuildStatements([]byte(inputString))
Wei Li455ba832021-11-04 22:58:12 +0000860
861 if err != nil {
862 t.Errorf("Unexpected error %q", err)
863 }
864
865 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400866 {
Wei Li455ba832021-11-04 22:58:12 +0000867 Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
868 "chmod a+x template_file'",
869 OutputPaths: []string{"template_file"},
870 Mnemonic: "TemplateExpand",
871 },
872 }
873 assertBuildStatements(t, expectedBuildStatements, actual)
874}
875
876func TestTemplateExpandActionNoOutput(t *testing.T) {
877 const inputString = `
878{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700879 "artifacts": [
880 { "id": 1, "pathFragmentId": 1 }],
Wei Li455ba832021-11-04 22:58:12 +0000881 "actions": [{
882 "targetId": 1,
883 "actionKey": "x",
884 "mnemonic": "TemplateExpand",
885 "configurationId": 1,
886 "primaryOutputId": 1,
887 "executionPlatform": "//build/bazel/platforms:linux_x86_64",
888 "templateContent": "Test template substitutions: %token1%, %python_binary%",
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700889 "substitutions": [
890 { "key": "%token1%", "value": "abcd" },
891 { "key": "%python_binary%", "value": "python3" }]
Wei Li455ba832021-11-04 22:58:12 +0000892 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700893 "pathFragments": [
894 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +0000895}`
896
Chris Parsons1a7aca02022-04-25 22:35:15 -0400897 _, _, err := AqueryBuildStatements([]byte(inputString))
Wei Li455ba832021-11-04 22:58:12 +0000898 assertError(t, err, `Expect 1 output to template expand action, got: output []`)
899}
900
Sasha Smundak1da064c2022-06-08 16:36:16 -0700901func TestFileWrite(t *testing.T) {
902 const inputString = `
903{
904 "artifacts": [
905 { "id": 1, "pathFragmentId": 1 }],
906 "actions": [{
907 "targetId": 1,
908 "actionKey": "x",
909 "mnemonic": "FileWrite",
910 "configurationId": 1,
911 "outputIds": [1],
912 "primaryOutputId": 1,
913 "executionPlatform": "//build/bazel/platforms:linux_x86_64",
914 "fileContents": "file data\n"
915 }],
916 "pathFragments": [
917 { "id": 1, "label": "foo.manifest" }]
918}
919`
920 actual, _, err := AqueryBuildStatements([]byte(inputString))
921 if err != nil {
922 t.Errorf("Unexpected error %q", err)
923 }
924 assertBuildStatements(t, []BuildStatement{
925 {
926 OutputPaths: []string{"foo.manifest"},
927 Mnemonic: "FileWrite",
928 FileContents: "file data\n",
929 },
930 }, actual)
931}
932
933func TestSourceSymlinkManifest(t *testing.T) {
934 const inputString = `
935{
936 "artifacts": [
937 { "id": 1, "pathFragmentId": 1 }],
938 "actions": [{
939 "targetId": 1,
940 "actionKey": "x",
941 "mnemonic": "SourceSymlinkManifest",
942 "configurationId": 1,
943 "outputIds": [1],
944 "primaryOutputId": 1,
945 "executionPlatform": "//build/bazel/platforms:linux_x86_64",
946 "fileContents": "symlink target\n"
947 }],
948 "pathFragments": [
949 { "id": 1, "label": "foo.manifest" }]
950}
951`
952 actual, _, err := AqueryBuildStatements([]byte(inputString))
953 if err != nil {
954 t.Errorf("Unexpected error %q", err)
955 }
956 assertBuildStatements(t, []BuildStatement{
957 {
958 OutputPaths: []string{"foo.manifest"},
959 Mnemonic: "SourceSymlinkManifest",
960 },
961 }, actual)
962}
963
Chris Parsons4f069892021-01-15 12:22:41 -0500964func assertError(t *testing.T, err error, expected string) {
Liz Kammerc49e6822021-06-08 15:04:11 -0400965 t.Helper()
Chris Parsons943f2432021-01-19 11:36:50 -0500966 if err == nil {
967 t.Errorf("expected error '%s', but got no error", expected)
968 } else if err.Error() != expected {
Liz Kammerc49e6822021-06-08 15:04:11 -0400969 t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
Chris Parsons4f069892021-01-15 12:22:41 -0500970 }
971}
972
973// Asserts that the given actual build statements match the given expected build statements.
974// Build statement equivalence is determined using buildStatementEquals.
975func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
Liz Kammerc49e6822021-06-08 15:04:11 -0400976 t.Helper()
Chris Parsons4f069892021-01-15 12:22:41 -0500977 if len(expected) != len(actual) {
Liz Kammerc3192992021-11-16 17:01:11 -0500978 t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
Chris Parsons4f069892021-01-15 12:22:41 -0500979 len(expected), len(actual), expected, actual)
980 return
981 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400982 type compareFn = func(i int, j int) bool
983 byCommand := func(slice []BuildStatement) compareFn {
984 return func(i int, j int) bool {
985 return slice[i].Command < slice[j].Command
Chris Parsons4f069892021-01-15 12:22:41 -0500986 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400987 }
988 sort.SliceStable(expected, byCommand(expected))
989 sort.SliceStable(actual, byCommand(actual))
990 for i, actualStatement := range actual {
991 expectedStatement := expected[i]
992 if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
993 t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
Usta Shresthaef922252022-06-02 14:23:02 -0400994 differingField, actualStatement, expectedStatement)
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400995 return
996 }
Chris Parsons4f069892021-01-15 12:22:41 -0500997 }
998}
999
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001000func buildStatementEquals(first BuildStatement, second BuildStatement) string {
Chris Parsons4f069892021-01-15 12:22:41 -05001001 if first.Mnemonic != second.Mnemonic {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001002 return "Mnemonic"
Chris Parsons4f069892021-01-15 12:22:41 -05001003 }
1004 if first.Command != second.Command {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001005 return "Command"
Chris Parsons4f069892021-01-15 12:22:41 -05001006 }
1007 // Ordering is significant for environment variables.
1008 if !reflect.DeepEqual(first.Env, second.Env) {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001009 return "Env"
Chris Parsons4f069892021-01-15 12:22:41 -05001010 }
1011 // Ordering is irrelevant for input and output paths, so compare sets.
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001012 if !reflect.DeepEqual(sortedStrings(first.InputPaths), sortedStrings(second.InputPaths)) {
1013 return "InputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001014 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001015 if !reflect.DeepEqual(sortedStrings(first.OutputPaths), sortedStrings(second.OutputPaths)) {
1016 return "OutputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -05001017 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001018 if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
1019 return "SymlinkPaths"
Liz Kammerc49e6822021-06-08 15:04:11 -04001020 }
1021 if first.Depfile != second.Depfile {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001022 return "Depfile"
Liz Kammerc49e6822021-06-08 15:04:11 -04001023 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001024 return ""
Chris Parsons4f069892021-01-15 12:22:41 -05001025}
1026
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001027func sortedStrings(stringSlice []string) []string {
1028 sorted := make([]string, len(stringSlice))
1029 copy(sorted, stringSlice)
1030 sort.Strings(sorted)
1031 return sorted
Chris Parsons4f069892021-01-15 12:22:41 -05001032}