• 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.
14package main
15
16import (
17	"strings"
18	"testing"
19
20	"github.com/google/blueprint/parser"
21	"github.com/google/blueprint/proptools"
22)
23
24var testCases = []struct {
25	name            string
26	input           string
27	output          string
28	property        string
29	addSet          string
30	removeSet       string
31	addLiteral      *string
32	setString       *string
33	setBool         *string
34	removeProperty  bool
35	replaceProperty string
36	moveProperty    bool
37	newLocation     string
38}{
39	{
40		name: "add",
41		input: `
42			cc_foo {
43				name: "foo",
44			}
45		`,
46		output: `
47			cc_foo {
48				name: "foo",
49				deps: ["bar"],
50			}
51		`,
52		property: "deps",
53		addSet:   "bar",
54	},
55	{
56		name: "remove",
57		input: `
58			cc_foo {
59				name: "foo",
60				deps: ["bar"],
61			}
62		`,
63		output: `
64			cc_foo {
65				name: "foo",
66				deps: [],
67			}
68		`,
69		property:  "deps",
70		removeSet: "bar",
71	},
72	{
73		name: "nested add",
74		input: `
75			cc_foo {
76				name: "foo",
77			}
78		`,
79		output: `
80			cc_foo {
81				name: "foo",
82				arch: {
83					arm: {
84						deps: [
85							"dep2",
86							"nested_dep",],
87					},
88				},
89			}
90		`,
91		property: "arch.arm.deps",
92		addSet:   "nested_dep,dep2",
93	},
94	{
95		name: "nested remove",
96		input: `
97			cc_foo {
98				name: "foo",
99				arch: {
100					arm: {
101						deps: [
102							"dep2",
103							"nested_dep",
104						],
105					},
106				},
107			}
108		`,
109		output: `
110			cc_foo {
111				name: "foo",
112				arch: {
113					arm: {
114						deps: [
115						],
116					},
117				},
118			}
119		`,
120		property:  "arch.arm.deps",
121		removeSet: "nested_dep,dep2",
122	},
123	{
124		name: "add existing",
125		input: `
126			cc_foo {
127				name: "foo",
128				arch: {
129					arm: {
130						deps: [
131							"nested_dep",
132							"dep2",
133						],
134					},
135				},
136			}
137		`,
138		output: `
139			cc_foo {
140				name: "foo",
141				arch: {
142					arm: {
143						deps: [
144							"nested_dep",
145							"dep2",
146						],
147					},
148				},
149			}
150		`,
151		property: "arch.arm.deps",
152		addSet:   "dep2,dep2",
153	},
154	{
155		name: "remove missing",
156		input: `
157			cc_foo {
158				name: "foo",
159				arch: {
160					arm: {
161						deps: [
162							"nested_dep",
163							"dep2",
164						],
165					},
166				},
167			}
168		`,
169		output: `
170			cc_foo {
171				name: "foo",
172				arch: {
173					arm: {
174						deps: [
175							"nested_dep",
176							"dep2",
177						],
178					},
179				},
180			}
181		`,
182		property:  "arch.arm.deps",
183		removeSet: "dep3,dep4",
184	},
185	{
186		name: "remove non existent",
187		input: `
188			cc_foo {
189				name: "foo",
190			}
191		`,
192		output: `
193			cc_foo {
194				name: "foo",
195			}
196		`,
197		property:  "deps",
198		removeSet: "bar",
199	},
200	{
201		name: "remove non existent nested",
202		input: `
203			cc_foo {
204				name: "foo",
205				arch: {},
206			}
207		`,
208		output: `
209			cc_foo {
210				name: "foo",
211				arch: {},
212			}
213		`,
214		property:  "arch.arm.deps",
215		removeSet: "dep3,dep4",
216	},
217	{
218		name: "add numeric sorted",
219		input: `
220			cc_foo {
221				name: "foo",
222				versions: ["1", "2"],
223			}
224		`,
225		output: `
226			cc_foo {
227				name: "foo",
228				versions: [
229					"1",
230					"2",
231					"10",
232				],
233			}
234		`,
235		property: "versions",
236		addSet:   "10",
237	},
238	{
239		name: "add mixed sorted",
240		input: `
241			cc_foo {
242				name: "foo",
243				deps: ["bar-v1-bar", "bar-v2-bar"],
244			}
245		`,
246		output: `
247			cc_foo {
248				name: "foo",
249				deps: [
250					"bar-v1-bar",
251					"bar-v2-bar",
252					"bar-v10-bar",
253				],
254			}
255		`,
256		property: "deps",
257		addSet:   "bar-v10-bar",
258	},
259	{
260		name:  "add a struct with literal",
261		input: `cc_foo {name: "foo"}`,
262		output: `cc_foo {
263    name: "foo",
264    structs: [
265        {
266            version: "1",
267            imports: [
268                "bar1",
269                "bar2",
270            ],
271        },
272    ],
273}
274`,
275		property:   "structs",
276		addLiteral: proptools.StringPtr(`{version: "1", imports: ["bar1", "bar2"]}`),
277	},
278	{
279		name: "set string",
280		input: `
281			cc_foo {
282				name: "foo",
283			}
284		`,
285		output: `
286			cc_foo {
287				name: "foo",
288				foo: "bar",
289			}
290		`,
291		property:  "foo",
292		setString: proptools.StringPtr("bar"),
293	},
294	{
295		name: "set existing string",
296		input: `
297			cc_foo {
298				name: "foo",
299				foo: "baz",
300			}
301		`,
302		output: `
303			cc_foo {
304				name: "foo",
305				foo: "bar",
306			}
307		`,
308		property:  "foo",
309		setString: proptools.StringPtr("bar"),
310	},
311	{
312		name: "set bool",
313		input: `
314			cc_foo {
315				name: "foo",
316			}
317		`,
318		output: `
319			cc_foo {
320				name: "foo",
321				foo: true,
322			}
323		`,
324		property: "foo",
325		setBool:  proptools.StringPtr("true"),
326	},
327	{
328		name: "set existing bool",
329		input: `
330			cc_foo {
331				name: "foo",
332				foo: true,
333			}
334		`,
335		output: `
336			cc_foo {
337				name: "foo",
338				foo: false,
339			}
340		`,
341		property: "foo",
342		setBool:  proptools.StringPtr("false"),
343	},
344	{
345		name: "remove existing property",
346		input: `
347			cc_foo {
348				name: "foo",
349				foo: "baz",
350			}
351		`,
352		output: `
353			cc_foo {
354				name: "foo",
355			}
356		`,
357		property:       "foo",
358		removeProperty: true,
359	}, {
360		name: "remove nested property",
361		input: `
362		cc_foo {
363			name: "foo",
364			foo: {
365				bar: "baz",
366			},
367		}
368	`,
369		output: `
370		cc_foo {
371			name: "foo",
372			foo: {},
373		}
374	`,
375		property:       "foo.bar",
376		removeProperty: true,
377	}, {
378		name: "remove non-existing property",
379		input: `
380			cc_foo {
381				name: "foo",
382				foo: "baz",
383			}
384		`,
385		output: `
386			cc_foo {
387				name: "foo",
388				foo: "baz",
389			}
390		`,
391		property:       "bar",
392		removeProperty: true,
393	}, {
394		name:     "replace property",
395		property: "deps",
396		input: `
397			cc_foo {
398				name: "foo",
399				deps: ["baz", "unchanged"],
400			}
401		`,
402		output: `
403			cc_foo {
404				name: "foo",
405				deps: [
406                "baz_lib",
407                "unchanged",
408				],
409			}
410		`,
411		replaceProperty: "baz=baz_lib,foobar=foobar_lib",
412	}, {
413		name:     "replace property multiple modules",
414		property: "deps,required",
415		input: `
416			cc_foo {
417				name: "foo",
418				deps: ["baz", "unchanged"],
419				unchanged: ["baz"],
420				required: ["foobar"],
421			}
422		`,
423		output: `
424			cc_foo {
425				name: "foo",
426				deps: [
427								"baz_lib",
428								"unchanged",
429				],
430				unchanged: ["baz"],
431				required: ["foobar_lib"],
432			}
433		`,
434		replaceProperty: "baz=baz_lib,foobar=foobar_lib",
435	}, {
436		name:     "replace property string value",
437		property: "name",
438		input: `
439			cc_foo {
440				name: "foo",
441				deps: ["baz"],
442				unchanged: ["baz"],
443				required: ["foobar"],
444			}
445		`,
446		output: `
447			cc_foo {
448				name: "foo_lib",
449				deps: ["baz"],
450				unchanged: ["baz"],
451				required: ["foobar"],
452			}
453		`,
454		replaceProperty: "foo=foo_lib",
455	}, {
456		name:     "replace property string and list values",
457		property: "name,deps",
458		input: `
459			cc_foo {
460				name: "foo",
461				deps: ["baz"],
462				unchanged: ["baz"],
463				required: ["foobar"],
464			}
465		`,
466		output: `
467			cc_foo {
468				name: "foo_lib",
469				deps: ["baz_lib"],
470				unchanged: ["baz"],
471				required: ["foobar"],
472			}
473		`,
474		replaceProperty: "foo=foo_lib,baz=baz_lib",
475	}, {
476		name: "move contents of property into non-existing property",
477		input: `
478			cc_foo {
479				name: "foo",
480				bar: ["barContents"],
481				}
482`,
483		output: `
484			cc_foo {
485				name: "foo",
486				baz: ["barContents"],
487			}
488		`,
489		property:     "bar",
490		moveProperty: true,
491		newLocation:  "baz",
492	}, {
493		name: "move contents of property into existing property",
494		input: `
495			cc_foo {
496				name: "foo",
497				baz: ["bazContents"],
498				bar: ["barContents"],
499			}
500		`,
501		output: `
502			cc_foo {
503				name: "foo",
504				baz: [
505					"bazContents",
506					"barContents",
507				],
508
509			}
510		`,
511		property:     "bar",
512		moveProperty: true,
513		newLocation:  "baz",
514	}, {
515		name: "replace nested",
516		input: `
517		cc_foo {
518			name: "foo",
519			foo: {
520				bar: "baz",
521			},
522		}
523	`,
524		output: `
525		cc_foo {
526			name: "foo",
527			foo: {
528				bar: "baz2",
529			},
530		}
531	`,
532		property:        "foo.bar",
533		replaceProperty: "baz=baz2",
534	},
535}
536
537func simplifyModuleDefinition(def string) string {
538	var result string
539	for _, line := range strings.Split(def, "\n") {
540		result += strings.TrimSpace(line)
541	}
542	return result
543}
544func TestProcessModule(t *testing.T) {
545	for i, testCase := range testCases {
546		t.Run(testCase.name, func(t *testing.T) {
547			targetedProperties.Set(testCase.property)
548			addIdents.Set(testCase.addSet)
549			removeIdents.Set(testCase.removeSet)
550			removeProperty = &testCase.removeProperty
551			moveProperty = &testCase.moveProperty
552			newLocation = testCase.newLocation
553			setString = testCase.setString
554			setBool = testCase.setBool
555			addLiteral = testCase.addLiteral
556			replaceProperty.Set(testCase.replaceProperty)
557
558			inAst, errs := parser.ParseAndEval("", strings.NewReader(testCase.input), parser.NewScope(nil))
559			if len(errs) > 0 {
560				for _, err := range errs {
561					t.Errorf("  %s", err)
562				}
563				t.Errorf("failed to parse:")
564				t.Errorf("%+v", testCase)
565				t.FailNow()
566			}
567			if inModule, ok := inAst.Defs[0].(*parser.Module); !ok {
568				t.Fatalf("  input must only contain a single module definition: %s", testCase.input)
569			} else {
570				for _, p := range targetedProperties.properties {
571					_, errs := processModuleProperty(inModule, "", inAst, p)
572					if len(errs) > 0 {
573						t.Errorf("test case %d:", i)
574						for _, err := range errs {
575							t.Errorf("  %s", err)
576						}
577					}
578
579				}
580				inModuleText, _ := parser.Print(inAst)
581				inModuleString := string(inModuleText)
582				if simplifyModuleDefinition(inModuleString) != simplifyModuleDefinition(testCase.output) {
583					t.Errorf("test case %d:", i)
584					t.Errorf("expected module definition:")
585					t.Errorf("  %s", testCase.output)
586					t.Errorf("actual module definition:")
587					t.Errorf("  %s", inModuleString)
588				}
589			}
590		})
591	}
592}
593
594func TestReplacementsCycleError(t *testing.T) {
595	cycleString := "old1=new1,new1=old1"
596	err := replaceProperty.Set(cycleString)
597
598	if err.Error() != "Duplicated replacement name new1" {
599		t.Errorf("Error message did not match")
600		t.Errorf("Expected ")
601		t.Errorf(" Duplicated replacement name new1")
602		t.Errorf("actual error:")
603		t.Errorf("  %s", err.Error())
604		t.FailNow()
605	}
606}
607
608func TestReplacementsDuplicatedError(t *testing.T) {
609	cycleString := "a=b,a=c"
610	err := replaceProperty.Set(cycleString)
611
612	if err.Error() != "Duplicated replacement name a" {
613		t.Errorf("Error message did not match")
614		t.Errorf("Expected ")
615		t.Errorf(" Duplicated replacement name a")
616		t.Errorf("actual error:")
617		t.Errorf("  %s", err.Error())
618		t.FailNow()
619	}
620}
621
622func TestReplacementsMultipleReplacedToSame(t *testing.T) {
623	cycleString := "a=c,d=c"
624	err := replaceProperty.Set(cycleString)
625
626	if err.Error() != "Duplicated replacement name c" {
627		t.Errorf("Error message did not match")
628		t.Errorf("Expected ")
629		t.Errorf(" Duplicated replacement name c")
630		t.Errorf("actual error:")
631		t.Errorf("  %s", err.Error())
632		t.FailNow()
633	}
634}
635