blob: 581036439d760b09870f415fca29189e07cd2a97 [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]
Usta Shresthae70f28c2022-08-08 22:48:13 -0400510 }, {
511 "id": 2222,
512 "directArtifactIds": [3]
Usta Shresthaef922252022-06-02 14:23:02 -0400513 }],
514 "actions": [{
515 "targetId": 100,
516 "actionKey": "x",
Usta Shresthae70f28c2022-08-08 22:48:13 -0400517 "inputDepSetIds": [1111, 2222],
Usta Shresthaef922252022-06-02 14:23:02 -0400518 "mnemonic": "x",
519 "arguments": ["bogus", "command"],
520 "outputIds": [2],
521 "primaryOutputId": 1
522 }],
Usta Shresthadb46a9b2022-07-11 11:29:56 -0400523 "pathFragments": [
524 { "id": 10, "label": "input" },
525 { "id": 20, "label": "output" },
526 { "id": 30, "label": "dep1", "parentId": 50 },
527 { "id": 40, "label": "dep2", "parentId": 60 },
528 { "id": 50, "label": "bazel_tools", "parentId": 60 },
529 { "id": 60, "label": ".."}
530 ]
Usta Shresthaef922252022-06-02 14:23:02 -0400531}`
532 actualBuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
Usta Shresthae70f28c2022-08-08 22:48:13 -0400533 if len(actualDepsets) != 2 {
Usta Shresthaef922252022-06-02 14:23:02 -0400534 t.Errorf("expected 1 depset but found %#v", actualDepsets)
535 return
536 }
537 dep2Found := false
538 for _, dep := range flattenDepsets([]string{actualDepsets[0].ContentHash}, actualDepsets) {
539 if dep == "../bazel_tools/dep1" {
540 t.Errorf("dependency %s expected to be removed but still exists", dep)
541 } else if dep == "../dep2" {
542 dep2Found = true
543 }
544 }
545 if !dep2Found {
546 t.Errorf("dependency ../dep2 expected but not found")
547 }
548
549 expectedBuildStatement := BuildStatement{
550 Command: "bogus command",
551 OutputPaths: []string{"output"},
552 Mnemonic: "x",
553 }
554 buildStatementFound := false
555 for _, actualBuildStatement := range actualBuildStatements {
556 if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
557 buildStatementFound = true
558 break
559 }
560 }
561 if !buildStatementFound {
562 t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
563 return
564 }
565}
566
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400567func TestMiddlemenAction(t *testing.T) {
568 const inputString = `
569{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700570 "artifacts": [
571 { "id": 1, "pathFragmentId": 1 },
572 { "id": 2, "pathFragmentId": 2 },
573 { "id": 3, "pathFragmentId": 3 },
574 { "id": 4, "pathFragmentId": 4 },
575 { "id": 5, "pathFragmentId": 5 },
576 { "id": 6, "pathFragmentId": 6 }],
577 "pathFragments": [
578 { "id": 1, "label": "middleinput_one" },
579 { "id": 2, "label": "middleinput_two" },
580 { "id": 3, "label": "middleman_artifact" },
581 { "id": 4, "label": "maininput_one" },
582 { "id": 5, "label": "maininput_two" },
583 { "id": 6, "label": "output" }],
584 "depSetOfFiles": [
585 { "id": 1, "directArtifactIds": [1, 2] },
586 { "id": 2, "directArtifactIds": [3, 4, 5] }],
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400587 "actions": [{
588 "targetId": 1,
589 "actionKey": "x",
590 "mnemonic": "Middleman",
591 "arguments": ["touch", "foo"],
592 "inputDepSetIds": [1],
593 "outputIds": [3],
594 "primaryOutputId": 3
595 }, {
596 "targetId": 2,
597 "actionKey": "y",
598 "mnemonic": "Main action",
599 "arguments": ["touch", "foo"],
600 "inputDepSetIds": [2],
601 "outputIds": [6],
602 "primaryOutputId": 6
603 }]
604}`
605
Chris Parsons1a7aca02022-04-25 22:35:15 -0400606 actualBuildStatements, actualDepsets, err := AqueryBuildStatements([]byte(inputString))
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400607 if err != nil {
608 t.Errorf("Unexpected error %q", err)
609 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400610 if expected := 1; len(actualBuildStatements) != expected {
611 t.Fatalf("Expected %d build statements, got %d", expected, len(actualBuildStatements))
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400612 }
613
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400614 expectedDepsetFiles := [][]string{
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400615 {"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"},
616 {"middleinput_one", "middleinput_two"},
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400617 }
618 assertFlattenedDepsets(t, actualDepsets, expectedDepsetFiles)
619
Chris Parsons1a7aca02022-04-25 22:35:15 -0400620 bs := actualBuildStatements[0]
621 if len(bs.InputPaths) > 0 {
622 t.Errorf("Expected main action raw inputs to be empty, but got %q", bs.InputPaths)
623 }
624
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400625 expectedOutputs := []string{"output"}
626 if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) {
627 t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths)
628 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400629
Chris Parsons1a7aca02022-04-25 22:35:15 -0400630 expectedFlattenedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400631 actualFlattenedInputs := flattenDepsets(bs.InputDepsetHashes, actualDepsets)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400632
633 if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
634 t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
635 }
636}
637
638// Returns the contents of given depsets in concatenated post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400639func flattenDepsets(depsetHashesToFlatten []string, allDepsets []AqueryDepset) []string {
640 depsetsByHash := map[string]AqueryDepset{}
Chris Parsons1a7aca02022-04-25 22:35:15 -0400641 for _, depset := range allDepsets {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400642 depsetsByHash[depset.ContentHash] = depset
Chris Parsons1a7aca02022-04-25 22:35:15 -0400643 }
Usta Shrestha16ac1352022-06-22 11:01:55 -0400644 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400645 for _, depsetId := range depsetHashesToFlatten {
646 result = append(result, flattenDepset(depsetId, depsetsByHash)...)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400647 }
648 return result
649}
650
651// Returns the contents of a given depset in post order.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400652func flattenDepset(depsetHashToFlatten string, allDepsets map[string]AqueryDepset) []string {
653 depset := allDepsets[depsetHashToFlatten]
Usta Shrestha16ac1352022-06-22 11:01:55 -0400654 var result []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400655 for _, depsetId := range depset.TransitiveDepSetHashes {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400656 result = append(result, flattenDepset(depsetId, allDepsets)...)
657 }
658 result = append(result, depset.DirectArtifacts...)
659 return result
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400660}
661
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400662func assertFlattenedDepsets(t *testing.T, actualDepsets []AqueryDepset, expectedDepsetFiles [][]string) {
663 t.Helper()
664 if len(actualDepsets) != len(expectedDepsetFiles) {
Sasha Smundakf10c3ac2022-06-08 11:46:31 -0700665 t.Errorf("Expected %d depsets, but got %d depsets", len(expectedDepsetFiles), len(actualDepsets))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400666 }
667 for i, actualDepset := range actualDepsets {
668 actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
669 if !reflect.DeepEqual(actualFlattenedInputs, expectedDepsetFiles[i]) {
670 t.Errorf("Expected depset files: %v, but got %v", expectedDepsetFiles[i], actualFlattenedInputs)
671 }
672 }
673}
674
Liz Kammerc49e6822021-06-08 15:04:11 -0400675func TestSimpleSymlink(t *testing.T) {
676 const inputString = `
677{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700678 "artifacts": [
679 { "id": 1, "pathFragmentId": 3 },
680 { "id": 2, "pathFragmentId": 5 }],
Liz Kammerc49e6822021-06-08 15:04:11 -0400681 "actions": [{
682 "targetId": 1,
683 "actionKey": "x",
684 "mnemonic": "Symlink",
685 "inputDepSetIds": [1],
686 "outputIds": [2],
687 "primaryOutputId": 2
688 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700689 "depSetOfFiles": [
690 { "id": 1, "directArtifactIds": [1] }],
691 "pathFragments": [
692 { "id": 1, "label": "one" },
693 { "id": 2, "label": "file_subdir", "parentId": 1 },
694 { "id": 3, "label": "file", "parentId": 2 },
695 { "id": 4, "label": "symlink_subdir", "parentId": 1 },
696 { "id": 5, "label": "symlink", "parentId": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400697}`
698
Chris Parsons1a7aca02022-04-25 22:35:15 -0400699 actual, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerc49e6822021-06-08 15:04:11 -0400700
701 if err != nil {
702 t.Errorf("Unexpected error %q", err)
703 }
704
705 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400706 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400707 Command: "mkdir -p one/symlink_subdir && " +
708 "rm -f one/symlink_subdir/symlink && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400709 "ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
Liz Kammerc49e6822021-06-08 15:04:11 -0400710 InputPaths: []string{"one/file_subdir/file"},
711 OutputPaths: []string{"one/symlink_subdir/symlink"},
712 SymlinkPaths: []string{"one/symlink_subdir/symlink"},
713 Mnemonic: "Symlink",
714 },
715 }
716 assertBuildStatements(t, actual, expectedBuildStatements)
717}
718
719func TestSymlinkQuotesPaths(t *testing.T) {
720 const inputString = `
721{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700722 "artifacts": [
723 { "id": 1, "pathFragmentId": 3 },
724 { "id": 2, "pathFragmentId": 5 }],
Liz Kammerc49e6822021-06-08 15:04:11 -0400725 "actions": [{
726 "targetId": 1,
727 "actionKey": "x",
728 "mnemonic": "SolibSymlink",
729 "inputDepSetIds": [1],
730 "outputIds": [2],
731 "primaryOutputId": 2
732 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700733 "depSetOfFiles": [
734 { "id": 1, "directArtifactIds": [1] }],
735 "pathFragments": [
736 { "id": 1, "label": "one" },
737 { "id": 2, "label": "file subdir", "parentId": 1 },
738 { "id": 3, "label": "file", "parentId": 2 },
739 { "id": 4, "label": "symlink subdir", "parentId": 1 },
740 { "id": 5, "label": "symlink", "parentId": 4 }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400741}`
742
Chris Parsons1a7aca02022-04-25 22:35:15 -0400743 actual, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerc49e6822021-06-08 15:04:11 -0400744
745 if err != nil {
746 t.Errorf("Unexpected error %q", err)
747 }
748
749 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400750 {
Liz Kammerc49e6822021-06-08 15:04:11 -0400751 Command: "mkdir -p 'one/symlink subdir' && " +
752 "rm -f 'one/symlink subdir/symlink' && " +
Liz Kammerc7737782021-11-04 10:56:13 -0400753 "ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
Liz Kammerc49e6822021-06-08 15:04:11 -0400754 InputPaths: []string{"one/file subdir/file"},
755 OutputPaths: []string{"one/symlink subdir/symlink"},
756 SymlinkPaths: []string{"one/symlink subdir/symlink"},
757 Mnemonic: "SolibSymlink",
758 },
759 }
Liz Kammerc7737782021-11-04 10:56:13 -0400760 assertBuildStatements(t, expectedBuildStatements, actual)
Liz Kammerc49e6822021-06-08 15:04:11 -0400761}
762
763func TestSymlinkMultipleInputs(t *testing.T) {
764 const inputString = `
765{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700766 "artifacts": [
767 { "id": 1, "pathFragmentId": 1 },
768 { "id": 2, "pathFragmentId": 2 },
769 { "id": 3, "pathFragmentId": 3 }],
Liz Kammerc49e6822021-06-08 15:04:11 -0400770 "actions": [{
771 "targetId": 1,
772 "actionKey": "x",
773 "mnemonic": "Symlink",
774 "inputDepSetIds": [1],
775 "outputIds": [3],
776 "primaryOutputId": 3
777 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700778 "depSetOfFiles": [{ "id": 1, "directArtifactIds": [1,2] }],
779 "pathFragments": [
780 { "id": 1, "label": "file" },
781 { "id": 2, "label": "other_file" },
782 { "id": 3, "label": "symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400783}`
784
Chris Parsons1a7aca02022-04-25 22:35:15 -0400785 _, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerc49e6822021-06-08 15:04:11 -0400786 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
787}
788
789func TestSymlinkMultipleOutputs(t *testing.T) {
790 const inputString = `
791{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700792 "artifacts": [
793 { "id": 1, "pathFragmentId": 1 },
794 { "id": 2, "pathFragmentId": 2 },
795 { "id": 3, "pathFragmentId": 3 }],
Liz Kammerc49e6822021-06-08 15:04:11 -0400796 "actions": [{
797 "targetId": 1,
798 "actionKey": "x",
799 "mnemonic": "Symlink",
800 "inputDepSetIds": [1],
801 "outputIds": [2,3],
802 "primaryOutputId": 2
803 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700804 "depSetOfFiles": [
805 { "id": 1, "directArtifactIds": [1] }],
806 "pathFragments": [
807 { "id": 1, "label": "file" },
808 { "id": 2, "label": "symlink" },
809 { "id": 3, "label": "other_symlink" }]
Liz Kammerc49e6822021-06-08 15:04:11 -0400810}`
811
Chris Parsons1a7aca02022-04-25 22:35:15 -0400812 _, _, err := AqueryBuildStatements([]byte(inputString))
Liz Kammerc49e6822021-06-08 15:04:11 -0400813 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`)
814}
815
Wei Li455ba832021-11-04 22:58:12 +0000816func TestTemplateExpandActionSubstitutions(t *testing.T) {
817 const inputString = `
818{
819 "artifacts": [{
820 "id": 1,
821 "pathFragmentId": 1
822 }],
823 "actions": [{
824 "targetId": 1,
825 "actionKey": "x",
826 "mnemonic": "TemplateExpand",
827 "configurationId": 1,
828 "outputIds": [1],
829 "primaryOutputId": 1,
830 "executionPlatform": "//build/bazel/platforms:linux_x86_64",
831 "templateContent": "Test template substitutions: %token1%, %python_binary%",
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700832 "substitutions": [
833 { "key": "%token1%", "value": "abcd" },
834 { "key": "%python_binary%", "value": "python3" }]
Wei Li455ba832021-11-04 22:58:12 +0000835 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700836 "pathFragments": [
837 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +0000838}`
839
Chris Parsons1a7aca02022-04-25 22:35:15 -0400840 actual, _, err := AqueryBuildStatements([]byte(inputString))
Wei Li455ba832021-11-04 22:58:12 +0000841
842 if err != nil {
843 t.Errorf("Unexpected error %q", err)
844 }
845
846 expectedBuildStatements := []BuildStatement{
Usta Shrestha16ac1352022-06-22 11:01:55 -0400847 {
Wei Li455ba832021-11-04 22:58:12 +0000848 Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
849 "chmod a+x template_file'",
850 OutputPaths: []string{"template_file"},
851 Mnemonic: "TemplateExpand",
852 },
853 }
854 assertBuildStatements(t, expectedBuildStatements, actual)
855}
856
857func TestTemplateExpandActionNoOutput(t *testing.T) {
858 const inputString = `
859{
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700860 "artifacts": [
861 { "id": 1, "pathFragmentId": 1 }],
Wei Li455ba832021-11-04 22:58:12 +0000862 "actions": [{
863 "targetId": 1,
864 "actionKey": "x",
865 "mnemonic": "TemplateExpand",
866 "configurationId": 1,
867 "primaryOutputId": 1,
868 "executionPlatform": "//build/bazel/platforms:linux_x86_64",
869 "templateContent": "Test template substitutions: %token1%, %python_binary%",
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700870 "substitutions": [
871 { "key": "%token1%", "value": "abcd" },
872 { "key": "%python_binary%", "value": "python3" }]
Wei Li455ba832021-11-04 22:58:12 +0000873 }],
Sasha Smundake3cf1ab2022-06-30 11:36:18 -0700874 "pathFragments": [
875 { "id": 1, "label": "template_file" }]
Wei Li455ba832021-11-04 22:58:12 +0000876}`
877
Chris Parsons1a7aca02022-04-25 22:35:15 -0400878 _, _, err := AqueryBuildStatements([]byte(inputString))
Wei Li455ba832021-11-04 22:58:12 +0000879 assertError(t, err, `Expect 1 output to template expand action, got: output []`)
880}
881
Sasha Smundak1da064c2022-06-08 16:36:16 -0700882func TestFileWrite(t *testing.T) {
883 const inputString = `
884{
885 "artifacts": [
886 { "id": 1, "pathFragmentId": 1 }],
887 "actions": [{
888 "targetId": 1,
889 "actionKey": "x",
890 "mnemonic": "FileWrite",
891 "configurationId": 1,
892 "outputIds": [1],
893 "primaryOutputId": 1,
894 "executionPlatform": "//build/bazel/platforms:linux_x86_64",
895 "fileContents": "file data\n"
896 }],
897 "pathFragments": [
898 { "id": 1, "label": "foo.manifest" }]
899}
900`
901 actual, _, err := AqueryBuildStatements([]byte(inputString))
902 if err != nil {
903 t.Errorf("Unexpected error %q", err)
904 }
905 assertBuildStatements(t, []BuildStatement{
906 {
907 OutputPaths: []string{"foo.manifest"},
908 Mnemonic: "FileWrite",
909 FileContents: "file data\n",
910 },
911 }, actual)
912}
913
914func TestSourceSymlinkManifest(t *testing.T) {
915 const inputString = `
916{
917 "artifacts": [
918 { "id": 1, "pathFragmentId": 1 }],
919 "actions": [{
920 "targetId": 1,
921 "actionKey": "x",
922 "mnemonic": "SourceSymlinkManifest",
923 "configurationId": 1,
924 "outputIds": [1],
925 "primaryOutputId": 1,
926 "executionPlatform": "//build/bazel/platforms:linux_x86_64",
927 "fileContents": "symlink target\n"
928 }],
929 "pathFragments": [
930 { "id": 1, "label": "foo.manifest" }]
931}
932`
933 actual, _, err := AqueryBuildStatements([]byte(inputString))
934 if err != nil {
935 t.Errorf("Unexpected error %q", err)
936 }
937 assertBuildStatements(t, []BuildStatement{
938 {
939 OutputPaths: []string{"foo.manifest"},
940 Mnemonic: "SourceSymlinkManifest",
941 },
942 }, actual)
943}
944
Chris Parsons4f069892021-01-15 12:22:41 -0500945func assertError(t *testing.T, err error, expected string) {
Liz Kammerc49e6822021-06-08 15:04:11 -0400946 t.Helper()
Chris Parsons943f2432021-01-19 11:36:50 -0500947 if err == nil {
948 t.Errorf("expected error '%s', but got no error", expected)
949 } else if err.Error() != expected {
Liz Kammerc49e6822021-06-08 15:04:11 -0400950 t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
Chris Parsons4f069892021-01-15 12:22:41 -0500951 }
952}
953
954// Asserts that the given actual build statements match the given expected build statements.
955// Build statement equivalence is determined using buildStatementEquals.
956func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
Liz Kammerc49e6822021-06-08 15:04:11 -0400957 t.Helper()
Chris Parsons4f069892021-01-15 12:22:41 -0500958 if len(expected) != len(actual) {
Liz Kammerc3192992021-11-16 17:01:11 -0500959 t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
Chris Parsons4f069892021-01-15 12:22:41 -0500960 len(expected), len(actual), expected, actual)
961 return
962 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400963 type compareFn = func(i int, j int) bool
964 byCommand := func(slice []BuildStatement) compareFn {
965 return func(i int, j int) bool {
966 return slice[i].Command < slice[j].Command
Chris Parsons4f069892021-01-15 12:22:41 -0500967 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400968 }
969 sort.SliceStable(expected, byCommand(expected))
970 sort.SliceStable(actual, byCommand(actual))
971 for i, actualStatement := range actual {
972 expectedStatement := expected[i]
973 if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
974 t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
Usta Shresthaef922252022-06-02 14:23:02 -0400975 differingField, actualStatement, expectedStatement)
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400976 return
977 }
Chris Parsons4f069892021-01-15 12:22:41 -0500978 }
979}
980
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400981func buildStatementEquals(first BuildStatement, second BuildStatement) string {
Chris Parsons4f069892021-01-15 12:22:41 -0500982 if first.Mnemonic != second.Mnemonic {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400983 return "Mnemonic"
Chris Parsons4f069892021-01-15 12:22:41 -0500984 }
985 if first.Command != second.Command {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400986 return "Command"
Chris Parsons4f069892021-01-15 12:22:41 -0500987 }
988 // Ordering is significant for environment variables.
989 if !reflect.DeepEqual(first.Env, second.Env) {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400990 return "Env"
Chris Parsons4f069892021-01-15 12:22:41 -0500991 }
992 // Ordering is irrelevant for input and output paths, so compare sets.
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400993 if !reflect.DeepEqual(sortedStrings(first.InputPaths), sortedStrings(second.InputPaths)) {
994 return "InputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -0500995 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400996 if !reflect.DeepEqual(sortedStrings(first.OutputPaths), sortedStrings(second.OutputPaths)) {
997 return "OutputPaths"
Chris Parsons4f069892021-01-15 12:22:41 -0500998 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -0400999 if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
1000 return "SymlinkPaths"
Liz Kammerc49e6822021-06-08 15:04:11 -04001001 }
1002 if first.Depfile != second.Depfile {
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001003 return "Depfile"
Liz Kammerc49e6822021-06-08 15:04:11 -04001004 }
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001005 return ""
Chris Parsons4f069892021-01-15 12:22:41 -05001006}
1007
Usta Shrestha1c4a3ea2022-06-23 16:09:32 -04001008func sortedStrings(stringSlice []string) []string {
1009 sorted := make([]string, len(stringSlice))
1010 copy(sorted, stringSlice)
1011 sort.Strings(sorted)
1012 return sorted
Chris Parsons4f069892021-01-15 12:22:41 -05001013}