blob: 53056c7422020f0f2d2bdc3ad800e422a372aded [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 = `{
Usta Shresthadb46a9b2022-07-11 11:29:56 -0400502 "artifacts": [
503 { "id": 1, "pathFragmentId": 10 },
504 { "id": 2, "pathFragmentId": 20 },
505 { "id": 3, "pathFragmentId": 30 },
506 { "id": 4, "pathFragmentId": 40 }],
Usta Shresthaef922252022-06-02 14:23:02 -0400507 "depSetOfFiles": [{
508 "id": 1111,
509 "directArtifactIds": [3 , 4]
510 }],
511 "actions": [{
512 "targetId": 100,
513 "actionKey": "x",
514 "inputDepSetIds": [1111],
515 "mnemonic": "x",
516 "arguments": ["bogus", "command"],
517 "outputIds": [2],
518 "primaryOutputId": 1
519 }],
Usta Shresthadb46a9b2022-07-11 11:29:56 -0400520 "pathFragments": [
521 { "id": 10, "label": "input" },
522 { "id": 20, "label": "output" },
523 { "id": 30, "label": "dep1", "parentId": 50 },
524 { "id": 40, "label": "dep2", "parentId": 60 },
525 { "id": 50, "label": "bazel_tools", "parentId": 60 },
526 { "id": 60, "label": ".."}
527 ]
Usta Shresthaef922252022-06-02 14:23:02 -0400528}`
529 actualBuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
530 if len(actualDepsets) != 1 {
531 t.Errorf("expected 1 depset but found %#v", actualDepsets)
532 return
533 }
534 dep2Found := false
535 for _, dep := range flattenDepsets([]string{actualDepsets[0].ContentHash}, actualDepsets) {
536 if dep == "../bazel_tools/dep1" {
537 t.Errorf("dependency %s expected to be removed but still exists", dep)
538 } else if dep == "../dep2" {
539 dep2Found = true
540 }
541 }
542 if !dep2Found {
543 t.Errorf("dependency ../dep2 expected but not found")
544 }
545
546 expectedBuildStatement := BuildStatement{
547 Command: "bogus command",
548 OutputPaths: []string{"output"},
549 Mnemonic: "x",
550 }
551 buildStatementFound := false
552 for _, actualBuildStatement := range actualBuildStatements {
553 if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
554 buildStatementFound = true
555 break
556 }
557 }
558 if !buildStatementFound {
559 t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
560 return
561 }
562}
563
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400564func TestMiddlemenAction(t *testing.T) {
565 const inputString = `
566{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700567 "artifacts": [
568 { "id": 1, "pathFragmentId": 1 },
569 { "id": 2, "pathFragmentId": 2 },
570 { "id": 3, "pathFragmentId": 3 },
571 { "id": 4, "pathFragmentId": 4 },
572 { "id": 5, "pathFragmentId": 5 },
573 { "id": 6, "pathFragmentId": 6 }],
574 "pathFragments": [
575 { "id": 1, "label": "middleinput_one" },
576 { "id": 2, "label": "middleinput_two" },
577 { "id": 3, "label": "middleman_artifact" },
578 { "id": 4, "label": "maininput_one" },
579 { "id": 5, "label": "maininput_two" },
580 { "id": 6, "label": "output" }],
581 "depSetOfFiles": [
582 { "id": 1, "directArtifactIds": [1, 2] },
583 { "id": 2, "directArtifactIds": [3, 4, 5] }],
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400584 "actions": [{
585 "targetId": 1,
586 "actionKey": "x",
587 "mnemonic": "Middleman",
588 "arguments": ["touch", "foo"],
589 "inputDepSetIds": [1],
590 "outputIds": [3],
591 "primaryOutputId": 3
592 }, {
593 "targetId": 2,
594 "actionKey": "y",
595 "mnemonic": "Main action",
596 "arguments": ["touch", "foo"],
597 "inputDepSetIds": [2],
598 "outputIds": [6],
599 "primaryOutputId": 6
600 }]
601}`
602
Chris Parsons1a7aca02022-04-25 22:35:15 -0400603 actualBuildStatements, actualDepsets, err := AqueryBuildStatements([]byte(inputString))
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400604 if err != nil {
605 t.Errorf("Unexpected error %q", err)
606 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400607 if expected := 1; len(actualBuildStatements) != expected {
608 t.Fatalf("Expected %d build statements, got %d", expected, len(actualBuildStatements))
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400609 }
610
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400611 expectedDepsetFiles := [][]string{
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400612 {"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"},
613 {"middleinput_one", "middleinput_two"},
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400614 }
615 assertFlattenedDepsets(t, actualDepsets, expectedDepsetFiles)
616
Chris Parsons1a7aca02022-04-25 22:35:15 -0400617 bs := actualBuildStatements[0]
618 if len(bs.InputPaths) > 0 {
619 t.Errorf("Expected main action raw inputs to be empty, but got %q", bs.InputPaths)
620 }
621
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400622 expectedOutputs := []string{"output"}
623 if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) {
624 t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths)
625 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400626
Chris Parsons1a7aca02022-04-25 22:35:15 -0400627 expectedFlattenedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400628 actualFlattenedInputs := flattenDepsets(bs.InputDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400629
630 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
631 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
632 }
633}
634
635// Returns the contents of given depsets in concatenated post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400636func flattenDepsets(depsetHashesToFlatten []string, allDepsets []AqueryDepset) []string {
637 depsetsByHash := map[string]AqueryDepset{}
Chris Parsons1a7aca02022-04-25 22:35:15 -0400638 for _, depset := range allDepsets {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400639 depsetsByHash[depset.ContentHash] = depset
Chris Parsons1a7aca02022-04-25 22:35:15 -0400640 }
Usta Shrestha16ac1352022-06-22 11:01:55 -0400641 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400642 for _, depsetId := range depsetHashesToFlatten {
643 result = append(result, flattenDepset(depsetId, depsetsByHash)...)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400644 }
645 return result
646}
647
648// Returns the contents of a given depset in post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400649func flattenDepset(depsetHashToFlatten string, allDepsets map[string]AqueryDepset) []string {
650 depset := allDepsets[depsetHashToFlatten]
Usta Shrestha16ac1352022-06-22 11:01:55 -0400651 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400652 for _, depsetId := range depset.TransitiveDepSetHashes {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400653 result = append(result, flattenDepset(depsetId, allDepsets)...)
654 }
655 result = append(result, depset.DirectArtifacts...)
656 return result
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400657}
658
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400659func assertFlattenedDepsets(t *testing.T, actualDepsets []AqueryDepset, expectedDepsetFiles [][]string) {
660 t.Helper()
661 if len(actualDepsets) != len(expectedDepsetFiles) {
Sasha Smundakf10c3ac2022-06-08 11:46:31 -0700662 t.Errorf("Expected %d depsets, but got %d depsets", len(expectedDepsetFiles), len(actualDepsets))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400663 }
664 for i, actualDepset := range actualDepsets {
665 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
666 if !reflect.DeepEqual(actualFlattenedInputs, expectedDepsetFiles[i]) {
667 t.Errorf("Expected depset files: %v, but got %v", expectedDepsetFiles[i], actualFlattenedInputs)
668 }
669 }
670}
671
Liz Kammerc49e6822021-06-08 15:04:11 -0400672func TestSimpleSymlink(t *testing.T) {
673 const inputString = `
674{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700675 "artifacts": [
676 { "id": 1, "pathFragmentId": 3 },
677 { "id": 2, "pathFragmentId": 5 }],
Liz Kammerc49e6822021-06-08 15:04:11 -0400678 "actions": [{
679 "targetId": 1,
680 "actionKey": "x",
681 "mnemonic": "Symlink",
682 "inputDepSetIds": [1],
683 "outputIds": [2],
684 "primaryOutputId": 2
685 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700686 "depSetOfFiles": [
687 { "id": 1, "directArtifactIds": [1] }],
688 "pathFragments": [
689 { "id": 1, "label": "one" },
690 { "id": 2, "label": "file_subdir", "parentId": 1 },
691 { "id": 3, "label": "file", "parentId": 2 },
692 { "id": 4, "label": "symlink_subdir", "parentId": 1 },
693 { "id": 5, "label": "symlink", "parentId": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400694}`
695
Chris Parsons1a7aca02022-04-25 22:35:15 -0400696 actual, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerc49e6822021-06-08 15:04:11 -0400697
698 if err != nil {
699 t.Errorf("Unexpected error %q", err)
700 }
701
702 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400703 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400704 Command: "mkdir -p one/symlink_subdir && " +
705 "rm -f one/symlink_subdir/symlink && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400706 "ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
Liz Kammerc49e6822021-06-08 15:04:11 -0400707 InputPaths: []string{"one/file_subdir/file"},
708 OutputPaths: []string{"one/symlink_subdir/symlink"},
709 SymlinkPaths: []string{"one/symlink_subdir/symlink"},
710 Mnemonic: "Symlink",
711 },
712 }
713 assertBuildStatements(t, actual, expectedBuildStatements)
714}
715
716func TestSymlinkQuotesPaths(t *testing.T) {
717 const inputString = `
718{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700719 "artifacts": [
720 { "id": 1, "pathFragmentId": 3 },
721 { "id": 2, "pathFragmentId": 5 }],
Liz Kammerc49e6822021-06-08 15:04:11 -0400722 "actions": [{
723 "targetId": 1,
724 "actionKey": "x",
725 "mnemonic": "SolibSymlink",
726 "inputDepSetIds": [1],
727 "outputIds": [2],
728 "primaryOutputId": 2
729 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700730 "depSetOfFiles": [
731 { "id": 1, "directArtifactIds": [1] }],
732 "pathFragments": [
733 { "id": 1, "label": "one" },
734 { "id": 2, "label": "file subdir", "parentId": 1 },
735 { "id": 3, "label": "file", "parentId": 2 },
736 { "id": 4, "label": "symlink subdir", "parentId": 1 },
737 { "id": 5, "label": "symlink", "parentId": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400738}`
739
Chris Parsons1a7aca02022-04-25 22:35:15 -0400740 actual, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerc49e6822021-06-08 15:04:11 -0400741
742 if err != nil {
743 t.Errorf("Unexpected error %q", err)
744 }
745
746 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400747 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400748 Command: "mkdir -p 'one/symlink subdir' && " +
749 "rm -f 'one/symlink subdir/symlink' && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400750 "ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
Liz Kammerc49e6822021-06-08 15:04:11 -0400751 InputPaths: []string{"one/file subdir/file"},
752 OutputPaths: []string{"one/symlink subdir/symlink"},
753 SymlinkPaths: []string{"one/symlink subdir/symlink"},
754 Mnemonic: "SolibSymlink",
755 },
756 }
Liz Kammerc7737782021-11-04 10:56:13 -0400757 assertBuildStatements(t, expectedBuildStatements, actual)
Liz Kammerc49e6822021-06-08 15:04:11 -0400758}
759
760func TestSymlinkMultipleInputs(t *testing.T) {
761 const inputString = `
762{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700763 "artifacts": [
764 { "id": 1, "pathFragmentId": 1 },
765 { "id": 2, "pathFragmentId": 2 },
766 { "id": 3, "pathFragmentId": 3 }],
Liz Kammerc49e6822021-06-08 15:04:11 -0400767 "actions": [{
768 "targetId": 1,
769 "actionKey": "x",
770 "mnemonic": "Symlink",
771 "inputDepSetIds": [1],
772 "outputIds": [3],
773 "primaryOutputId": 3
774 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700775 "depSetOfFiles": [{ "id": 1, "directArtifactIds": [1,2] }],
776 "pathFragments": [
777 { "id": 1, "label": "file" },
778 { "id": 2, "label": "other_file" },
779 { "id": 3, "label": "symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400780}`
781
Chris Parsons1a7aca02022-04-25 22:35:15 -0400782 _, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerc49e6822021-06-08 15:04:11 -0400783 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
784}
785
786func TestSymlinkMultipleOutputs(t *testing.T) {
787 const inputString = `
788{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700789 "artifacts": [
790 { "id": 1, "pathFragmentId": 1 },
791 { "id": 2, "pathFragmentId": 2 },
792 { "id": 3, "pathFragmentId": 3 }],
Liz Kammerc49e6822021-06-08 15:04:11 -0400793 "actions": [{
794 "targetId": 1,
795 "actionKey": "x",
796 "mnemonic": "Symlink",
797 "inputDepSetIds": [1],
798 "outputIds": [2,3],
799 "primaryOutputId": 2
800 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700801 "depSetOfFiles": [
802 { "id": 1, "directArtifactIds": [1] }],
803 "pathFragments": [
804 { "id": 1, "label": "file" },
805 { "id": 2, "label": "symlink" },
806 { "id": 3, "label": "other_symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400807}`
808
Chris Parsons1a7aca02022-04-25 22:35:15 -0400809 _, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerc49e6822021-06-08 15:04:11 -0400810 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`)
811}
812
Wei Li455ba832021-11-04 22:58:12 +0000813func TestTemplateExpandActionSubstitutions(t *testing.T) {
814 const inputString = `
815{
816 "artifacts": [{
817 "id": 1,
818 "pathFragmentId": 1
819 }],
820 "actions": [{
821 "targetId": 1,
822 "actionKey": "x",
823 "mnemonic": "TemplateExpand",
824 "configurationId": 1,
825 "outputIds": [1],
826 "primaryOutputId": 1,
827 "executionPlatform": "//build/bazel/platforms:linux_x86_64",
828 "templateContent": "Test template substitutions: %token1%, %python_binary%",
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700829 "substitutions": [
830 { "key": "%token1%", "value": "abcd" },
831 { "key": "%python_binary%", "value": "python3" }]
Wei Li455ba832021-11-04 22:58:12 +0000832 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700833 "pathFragments": [
834 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +0000835}`
836
Chris Parsons1a7aca02022-04-25 22:35:15 -0400837 actual, _, err := AqueryBuildStatements([]byte(inputString))
Wei Li455ba832021-11-04 22:58:12 +0000838
839 if err != nil {
840 t.Errorf("Unexpected error %q", err)
841 }
842
843 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400844 {
Wei Li455ba832021-11-04 22:58:12 +0000845 Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
846 "chmod a+x template_file'",
847 OutputPaths: []string{"template_file"},
848 Mnemonic: "TemplateExpand",
849 },
850 }
851 assertBuildStatements(t, expectedBuildStatements, actual)
852}
853
854func TestTemplateExpandActionNoOutput(t *testing.T) {
855 const inputString = `
856{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700857 "artifacts": [
858 { "id": 1, "pathFragmentId": 1 }],
Wei Li455ba832021-11-04 22:58:12 +0000859 "actions": [{
860 "targetId": 1,
861 "actionKey": "x",
862 "mnemonic": "TemplateExpand",
863 "configurationId": 1,
864 "primaryOutputId": 1,
865 "executionPlatform": "//build/bazel/platforms:linux_x86_64",
866 "templateContent": "Test template substitutions: %token1%, %python_binary%",
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700867 "substitutions": [
868 { "key": "%token1%", "value": "abcd" },
869 { "key": "%python_binary%", "value": "python3" }]
Wei Li455ba832021-11-04 22:58:12 +0000870 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700871 "pathFragments": [
872 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +0000873}`
874
Chris Parsons1a7aca02022-04-25 22:35:15 -0400875 _, _, err := AqueryBuildStatements([]byte(inputString))
Wei Li455ba832021-11-04 22:58:12 +0000876 assertError(t, err, `Expect 1 output to template expand action, got: output []`)
877}
878
Sasha Smundak1da064c2022-06-08 16:36:16 -0700879func TestFileWrite(t *testing.T) {
880 const inputString = `
881{
882 "artifacts": [
883 { "id": 1, "pathFragmentId": 1 }],
884 "actions": [{
885 "targetId": 1,
886 "actionKey": "x",
887 "mnemonic": "FileWrite",
888 "configurationId": 1,
889 "outputIds": [1],
890 "primaryOutputId": 1,
891 "executionPlatform": "//build/bazel/platforms:linux_x86_64",
892 "fileContents": "file data\n"
893 }],
894 "pathFragments": [
895 { "id": 1, "label": "foo.manifest" }]
896}
897`
898 actual, _, err := AqueryBuildStatements([]byte(inputString))
899 if err != nil {
900 t.Errorf("Unexpected error %q", err)
901 }
902 assertBuildStatements(t, []BuildStatement{
903 {
904 OutputPaths: []string{"foo.manifest"},
905 Mnemonic: "FileWrite",
906 FileContents: "file data\n",
907 },
908 }, actual)
909}
910
911func TestSourceSymlinkManifest(t *testing.T) {
912 const inputString = `
913{
914 "artifacts": [
915 { "id": 1, "pathFragmentId": 1 }],
916 "actions": [{
917 "targetId": 1,
918 "actionKey": "x",
919 "mnemonic": "SourceSymlinkManifest",
920 "configurationId": 1,
921 "outputIds": [1],
922 "primaryOutputId": 1,
923 "executionPlatform": "//build/bazel/platforms:linux_x86_64",
924 "fileContents": "symlink target\n"
925 }],
926 "pathFragments": [
927 { "id": 1, "label": "foo.manifest" }]
928}
929`
930 actual, _, err := AqueryBuildStatements([]byte(inputString))
931 if err != nil {
932 t.Errorf("Unexpected error %q", err)
933 }
934 assertBuildStatements(t, []BuildStatement{
935 {
936 OutputPaths: []string{"foo.manifest"},
937 Mnemonic: "SourceSymlinkManifest",
938 },
939 }, actual)
940}
941
Chris Parsons4f069892021-01-15 12:22:41 -0500942func assertError(t *testing.T, err error, expected string) {
Liz Kammerc49e6822021-06-08 15:04:11 -0400943 t.Helper()
Chris Parsons943f2432021-01-19 11:36:50 -0500944 if err == nil {
945 t.Errorf("expected error '%s', but got no error", expected)
946 } else if err.Error() != expected {
Liz Kammerc49e6822021-06-08 15:04:11 -0400947 t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
Chris Parsons4f069892021-01-15 12:22:41 -0500948 }
949}
950
951// Asserts that the given actual build statements match the given expected build statements.
952// Build statement equivalence is determined using buildStatementEquals.
953func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
Liz Kammerc49e6822021-06-08 15:04:11 -0400954 t.Helper()
Chris Parsons4f069892021-01-15 12:22:41 -0500955 if len(expected) != len(actual) {
Liz Kammerc3192992021-11-16 17:01:11 -0500956 t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
Chris Parsons4f069892021-01-15 12:22:41 -0500957 len(expected), len(actual), expected, actual)
958 return
959 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400960 type compareFn = func(i int, j int) bool
961 byCommand := func(slice []BuildStatement) compareFn {
962 return func(i int, j int) bool {
963 return slice[i].Command < slice[j].Command
Chris Parsons4f069892021-01-15 12:22:41 -0500964 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400965 }
966 sort.SliceStable(expected, byCommand(expected))
967 sort.SliceStable(actual, byCommand(actual))
968 for i, actualStatement := range actual {
969 expectedStatement := expected[i]
970 if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
971 t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
Usta Shresthaef922252022-06-02 14:23:02 -0400972 differingField, actualStatement, expectedStatement)
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400973 return
974 }
Chris Parsons4f069892021-01-15 12:22:41 -0500975 }
976}
977
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400978func buildStatementEquals(first BuildStatement, second BuildStatement) string {
Chris Parsons4f069892021-01-15 12:22:41 -0500979 if first.Mnemonic != second.Mnemonic {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400980 return "Mnemonic"
Chris Parsons4f069892021-01-15 12:22:41 -0500981 }
982 if first.Command != second.Command {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400983 return "Command"
Chris Parsons4f069892021-01-15 12:22:41 -0500984 }
985 // Ordering is significant for environment variables.
986 if !reflect.DeepEqual(first.Env, second.Env) {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400987 return "Env"
Chris Parsons4f069892021-01-15 12:22:41 -0500988 }
989 // Ordering is irrelevant for input and output paths, so compare sets.
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400990 if !reflect.DeepEqual(sortedStrings(first.InputPaths), sortedStrings(second.InputPaths)) {
991 return "InputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -0500992 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400993 if !reflect.DeepEqual(sortedStrings(first.OutputPaths), sortedStrings(second.OutputPaths)) {
994 return "OutputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -0500995 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400996 if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
997 return "SymlinkPaths"
Liz Kammerc49e6822021-06-08 15:04:11 -0400998 }
999 if first.Depfile != second.Depfile {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001000 return "Depfile"
Liz Kammerc49e6822021-06-08 15:04:11 -04001001 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001002 return ""
Chris Parsons4f069892021-01-15 12:22:41 -05001003}
1004
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001005func sortedStrings(stringSlice []string) []string {
1006 sorted := make([]string, len(stringSlice))
1007 copy(sorted, stringSlice)
1008 sort.Strings(sorted)
1009 return sorted
Chris Parsons4f069892021-01-15 12:22:41 -05001010}