• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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	"encoding/json"
19	"fmt"
20	"reflect"
21	"sort"
22	"testing"
23
24	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
25
26	"github.com/google/blueprint/metrics"
27	"google.golang.org/protobuf/proto"
28)
29
30func TestAqueryMultiArchGenrule(t *testing.T) {
31	// This input string is retrieved from a real build of bionic-related genrules.
32	const inputString = `
33{
34 "Artifacts": [
35   { "Id": 1, "path_fragment_id": 1 },
36   { "Id": 2, "path_fragment_id": 6 },
37   { "Id": 3, "path_fragment_id": 8 },
38   { "Id": 4, "path_fragment_id": 12 },
39   { "Id": 5, "path_fragment_id": 19 },
40   { "Id": 6, "path_fragment_id": 20 },
41   { "Id": 7, "path_fragment_id": 21 }],
42 "Actions": [{
43   "target_id": 1,
44   "action_key": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
45   "Mnemonic": "Genrule",
46   "configuration_id": 1,
47   "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm.S"],
48   "environment_variables": [{
49     "Key": "PATH",
50     "Value": "/bin:/usr/bin:/usr/local/bin"
51   }],
52   "input_dep_set_ids": [1],
53   "output_ids": [4],
54   "primary_output_id": 4
55 }, {
56   "target_id": 2,
57   "action_key": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
58   "Mnemonic": "Genrule",
59   "configuration_id": 1,
60   "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86.S"],
61   "environment_variables": [{
62     "Key": "PATH",
63     "Value": "/bin:/usr/bin:/usr/local/bin"
64   }],
65   "input_dep_set_ids": [2],
66   "output_ids": [5],
67   "primary_output_id": 5
68 }, {
69   "target_id": 3,
70   "action_key": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
71   "Mnemonic": "Genrule",
72   "configuration_id": 1,
73   "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86_64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86_64.S"],
74   "environment_variables": [{
75     "Key": "PATH",
76     "Value": "/bin:/usr/bin:/usr/local/bin"
77   }],
78   "input_dep_set_ids": [3],
79   "output_ids": [6],
80   "primary_output_id": 6
81 }, {
82   "target_id": 4,
83   "action_key": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
84   "Mnemonic": "Genrule",
85   "configuration_id": 1,
86   "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm64.S"],
87   "environment_variables": [{
88     "Key": "PATH",
89     "Value": "/bin:/usr/bin:/usr/local/bin"
90   }],
91   "input_dep_set_ids": [4],
92   "output_ids": [7],
93   "primary_output_id": 7
94 }],
95 "Targets": [
96   { "Id": 1, "Label": "@sourceroot//bionic/libc:syscalls-arm", "rule_class_id": 1 },
97   { "Id": 2, "Label": "@sourceroot//bionic/libc:syscalls-x86", "rule_class_id": 1 },
98   { "Id": 3, "Label": "@sourceroot//bionic/libc:syscalls-x86_64", "rule_class_id": 1 },
99   { "Id": 4, "Label": "@sourceroot//bionic/libc:syscalls-arm64", "rule_class_id": 1 }],
100 "dep_set_of_files": [
101   { "Id": 1, "direct_artifact_ids": [1, 2, 3] },
102   { "Id": 2, "direct_artifact_ids": [1, 2, 3] },
103   { "Id": 3, "direct_artifact_ids": [1, 2, 3] },
104   { "Id": 4, "direct_artifact_ids": [1, 2, 3] }],
105 "Configuration": [{
106   "Id": 1,
107   "Mnemonic": "k8-fastbuild",
108   "platform_name": "k8",
109   "Checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
110 }],
111 "rule_classes": [{ "Id": 1, "Name": "genrule"}],
112 "path_fragments": [
113   { "Id": 5, "Label": ".." },
114   { "Id": 4, "Label": "sourceroot", "parent_id": 5 },
115   { "Id": 3, "Label": "bionic", "parent_id": 4 },
116   { "Id": 2, "Label": "libc", "parent_id": 3 },
117   { "Id": 1, "Label": "SYSCALLS.TXT", "parent_id": 2 },
118   { "Id": 7, "Label": "tools", "parent_id": 2 },
119   { "Id": 6, "Label": "gensyscalls.py", "parent_id": 7 },
120   { "Id": 11, "Label": "bazel_tools", "parent_id": 5 },
121   { "Id": 10, "Label": "tools", "parent_id": 11 },
122   { "Id": 9, "Label": "genrule", "parent_id": 10 },
123   { "Id": 8, "Label": "genrule-setup.sh", "parent_id": 9 },
124   { "Id": 18, "Label": "bazel-out" },
125   { "Id": 17, "Label": "sourceroot", "parent_id": 18 },
126   { "Id": 16, "Label": "k8-fastbuild", "parent_id": 17 },
127   { "Id": 15, "Label": "bin", "parent_id": 16 },
128   { "Id": 14, "Label": "bionic", "parent_id": 15 },
129   { "Id": 13, "Label": "libc", "parent_id": 14 },
130   { "Id": 12, "Label": "syscalls-arm.S", "parent_id": 13 },
131   { "Id": 19, "Label": "syscalls-x86.S", "parent_id": 13 },
132   { "Id": 20, "Label": "syscalls-x86_64.S", "parent_id": 13 },
133   { "Id": 21, "Label": "syscalls-arm64.S", "parent_id": 13 }]
134}
135`
136	data, err := JsonToActionGraphContainer(inputString)
137	if err != nil {
138		t.Error(err)
139		return
140	}
141	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
142	var expectedBuildStatements []*BuildStatement
143	for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
144		expectedBuildStatements = append(expectedBuildStatements,
145			&BuildStatement{
146				Command: fmt.Sprintf(
147					"/bin/bash -c 'source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py %s ../sourceroot/bionic/libc/SYSCALLS.TXT > bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S'",
148					arch, arch),
149				OutputPaths: []string{
150					fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
151				},
152				Env: []*analysis_v2_proto.KeyValuePair{
153					{Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
154				},
155				Mnemonic: "Genrule",
156			})
157	}
158	assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
159
160	expectedFlattenedInputs := []string{
161		"../sourceroot/bionic/libc/SYSCALLS.TXT",
162		"../sourceroot/bionic/libc/tools/gensyscalls.py",
163	}
164	// In this example, each depset should have the same expected inputs.
165	for _, actualDepset := range actualDepsets {
166		actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
167		if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
168			t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
169		}
170	}
171}
172
173func TestInvalidOutputId(t *testing.T) {
174	const inputString = `
175{
176 "artifacts": [
177   { "id": 1, "path_fragment_id": 1 },
178   { "id": 2, "path_fragment_id": 2 }],
179 "actions": [{
180   "target_id": 1,
181   "action_key": "x",
182   "mnemonic": "x",
183   "arguments": ["touch", "foo"],
184   "input_dep_set_ids": [1],
185   "output_ids": [3],
186   "primary_output_id": 3
187 }],
188 "dep_set_of_files": [
189   { "id": 1, "direct_artifact_ids": [1, 2] }],
190 "path_fragments": [
191   { "id": 1, "label": "one" },
192   { "id": 2, "label": "two" }]
193}`
194
195	data, err := JsonToActionGraphContainer(inputString)
196	if err != nil {
197		t.Error(err)
198		return
199	}
200	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
201	assertError(t, err, "undefined outputId 3")
202}
203
204func TestInvalidInputDepsetIdFromAction(t *testing.T) {
205	const inputString = `
206{
207 "artifacts": [
208   { "id": 1, "path_fragment_id": 1 },
209   { "id": 2, "path_fragment_id": 2 }],
210 "actions": [{
211   "target_id": 1,
212   "action_key": "x",
213   "mnemonic": "x",
214   "arguments": ["touch", "foo"],
215   "input_dep_set_ids": [2],
216   "output_ids": [1],
217   "primary_output_id": 1
218 }],
219 "dep_set_of_files": [
220   { "id": 1, "direct_artifact_ids": [1, 2] }],
221 "path_fragments": [
222   { "id": 1, "label": "one" },
223   { "id": 2, "label": "two" }]
224}`
225
226	data, err := JsonToActionGraphContainer(inputString)
227	if err != nil {
228		t.Error(err)
229		return
230	}
231	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
232	assertError(t, err, "undefined (not even empty) input depsetId 2")
233}
234
235func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
236	const inputString = `
237{
238 "artifacts": [
239   { "id": 1, "path_fragment_id": 1 },
240   { "id": 2, "path_fragment_id": 2 }],
241 "actions": [{
242   "target_id": 1,
243   "action_key": "x",
244   "mnemonic": "x",
245   "arguments": ["touch", "foo"],
246   "input_dep_set_ids": [1],
247   "output_ids": [1],
248   "primary_output_id": 1
249 }],
250 "dep_set_of_files": [
251   { "id": 1, "direct_artifact_ids": [1, 2], "transitive_dep_set_ids": [42] }],
252 "path_fragments": [
253   { "id": 1, "label": "one"},
254   { "id": 2, "label": "two" }]
255}`
256
257	data, err := JsonToActionGraphContainer(inputString)
258	if err != nil {
259		t.Error(err)
260		return
261	}
262	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
263	assertError(t, err, "undefined input depsetId 42 (referenced by depsetId 1)")
264}
265
266func TestInvalidInputArtifactId(t *testing.T) {
267	const inputString = `
268{
269 "artifacts": [
270   { "id": 1, "path_fragment_id": 1 },
271   { "id": 2, "path_fragment_id": 2 }],
272 "actions": [{
273   "target_id": 1,
274   "action_key": "x",
275   "mnemonic": "x",
276   "arguments": ["touch", "foo"],
277   "input_dep_set_ids": [1],
278   "output_ids": [1],
279   "primary_output_id": 1
280 }],
281 "dep_set_of_files": [
282   { "id": 1, "direct_artifact_ids": [1, 3] }],
283 "path_fragments": [
284   { "id": 1, "label": "one" },
285   { "id": 2, "label": "two" }]
286}`
287
288	data, err := JsonToActionGraphContainer(inputString)
289	if err != nil {
290		t.Error(err)
291		return
292	}
293	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
294	assertError(t, err, "undefined input artifactId 3")
295}
296
297func TestInvalidPathFragmentId(t *testing.T) {
298	const inputString = `
299{
300 "artifacts": [
301   { "id": 1, "path_fragment_id": 1 },
302   { "id": 2, "path_fragment_id": 2 }],
303 "actions": [{
304   "target_id": 1,
305   "action_key": "x",
306   "mnemonic": "x",
307   "arguments": ["touch", "foo"],
308   "input_dep_set_ids": [1],
309   "output_ids": [1],
310   "primary_output_id": 1
311 }],
312 "dep_set_of_files": [
313    { "id": 1, "direct_artifact_ids": [1, 2] }],
314 "path_fragments": [
315   {  "id": 1, "label": "one" },
316   {  "id": 2, "label": "two", "parent_id": 3 }]
317}`
318
319	data, err := JsonToActionGraphContainer(inputString)
320	if err != nil {
321		t.Error(err)
322		return
323	}
324	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
325	assertError(t, err, "undefined path fragment id 3")
326}
327
328func TestDepfiles(t *testing.T) {
329	const inputString = `
330{
331  "artifacts": [
332    { "id": 1, "path_fragment_id": 1 },
333    { "id": 2, "path_fragment_id": 2 },
334    { "id": 3, "path_fragment_id": 3 }],
335  "actions": [{
336    "target_Id": 1,
337    "action_Key": "x",
338    "mnemonic": "x",
339    "arguments": ["touch", "foo"],
340    "input_dep_set_ids": [1],
341    "output_ids": [2, 3],
342    "primary_output_id": 2
343  }],
344  "dep_set_of_files": [
345    { "id": 1, "direct_Artifact_Ids": [1, 2, 3] }],
346  "path_fragments": [
347    { "id": 1, "label": "one" },
348    { "id": 2, "label": "two" },
349    { "id": 3, "label": "two.d" }]
350}`
351
352	data, err := JsonToActionGraphContainer(inputString)
353	if err != nil {
354		t.Error(err)
355		return
356	}
357	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
358	if err != nil {
359		t.Errorf("Unexpected error %q", err)
360	}
361	if expected := 1; len(actual) != expected {
362		t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
363	}
364
365	bs := actual[0]
366	expectedDepfile := "two.d"
367	if bs.Depfile == nil {
368		t.Errorf("Expected depfile %q, but there was none found", expectedDepfile)
369	} else if *bs.Depfile != expectedDepfile {
370		t.Errorf("Expected depfile %q, but got %q", expectedDepfile, *bs.Depfile)
371	}
372}
373
374func TestMultipleDepfiles(t *testing.T) {
375	const inputString = `
376{
377 "artifacts": [
378   { "id": 1, "path_fragment_id": 1 },
379   { "id": 2, "path_fragment_id": 2 },
380   { "id": 3, "path_fragment_id": 3 },
381   { "id": 4, "path_fragment_id": 4 }],
382 "actions": [{
383   "target_id": 1,
384   "action_key": "x",
385   "mnemonic": "x",
386   "arguments": ["touch", "foo"],
387   "input_dep_set_ids": [1],
388   "output_ids": [2,3,4],
389   "primary_output_id": 2
390 }],
391 "dep_set_of_files": [{
392   "id": 1,
393   "direct_artifact_ids": [1, 2, 3, 4]
394 }],
395 "path_fragments": [
396   { "id": 1, "label": "one" },
397   { "id": 2, "label": "two" },
398   { "id": 3, "label": "two.d" },
399   { "id": 4, "label": "other.d" }]
400}`
401
402	data, err := JsonToActionGraphContainer(inputString)
403	if err != nil {
404		t.Error(err)
405		return
406	}
407	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
408	assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
409}
410
411func TestTransitiveInputDepsets(t *testing.T) {
412	// The input aquery for this test comes from a proof-of-concept starlark rule which registers
413	// a single action with many inputs given via a deep depset.
414	const inputString = `
415{
416 "artifacts": [
417  { "id": 1, "path_fragment_id": 1 },
418  { "id": 2, "path_fragment_id": 7 },
419  { "id": 3, "path_fragment_id": 8 },
420  { "id": 4, "path_fragment_id": 9 },
421  { "id": 5, "path_fragment_id": 10 },
422  { "id": 6, "path_fragment_id": 11 },
423  { "id": 7, "path_fragment_id": 12 },
424  { "id": 8, "path_fragment_id": 13 },
425  { "id": 9, "path_fragment_id": 14 },
426  { "id": 10, "path_fragment_id": 15 },
427  { "id": 11, "path_fragment_id": 16 },
428  { "id": 12, "path_fragment_id": 17 },
429  { "id": 13, "path_fragment_id": 18 },
430  { "id": 14, "path_fragment_id": 19 },
431  { "id": 15, "path_fragment_id": 20 },
432  { "id": 16, "path_fragment_id": 21 },
433  { "id": 17, "path_fragment_id": 22 },
434  { "id": 18, "path_fragment_id": 23 },
435  { "id": 19, "path_fragment_id": 24 },
436  { "id": 20, "path_fragment_id": 25 },
437  { "id": 21, "path_fragment_id": 26 }],
438 "actions": [{
439   "target_id": 1,
440   "action_key": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
441   "mnemonic": "Action",
442   "configuration_id": 1,
443   "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"],
444   "input_dep_set_ids": [1],
445   "output_ids": [21],
446   "primary_output_id": 21
447 }],
448 "dep_set_of_files": [
449   { "id": 3, "direct_artifact_ids": [1, 2, 3, 4, 5] },
450   { "id": 4, "direct_artifact_ids": [6, 7, 8, 9, 10] },
451   { "id": 2, "transitive_dep_set_ids": [3, 4], "direct_artifact_ids": [11, 12, 13, 14, 15] },
452   { "id": 5, "direct_artifact_ids": [16, 17, 18, 19] },
453   { "id": 1, "transitive_dep_set_ids": [2, 5], "direct_artifact_ids": [20] }],
454 "path_fragments": [
455   { "id": 6, "label": "bazel-out" },
456   { "id": 5, "label": "sourceroot", "parent_id": 6 },
457   { "id": 4, "label": "k8-fastbuild", "parent_id": 5 },
458   { "id": 3, "label": "bin", "parent_id": 4 },
459   { "id": 2, "label": "testpkg", "parent_id": 3 },
460   { "id": 1, "label": "test_1", "parent_id": 2 },
461   { "id": 7, "label": "test_2", "parent_id": 2 },
462   { "id": 8, "label": "test_3", "parent_id": 2 },
463   { "id": 9, "label": "test_4", "parent_id": 2 },
464   { "id": 10, "label": "test_5", "parent_id": 2 },
465   { "id": 11, "label": "test_6", "parent_id": 2 },
466   { "id": 12, "label": "test_7", "parent_id": 2 },
467	 { "id": 13, "label": "test_8", "parent_id": 2 },
468   { "id": 14, "label": "test_9", "parent_id": 2 },
469   { "id": 15, "label": "test_10", "parent_id": 2 },
470   { "id": 16, "label": "test_11", "parent_id": 2 },
471   { "id": 17, "label": "test_12", "parent_id": 2 },
472   { "id": 18, "label": "test_13", "parent_id": 2 },
473   { "id": 19, "label": "test_14", "parent_id": 2 },
474   { "id": 20, "label": "test_15", "parent_id": 2 },
475   { "id": 21, "label": "test_16", "parent_id": 2 },
476   { "id": 22, "label": "test_17", "parent_id": 2 },
477   { "id": 23, "label": "test_18", "parent_id": 2 },
478   { "id": 24, "label": "test_19", "parent_id": 2 },
479   { "id": 25, "label": "test_root", "parent_id": 2 },
480   { "id": 26,"label": "test_out", "parent_id": 2 }]
481}`
482
483	data, err := JsonToActionGraphContainer(inputString)
484	if err != nil {
485		t.Error(err)
486		return
487	}
488	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
489
490	expectedBuildStatements := []*BuildStatement{
491		&BuildStatement{
492			Command:      "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
493			OutputPaths:  []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
494			Mnemonic:     "Action",
495			SymlinkPaths: []string{},
496		},
497	}
498	assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
499
500	// Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
501	// are given via a deep depset, but the depset is flattened when returned as a
502	// BuildStatement slice.
503	var expectedFlattenedInputs []string
504	for i := 1; i < 20; i++ {
505		expectedFlattenedInputs = append(expectedFlattenedInputs, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
506	}
507	expectedFlattenedInputs = append(expectedFlattenedInputs, "bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root")
508
509	actualDepsetHashes := actualbuildStatements[0].InputDepsetHashes
510	actualFlattenedInputs := flattenDepsets(actualDepsetHashes, actualDepsets)
511	if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
512		t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
513	}
514}
515
516func TestSymlinkTree(t *testing.T) {
517	const inputString = `
518{
519 "artifacts": [
520   { "id": 1, "path_fragment_id": 1 },
521   { "id": 2, "path_fragment_id": 2 }],
522 "actions": [{
523   "target_id": 1,
524   "action_key": "x",
525   "mnemonic": "SymlinkTree",
526   "configuration_id": 1,
527   "input_dep_set_ids": [1],
528   "output_ids": [2],
529   "primary_output_id": 2,
530   "execution_platform": "//build/bazel/platforms:linux_x86_64"
531 }],
532 "path_fragments": [
533   { "id": 1, "label": "foo.manifest" },
534   { "id": 2, "label": "foo.runfiles/MANIFEST" }],
535 "dep_set_of_files": [
536   { "id": 1, "direct_artifact_ids": [1] }]
537}
538`
539	data, err := JsonToActionGraphContainer(inputString)
540	if err != nil {
541		t.Error(err)
542		return
543	}
544	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
545	if err != nil {
546		t.Errorf("Unexpected error %q", err)
547	}
548	assertBuildStatements(t, []*BuildStatement{
549		&BuildStatement{
550			Command:      "",
551			OutputPaths:  []string{"foo.runfiles/MANIFEST"},
552			Mnemonic:     "SymlinkTree",
553			InputPaths:   []string{"foo.manifest"},
554			SymlinkPaths: []string{},
555		},
556	}, actual)
557}
558
559func TestBazelOutRemovalFromInputDepsets(t *testing.T) {
560	const inputString = `{
561 "artifacts": [
562   { "id": 1, "path_fragment_id": 10 },
563   { "id": 2, "path_fragment_id": 20 },
564   { "id": 3, "path_fragment_id": 30 },
565   { "id": 4, "path_fragment_id": 40 }],
566 "dep_set_of_files": [{
567   "id": 1111,
568   "direct_artifact_ids": [3 , 4]
569 }, {
570   "id": 2222,
571   "direct_artifact_ids": [3]
572 }],
573 "actions": [{
574   "target_id": 100,
575   "action_key": "x",
576   "input_dep_set_ids": [1111, 2222],
577   "mnemonic": "x",
578   "arguments": ["bogus", "command"],
579   "output_ids": [2],
580   "primary_output_id": 1
581 }],
582 "path_fragments": [
583   { "id": 10, "label": "input" },
584   { "id": 20, "label": "output" },
585   { "id": 30, "label": "dep1", "parent_id": 50 },
586   { "id": 40, "label": "dep2", "parent_id": 60 },
587   { "id": 50, "label": "bazel_tools", "parent_id": 60 },
588   { "id": 60, "label": ".."}
589 ]
590}`
591	/* depsets
592	       1111  2222
593	       /  \   |
594	../dep2    ../bazel_tools/dep1
595	*/
596	data, err := JsonToActionGraphContainer(inputString)
597	if err != nil {
598		t.Error(err)
599		return
600	}
601	actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
602	if len(actualDepsets) != 1 {
603		t.Errorf("expected 1 depset but found %#v", actualDepsets)
604		return
605	}
606	dep2Found := false
607	for _, dep := range flattenDepsets([]string{actualDepsets[0].ContentHash}, actualDepsets) {
608		if dep == "../bazel_tools/dep1" {
609			t.Errorf("dependency %s expected to be removed but still exists", dep)
610		} else if dep == "../dep2" {
611			dep2Found = true
612		}
613	}
614	if !dep2Found {
615		t.Errorf("dependency ../dep2 expected but not found")
616	}
617
618	expectedBuildStatement := &BuildStatement{
619		Command:      "bogus command",
620		OutputPaths:  []string{"output"},
621		Mnemonic:     "x",
622		SymlinkPaths: []string{},
623	}
624	buildStatementFound := false
625	for _, actualBuildStatement := range actualBuildStatements {
626		if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
627			buildStatementFound = true
628			break
629		}
630	}
631	if !buildStatementFound {
632		t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
633		return
634	}
635}
636
637func TestBazelOutRemovalFromTransitiveInputDepsets(t *testing.T) {
638	const inputString = `{
639 "artifacts": [
640   { "id": 1, "path_fragment_id": 10 },
641   { "id": 2, "path_fragment_id": 20 },
642   { "id": 3, "path_fragment_id": 30 }],
643 "dep_set_of_files": [{
644   "id": 1111,
645   "transitive_dep_set_ids": [2222]
646 }, {
647   "id": 2222,
648   "direct_artifact_ids": [3]
649 }, {
650   "id": 3333,
651   "direct_artifact_ids": [3]
652 }, {
653   "id": 4444,
654   "transitive_dep_set_ids": [3333]
655 }],
656 "actions": [{
657   "target_id": 100,
658   "action_key": "x",
659   "input_dep_set_ids": [1111, 4444],
660   "mnemonic": "x",
661   "arguments": ["bogus", "command"],
662   "output_ids": [2],
663   "primary_output_id": 1
664 }],
665 "path_fragments": [
666   { "id": 10, "label": "input" },
667   { "id": 20, "label": "output" },
668   { "id": 30, "label": "dep", "parent_id": 50 },
669   { "id": 50, "label": "bazel_tools", "parent_id": 60 },
670   { "id": 60, "label": ".."}
671 ]
672}`
673	/* depsets
674	    1111    4444
675	     ||      ||
676	    2222    3333
677	      |      |
678	../bazel_tools/dep
679	Note: in dep_set_of_files:
680	  1111 appears BEFORE its dependency,2222 while
681	  4444 appears AFTER its dependency 3333
682	and this test shows that that order doesn't affect empty depset pruning
683	*/
684	data, err := JsonToActionGraphContainer(inputString)
685	if err != nil {
686		t.Error(err)
687		return
688	}
689	actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
690	if len(actualDepsets) != 0 {
691		t.Errorf("expected 0 depsets but found %#v", actualDepsets)
692		return
693	}
694
695	expectedBuildStatement := &BuildStatement{
696		Command:     "bogus command",
697		OutputPaths: []string{"output"},
698		Mnemonic:    "x",
699	}
700	buildStatementFound := false
701	for _, actualBuildStatement := range actualBuildStatements {
702		if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
703			buildStatementFound = true
704			break
705		}
706	}
707	if !buildStatementFound {
708		t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
709		return
710	}
711}
712
713func TestMiddlemenAction(t *testing.T) {
714	const inputString = `
715{
716 "artifacts": [
717   { "id": 1, "path_fragment_id": 1 },
718   { "id": 2, "path_fragment_id": 2 },
719   { "id": 3, "path_fragment_id": 3 },
720   { "id": 4, "path_fragment_id": 4 },
721   { "id": 5, "path_fragment_id": 5 },
722   { "id": 6, "path_fragment_id": 6 }],
723 "path_fragments": [
724   { "id": 1, "label": "middleinput_one" },
725   { "id": 2, "label": "middleinput_two" },
726   { "id": 3, "label": "middleman_artifact" },
727   { "id": 4, "label": "maininput_one" },
728   { "id": 5, "label": "maininput_two" },
729   { "id": 6, "label": "output" }],
730 "dep_set_of_files": [
731   { "id": 1, "direct_artifact_ids": [1, 2] },
732   { "id": 2, "direct_artifact_ids": [3, 4, 5] }],
733 "actions": [{
734   "target_id": 1,
735   "action_key": "x",
736   "mnemonic": "Middleman",
737   "arguments": ["touch", "foo"],
738   "input_dep_set_ids": [1],
739   "output_ids": [3],
740   "primary_output_id": 3
741 }, {
742   "target_id": 2,
743   "action_key": "y",
744   "mnemonic": "Main action",
745   "arguments": ["touch", "foo"],
746   "input_dep_set_ids": [2],
747   "output_ids": [6],
748   "primary_output_id": 6
749 }]
750}`
751	data, err := JsonToActionGraphContainer(inputString)
752	if err != nil {
753		t.Error(err)
754		return
755	}
756	actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data, &metrics.EventHandler{})
757	if err != nil {
758		t.Errorf("Unexpected error %q", err)
759	}
760	if expected := 2; len(actualBuildStatements) != expected {
761		t.Fatalf("Expected %d build statements, got %d %#v", expected, len(actualBuildStatements), actualBuildStatements)
762	}
763
764	expectedDepsetFiles := [][]string{
765		{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"},
766		{"middleinput_one", "middleinput_two"},
767	}
768	assertFlattenedDepsets(t, actualDepsets, expectedDepsetFiles)
769
770	bs := actualBuildStatements[0]
771	if len(bs.InputPaths) > 0 {
772		t.Errorf("Expected main action raw inputs to be empty, but got %q", bs.InputPaths)
773	}
774
775	expectedOutputs := []string{"output"}
776	if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) {
777		t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths)
778	}
779
780	expectedFlattenedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
781	actualFlattenedInputs := flattenDepsets(bs.InputDepsetHashes, actualDepsets)
782
783	if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
784		t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
785	}
786
787	bs = actualBuildStatements[1]
788	if bs != nil {
789		t.Errorf("Expected nil action for skipped")
790	}
791}
792
793// Returns the contents of given depsets in concatenated post order.
794func flattenDepsets(depsetHashesToFlatten []string, allDepsets []AqueryDepset) []string {
795	depsetsByHash := map[string]AqueryDepset{}
796	for _, depset := range allDepsets {
797		depsetsByHash[depset.ContentHash] = depset
798	}
799	var result []string
800	for _, depsetId := range depsetHashesToFlatten {
801		result = append(result, flattenDepset(depsetId, depsetsByHash)...)
802	}
803	return result
804}
805
806// Returns the contents of a given depset in post order.
807func flattenDepset(depsetHashToFlatten string, allDepsets map[string]AqueryDepset) []string {
808	depset := allDepsets[depsetHashToFlatten]
809	var result []string
810	for _, depsetId := range depset.TransitiveDepSetHashes {
811		result = append(result, flattenDepset(depsetId, allDepsets)...)
812	}
813	result = append(result, depset.DirectArtifacts...)
814	return result
815}
816
817func assertFlattenedDepsets(t *testing.T, actualDepsets []AqueryDepset, expectedDepsetFiles [][]string) {
818	t.Helper()
819	if len(actualDepsets) != len(expectedDepsetFiles) {
820		t.Errorf("Expected %d depsets, but got %d depsets", len(expectedDepsetFiles), len(actualDepsets))
821	}
822	for i, actualDepset := range actualDepsets {
823		actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
824		if !reflect.DeepEqual(actualFlattenedInputs, expectedDepsetFiles[i]) {
825			t.Errorf("Expected depset files: %v, but got %v", expectedDepsetFiles[i], actualFlattenedInputs)
826		}
827	}
828}
829
830func TestSimpleSymlink(t *testing.T) {
831	const inputString = `
832{
833 "artifacts": [
834   { "id": 1, "path_fragment_id": 3 },
835   { "id": 2, "path_fragment_id": 5 }],
836 "actions": [{
837   "target_id": 1,
838   "action_key": "x",
839   "mnemonic": "Symlink",
840   "input_dep_set_ids": [1],
841   "output_ids": [2],
842   "primary_output_id": 2
843 }],
844 "dep_set_of_files": [
845   { "id": 1, "direct_artifact_ids": [1] }],
846 "path_fragments": [
847   { "id": 1, "label": "one" },
848   { "id": 2, "label": "file_subdir", "parent_id": 1 },
849   { "id": 3, "label": "file", "parent_id": 2 },
850   { "id": 4, "label": "symlink_subdir", "parent_id": 1 },
851   { "id": 5, "label": "symlink", "parent_id": 4 }]
852}`
853	data, err := JsonToActionGraphContainer(inputString)
854	if err != nil {
855		t.Error(err)
856		return
857	}
858	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
859
860	if err != nil {
861		t.Errorf("Unexpected error %q", err)
862	}
863
864	expectedBuildStatements := []*BuildStatement{
865		&BuildStatement{
866			Command: "mkdir -p one/symlink_subdir && " +
867				"rm -f one/symlink_subdir/symlink && " +
868				"ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
869			InputPaths:   []string{"one/file_subdir/file"},
870			OutputPaths:  []string{"one/symlink_subdir/symlink"},
871			SymlinkPaths: []string{"one/symlink_subdir/symlink"},
872			Mnemonic:     "Symlink",
873		},
874	}
875	assertBuildStatements(t, actual, expectedBuildStatements)
876}
877
878func TestSymlinkQuotesPaths(t *testing.T) {
879	const inputString = `
880{
881 "artifacts": [
882   { "id": 1, "path_fragment_id": 3 },
883   { "id": 2, "path_fragment_id": 5 }],
884 "actions": [{
885   "target_id": 1,
886   "action_key": "x",
887   "mnemonic": "SolibSymlink",
888   "input_dep_set_ids": [1],
889   "output_ids": [2],
890   "primary_output_id": 2
891 }],
892 "dep_set_of_files": [
893   { "id": 1, "direct_artifact_ids": [1] }],
894 "path_fragments": [
895   { "id": 1, "label": "one" },
896   { "id": 2, "label": "file subdir", "parent_id": 1 },
897   { "id": 3, "label": "file", "parent_id": 2 },
898   { "id": 4, "label": "symlink subdir", "parent_id": 1 },
899   { "id": 5, "label": "symlink", "parent_id": 4 }]
900}`
901
902	data, err := JsonToActionGraphContainer(inputString)
903	if err != nil {
904		t.Error(err)
905		return
906	}
907	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
908	if err != nil {
909		t.Errorf("Unexpected error %q", err)
910	}
911
912	expectedBuildStatements := []*BuildStatement{
913		&BuildStatement{
914			Command: "mkdir -p 'one/symlink subdir' && " +
915				"rm -f 'one/symlink subdir/symlink' && " +
916				"ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
917			InputPaths:   []string{"one/file subdir/file"},
918			OutputPaths:  []string{"one/symlink subdir/symlink"},
919			SymlinkPaths: []string{"one/symlink subdir/symlink"},
920			Mnemonic:     "SolibSymlink",
921		},
922	}
923	assertBuildStatements(t, expectedBuildStatements, actual)
924}
925
926func TestSymlinkMultipleInputs(t *testing.T) {
927	const inputString = `
928{
929 "artifacts": [
930   { "id": 1, "path_fragment_id": 1 },
931   { "id": 2, "path_fragment_id": 2 },
932   { "id": 3, "path_fragment_id": 3 }],
933 "actions": [{
934   "target_id": 1,
935   "action_key": "x",
936   "mnemonic": "Symlink",
937   "input_dep_set_ids": [1],
938   "output_ids": [3],
939   "primary_output_id": 3
940 }],
941 "dep_set_of_files": [{ "id": 1, "direct_artifact_ids": [1,2] }],
942 "path_fragments": [
943   { "id": 1, "label": "file" },
944   { "id": 2, "label": "other_file" },
945   { "id": 3, "label": "symlink" }]
946}`
947
948	data, err := JsonToActionGraphContainer(inputString)
949	if err != nil {
950		t.Error(err)
951		return
952	}
953	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
954	assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
955}
956
957func TestSymlinkMultipleOutputs(t *testing.T) {
958	const inputString = `
959{
960 "artifacts": [
961   { "id": 1, "path_fragment_id": 1 },
962   { "id": 3, "path_fragment_id": 3 }],
963 "actions": [{
964   "target_id": 1,
965   "action_key": "x",
966   "mnemonic": "Symlink",
967   "input_dep_set_ids": [1],
968   "output_ids": [2,3],
969   "primary_output_id": 2
970 }],
971 "dep_set_of_files": [
972   { "id": 1, "direct_artifact_ids": [1] }],
973 "path_fragments": [
974   { "id": 1, "label": "file" },
975   { "id": 2, "label": "symlink" },
976   { "id": 3,  "label": "other_symlink" }]
977}`
978
979	data, err := JsonToActionGraphContainer(inputString)
980	if err != nil {
981		t.Error(err)
982		return
983	}
984	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
985	assertError(t, err, "undefined outputId 2")
986}
987
988func TestTemplateExpandActionSubstitutions(t *testing.T) {
989	const inputString = `
990{
991 "artifacts": [{
992   "id": 1,
993   "path_fragment_id": 1
994 }],
995 "actions": [{
996   "target_id": 1,
997   "action_key": "x",
998   "mnemonic": "TemplateExpand",
999   "configuration_id": 1,
1000   "output_ids": [1],
1001   "primary_output_id": 1,
1002   "execution_platform": "//build/bazel/platforms:linux_x86_64",
1003   "template_content": "Test template substitutions: %token1%, %python_binary%",
1004   "substitutions": [
1005     { "key": "%token1%", "value": "abcd" },
1006     { "key": "%python_binary%", "value": "python3" }]
1007 }],
1008 "path_fragments": [
1009   { "id": 1, "label": "template_file" }]
1010}`
1011
1012	data, err := JsonToActionGraphContainer(inputString)
1013	if err != nil {
1014		t.Error(err)
1015		return
1016	}
1017	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
1018	if err != nil {
1019		t.Errorf("Unexpected error %q", err)
1020	}
1021
1022	expectedBuildStatements := []*BuildStatement{
1023		&BuildStatement{
1024			Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
1025				"chmod a+x template_file'",
1026			OutputPaths:  []string{"template_file"},
1027			Mnemonic:     "TemplateExpand",
1028			SymlinkPaths: []string{},
1029		},
1030	}
1031	assertBuildStatements(t, expectedBuildStatements, actual)
1032}
1033
1034func TestTemplateExpandActionNoOutput(t *testing.T) {
1035	const inputString = `
1036{
1037 "artifacts": [
1038   { "id": 1, "path_fragment_id": 1 }],
1039 "actions": [{
1040   "target_id": 1,
1041   "action_key": "x",
1042   "mnemonic": "TemplateExpand",
1043   "configuration_id": 1,
1044   "primary_output_id": 1,
1045   "execution_platform": "//build/bazel/platforms:linux_x86_64",
1046   "templateContent": "Test template substitutions: %token1%, %python_binary%",
1047   "substitutions": [
1048     { "key": "%token1%", "value": "abcd" },
1049     { "key": "%python_binary%", "value": "python3" }]
1050 }],
1051 "path_fragments": [
1052   { "id": 1, "label": "template_file" }]
1053}`
1054
1055	data, err := JsonToActionGraphContainer(inputString)
1056	if err != nil {
1057		t.Error(err)
1058		return
1059	}
1060	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
1061	assertError(t, err, `Expect 1 output to template expand action, got: output []`)
1062}
1063
1064func TestFileWrite(t *testing.T) {
1065	const inputString = `
1066{
1067 "artifacts": [
1068   { "id": 1, "path_fragment_id": 1 }],
1069 "actions": [{
1070   "target_id": 1,
1071   "action_key": "x",
1072   "mnemonic": "FileWrite",
1073   "configuration_id": 1,
1074   "output_ids": [1],
1075   "primary_output_id": 1,
1076   "execution_platform": "//build/bazel/platforms:linux_x86_64",
1077   "file_contents": "file data\n"
1078 }],
1079 "path_fragments": [
1080   { "id": 1, "label": "foo.manifest" }]
1081}
1082`
1083	data, err := JsonToActionGraphContainer(inputString)
1084	if err != nil {
1085		t.Error(err)
1086		return
1087	}
1088	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
1089	if err != nil {
1090		t.Errorf("Unexpected error %q", err)
1091	}
1092	assertBuildStatements(t, []*BuildStatement{
1093		&BuildStatement{
1094			OutputPaths:  []string{"foo.manifest"},
1095			Mnemonic:     "FileWrite",
1096			FileContents: "file data\n",
1097			SymlinkPaths: []string{},
1098		},
1099	}, actual)
1100}
1101
1102func TestSourceSymlinkManifest(t *testing.T) {
1103	const inputString = `
1104{
1105 "artifacts": [
1106   { "id": 1, "path_fragment_id": 1 }],
1107 "actions": [{
1108   "target_id": 1,
1109   "action_key": "x",
1110   "mnemonic": "SourceSymlinkManifest",
1111   "configuration_id": 1,
1112   "output_ids": [1],
1113   "primary_output_id": 1,
1114   "execution_platform": "//build/bazel/platforms:linux_x86_64",
1115   "file_contents": "symlink target\n"
1116 }],
1117 "path_fragments": [
1118   { "id": 1, "label": "foo.manifest" }]
1119}
1120`
1121	data, err := JsonToActionGraphContainer(inputString)
1122	if err != nil {
1123		t.Error(err)
1124		return
1125	}
1126	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
1127	if err != nil {
1128		t.Errorf("Unexpected error %q", err)
1129	}
1130	assertBuildStatements(t, []*BuildStatement{
1131		&BuildStatement{
1132			OutputPaths:  []string{"foo.manifest"},
1133			Mnemonic:     "SourceSymlinkManifest",
1134			SymlinkPaths: []string{},
1135		},
1136	}, actual)
1137}
1138
1139func assertError(t *testing.T, err error, expected string) {
1140	t.Helper()
1141	if err == nil {
1142		t.Errorf("expected error '%s', but got no error", expected)
1143	} else if err.Error() != expected {
1144		t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
1145	}
1146}
1147
1148// Asserts that the given actual build statements match the given expected build statements.
1149// Build statement equivalence is determined using buildStatementEquals.
1150func assertBuildStatements(t *testing.T, expected []*BuildStatement, actual []*BuildStatement) {
1151	t.Helper()
1152	if len(expected) != len(actual) {
1153		t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
1154			len(expected), len(actual), expected, actual)
1155		return
1156	}
1157	type compareFn = func(i int, j int) bool
1158	byCommand := func(slice []*BuildStatement) compareFn {
1159		return func(i int, j int) bool {
1160			if slice[i] == nil {
1161				return false
1162			} else if slice[j] == nil {
1163				return false
1164			}
1165			return slice[i].Command < slice[j].Command
1166		}
1167	}
1168	sort.SliceStable(expected, byCommand(expected))
1169	sort.SliceStable(actual, byCommand(actual))
1170	for i, actualStatement := range actual {
1171		expectedStatement := expected[i]
1172		if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
1173			t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
1174				differingField, actualStatement, expectedStatement)
1175			return
1176		}
1177	}
1178}
1179
1180func buildStatementEquals(first *BuildStatement, second *BuildStatement) string {
1181	if (first == nil) != (second == nil) {
1182		return "Nil"
1183	}
1184	if first.Mnemonic != second.Mnemonic {
1185		return "Mnemonic"
1186	}
1187	if first.Command != second.Command {
1188		return "Command"
1189	}
1190	// Ordering is significant for environment variables.
1191	if !reflect.DeepEqual(first.Env, second.Env) {
1192		return "Env"
1193	}
1194	// Ordering is irrelevant for input and output paths, so compare sets.
1195	if !reflect.DeepEqual(sortedStrings(first.InputPaths), sortedStrings(second.InputPaths)) {
1196		return "InputPaths"
1197	}
1198	if !reflect.DeepEqual(sortedStrings(first.OutputPaths), sortedStrings(second.OutputPaths)) {
1199		return "OutputPaths"
1200	}
1201	if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
1202		return "SymlinkPaths"
1203	}
1204	if first.Depfile != second.Depfile {
1205		return "Depfile"
1206	}
1207	return ""
1208}
1209
1210func sortedStrings(stringSlice []string) []string {
1211	sorted := make([]string, len(stringSlice))
1212	copy(sorted, stringSlice)
1213	sort.Strings(sorted)
1214	return sorted
1215}
1216
1217// Transform the json format to ActionGraphContainer
1218func JsonToActionGraphContainer(inputString string) ([]byte, error) {
1219	var aqueryProtoResult analysis_v2_proto.ActionGraphContainer
1220	err := json.Unmarshal([]byte(inputString), &aqueryProtoResult)
1221	if err != nil {
1222		return []byte(""), err
1223	}
1224	data, _ := proto.Marshal(&aqueryProtoResult)
1225	return data, err
1226}
1227