• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2021 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	"reflect"
19	"strings"
20	"testing"
21
22	"github.com/google/blueprint/proptools"
23)
24
25func TestUniqueBazelLabels(t *testing.T) {
26	testCases := []struct {
27		originalLabels       []Label
28		expectedUniqueLabels []Label
29	}{
30		{
31			originalLabels: []Label{
32				{Label: "a"},
33				{Label: "b"},
34				{Label: "a"},
35				{Label: "c"},
36			},
37			expectedUniqueLabels: []Label{
38				{Label: "a"},
39				{Label: "b"},
40				{Label: "c"},
41			},
42		},
43	}
44	for _, tc := range testCases {
45		actualUniqueLabels := UniqueSortedBazelLabels(tc.originalLabels)
46		if !reflect.DeepEqual(tc.expectedUniqueLabels, actualUniqueLabels) {
47			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabels, actualUniqueLabels)
48		}
49	}
50}
51
52func TestSubtractStrings(t *testing.T) {
53	testCases := []struct {
54		haystack       []string
55		needle         []string
56		expectedResult []string
57	}{
58		{
59			haystack: []string{
60				"a",
61				"b",
62				"c",
63			},
64			needle: []string{
65				"a",
66			},
67			expectedResult: []string{
68				"b", "c",
69			},
70		},
71	}
72	for _, tc := range testCases {
73		actualResult := SubtractStrings(tc.haystack, tc.needle)
74		if !reflect.DeepEqual(tc.expectedResult, actualResult) {
75			t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult)
76		}
77	}
78}
79
80func TestSubtractBazelLabelList(t *testing.T) {
81	testCases := []struct {
82		haystack       LabelList
83		needle         LabelList
84		expectedResult LabelList
85	}{
86		{
87			haystack: LabelList{
88				Includes: []Label{
89					{Label: "a"},
90					{Label: "b"},
91					{Label: "c"},
92				},
93				Excludes: []Label{
94					{Label: "x"},
95					{Label: "y"},
96					{Label: "z"},
97				},
98			},
99			needle: LabelList{
100				Includes: []Label{
101					{Label: "a"},
102				},
103				Excludes: []Label{
104					{Label: "z"},
105				},
106			},
107			// NOTE: Excludes are intentionally not subtracted
108			expectedResult: LabelList{
109				Includes: []Label{
110					{Label: "b"},
111					{Label: "c"},
112				},
113				Excludes: []Label{
114					{Label: "x"},
115					{Label: "y"},
116					{Label: "z"},
117				},
118			},
119		},
120	}
121	for _, tc := range testCases {
122		actualResult := SubtractBazelLabelList(tc.haystack, tc.needle)
123		if !reflect.DeepEqual(tc.expectedResult, actualResult) {
124			t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult)
125		}
126	}
127}
128func TestFirstUniqueBazelLabelList(t *testing.T) {
129	testCases := []struct {
130		originalLabelList       LabelList
131		expectedUniqueLabelList LabelList
132	}{
133		{
134			originalLabelList: LabelList{
135				Includes: []Label{
136					{Label: "a"},
137					{Label: "b"},
138					{Label: "a"},
139					{Label: "c"},
140				},
141				Excludes: []Label{
142					{Label: "x"},
143					{Label: "x"},
144					{Label: "y"},
145					{Label: "z"},
146				},
147			},
148			expectedUniqueLabelList: LabelList{
149				Includes: []Label{
150					{Label: "a"},
151					{Label: "b"},
152					{Label: "c"},
153				},
154				Excludes: []Label{
155					{Label: "x"},
156					{Label: "y"},
157					{Label: "z"},
158				},
159			},
160		},
161	}
162	for _, tc := range testCases {
163		actualUniqueLabelList := FirstUniqueBazelLabelList(tc.originalLabelList)
164		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
165			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
166		}
167	}
168}
169
170func TestUniqueSortedBazelLabelList(t *testing.T) {
171	testCases := []struct {
172		originalLabelList       LabelList
173		expectedUniqueLabelList LabelList
174	}{
175		{
176			originalLabelList: LabelList{
177				Includes: []Label{
178					{Label: "c"},
179					{Label: "a"},
180					{Label: "a"},
181					{Label: "b"},
182				},
183				Excludes: []Label{
184					{Label: "y"},
185					{Label: "z"},
186					{Label: "x"},
187					{Label: "x"},
188				},
189			},
190			expectedUniqueLabelList: LabelList{
191				Includes: []Label{
192					{Label: "a"},
193					{Label: "b"},
194					{Label: "c"},
195				},
196				Excludes: []Label{
197					{Label: "x"},
198					{Label: "y"},
199					{Label: "z"},
200				},
201			},
202		},
203	}
204	for _, tc := range testCases {
205		actualUniqueLabelList := UniqueSortedBazelLabelList(tc.originalLabelList)
206		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
207			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
208		}
209	}
210}
211
212func makeLabels(labels ...string) []Label {
213	var ret []Label
214	for _, l := range labels {
215		ret = append(ret, Label{Label: l})
216	}
217	return ret
218}
219
220func makeLabelList(includes, excludes []string) LabelList {
221	return LabelList{
222		Includes: makeLabels(includes...),
223		Excludes: makeLabels(excludes...),
224	}
225}
226
227func TestResolveExcludes(t *testing.T) {
228	attr := LabelListAttribute{
229		Value: makeLabelList(
230			[]string{
231				"all_include",
232				"arm_exclude",
233				"android_exclude",
234			},
235			[]string{"all_exclude"},
236		),
237		ConfigurableValues: configurableLabelLists{
238			ArchConfigurationAxis: labelListSelectValues{
239				"arm":                      makeLabelList([]string{}, []string{"arm_exclude"}),
240				"x86":                      makeLabelList([]string{"x86_include"}, []string{}),
241				ConditionsDefaultConfigKey: makeLabelList([]string{"default_include"}, []string{}),
242			},
243			OsConfigurationAxis: labelListSelectValues{
244				"android": makeLabelList([]string{}, []string{"android_exclude"}),
245				"linux":   makeLabelList([]string{"linux_include"}, []string{}),
246			},
247			OsArchConfigurationAxis: labelListSelectValues{
248				"linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
249			},
250			ProductVariableConfigurationAxis("product_with_defaults"): labelListSelectValues{
251				"a":                        makeLabelList([]string{}, []string{"not_in_value"}),
252				"b":                        makeLabelList([]string{"b_val"}, []string{}),
253				"c":                        makeLabelList([]string{"c_val"}, []string{}),
254				ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2"}, []string{}),
255			},
256			ProductVariableConfigurationAxis("product_only_with_excludes"): labelListSelectValues{
257				"a": makeLabelList([]string{}, []string{"not_in_value"}),
258			},
259		},
260	}
261
262	attr.ResolveExcludes()
263
264	expectedBaseIncludes := []Label{{Label: "all_include"}}
265	if !reflect.DeepEqual(expectedBaseIncludes, attr.Value.Includes) {
266		t.Errorf("Expected Value includes %q, got %q", attr.Value.Includes, expectedBaseIncludes)
267	}
268	var nilLabels []Label
269	expectedConfiguredIncludes := map[ConfigurationAxis]map[string][]Label{
270		ArchConfigurationAxis: {
271			"arm":                      nilLabels,
272			"x86":                      makeLabels("arm_exclude", "x86_include"),
273			ConditionsDefaultConfigKey: makeLabels("arm_exclude", "default_include"),
274		},
275		OsConfigurationAxis: {
276			"android":                  nilLabels,
277			"linux":                    makeLabels("android_exclude", "linux_include"),
278			ConditionsDefaultConfigKey: makeLabels("android_exclude"),
279		},
280		OsArchConfigurationAxis: {
281			"linux_x86":                makeLabels("linux_x86_include"),
282			ConditionsDefaultConfigKey: nilLabels,
283		},
284		ProductVariableConfigurationAxis("product_with_defaults"): {
285			"a":                        nilLabels,
286			"b":                        makeLabels("b_val"),
287			"c":                        makeLabels("c_val"),
288			ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"),
289		},
290	}
291	for _, axis := range attr.SortedConfigurationAxes() {
292		if _, ok := expectedConfiguredIncludes[axis]; !ok {
293			t.Errorf("Found unexpected axis %s", axis)
294			continue
295		}
296		expectedForAxis := expectedConfiguredIncludes[axis]
297		gotForAxis := attr.ConfigurableValues[axis]
298		if len(expectedForAxis) != len(gotForAxis) {
299			t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis)
300		}
301		for config, value := range gotForAxis {
302			if expected, ok := expectedForAxis[config]; ok {
303				if !reflect.DeepEqual(expected, value.Includes) {
304					t.Errorf("For %s,\nexpected: %#v\ngot %#v", axis, expected, value.Includes)
305				}
306			} else {
307				t.Errorf("Got unexpected config %q for %s", config, axis)
308			}
309		}
310	}
311}
312
313// labelAddSuffixForTypeMapper returns a LabelMapper that adds suffix to label name for modules of
314// typ
315func labelAddSuffixForTypeMapper(suffix, typ string) LabelMapper {
316	return func(omc OtherModuleContext, label Label) (string, bool) {
317		m, ok := omc.ModuleFromName(label.Label)
318		if !ok {
319			return label.Label, false
320		}
321		mTyp := omc.OtherModuleType(m)
322		if typ == mTyp {
323			return label.Label + suffix, true
324		}
325		return label.Label, false
326	}
327}
328
329func TestPartitionLabelListAttribute(t *testing.T) {
330	testCases := []struct {
331		name           string
332		ctx            *OtherModuleTestContext
333		labelList      LabelListAttribute
334		filters        LabelPartitions
335		expected       PartitionToLabelListAttribute
336		expectedErrMsg *string
337	}{
338		{
339			name: "no configurable values",
340			ctx:  &OtherModuleTestContext{},
341			labelList: LabelListAttribute{
342				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
343			},
344			filters: LabelPartitions{
345				"A": LabelPartition{Extensions: []string{".a"}},
346				"B": LabelPartition{Extensions: []string{".b"}},
347				"C": LabelPartition{Extensions: []string{".c"}},
348			},
349			expected: PartitionToLabelListAttribute{
350				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})},
351				"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
352				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
353			},
354		},
355		{
356			name: "no configurable values, remainder partition",
357			ctx:  &OtherModuleTestContext{},
358			labelList: LabelListAttribute{
359				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
360			},
361			filters: LabelPartitions{
362				"A": LabelPartition{Extensions: []string{".a"}, Keep_remainder: true},
363				"B": LabelPartition{Extensions: []string{".b"}},
364				"C": LabelPartition{Extensions: []string{".c"}},
365			},
366			expected: PartitionToLabelListAttribute{
367				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "d.d", "e.e"}, []string{})},
368				"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
369				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
370			},
371		},
372		{
373			name: "no configurable values, empty partition",
374			ctx:  &OtherModuleTestContext{},
375			labelList: LabelListAttribute{
376				Value: makeLabelList([]string{"a.a", "c.c"}, []string{}),
377			},
378			filters: LabelPartitions{
379				"A": LabelPartition{Extensions: []string{".a"}},
380				"B": LabelPartition{Extensions: []string{".b"}},
381				"C": LabelPartition{Extensions: []string{".c"}},
382			},
383			expected: PartitionToLabelListAttribute{
384				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})},
385				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
386			},
387		},
388		{
389			name: "no configurable values, has map",
390			ctx: &OtherModuleTestContext{
391				Modules: []TestModuleInfo{{ModuleName: "srcs", Typ: "fg", Dir: "dir"}},
392			},
393			labelList: LabelListAttribute{
394				Value: makeLabelList([]string{"a.a", "srcs", "b.b", "c.c"}, []string{}),
395			},
396			filters: LabelPartitions{
397				"A": LabelPartition{Extensions: []string{".a"}, LabelMapper: labelAddSuffixForTypeMapper("_a", "fg")},
398				"B": LabelPartition{Extensions: []string{".b"}},
399				"C": LabelPartition{Extensions: []string{".c"}},
400			},
401			expected: PartitionToLabelListAttribute{
402				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "srcs_a"}, []string{})},
403				"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
404				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
405			},
406		},
407		{
408			name: "configurable values, keeps empty if excludes",
409			ctx:  &OtherModuleTestContext{},
410			labelList: LabelListAttribute{
411				ConfigurableValues: configurableLabelLists{
412					ArchConfigurationAxis: labelListSelectValues{
413						"x86":    makeLabelList([]string{"a.a", "c.c"}, []string{}),
414						"arm":    makeLabelList([]string{"b.b"}, []string{}),
415						"x86_64": makeLabelList([]string{"b.b"}, []string{"d.d"}),
416					},
417				},
418			},
419			filters: LabelPartitions{
420				"A": LabelPartition{Extensions: []string{".a"}},
421				"B": LabelPartition{Extensions: []string{".b"}},
422				"C": LabelPartition{Extensions: []string{".c"}},
423			},
424			expected: PartitionToLabelListAttribute{
425				"A": LabelListAttribute{
426					ConfigurableValues: configurableLabelLists{
427						ArchConfigurationAxis: labelListSelectValues{
428							"x86":    makeLabelList([]string{"a.a"}, []string{}),
429							"x86_64": makeLabelList([]string{}, []string{"c.c"}),
430						},
431					},
432				},
433				"B": LabelListAttribute{
434					ConfigurableValues: configurableLabelLists{
435						ArchConfigurationAxis: labelListSelectValues{
436							"arm":    makeLabelList([]string{"b.b"}, []string{}),
437							"x86_64": makeLabelList([]string{"b.b"}, []string{"c.c"}),
438						},
439					},
440				},
441				"C": LabelListAttribute{
442					ConfigurableValues: configurableLabelLists{
443						ArchConfigurationAxis: labelListSelectValues{
444							"x86":    makeLabelList([]string{"c.c"}, []string{}),
445							"x86_64": makeLabelList([]string{}, []string{"c.c"}),
446						},
447					},
448				},
449			},
450		},
451		{
452			name: "error for multiple partitions same value",
453			ctx:  &OtherModuleTestContext{},
454			labelList: LabelListAttribute{
455				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
456			},
457			filters: LabelPartitions{
458				"A":       LabelPartition{Extensions: []string{".a"}},
459				"other A": LabelPartition{Extensions: []string{".a"}},
460			},
461			expected:       PartitionToLabelListAttribute{},
462			expectedErrMsg: proptools.StringPtr(`"a.a" was found in multiple partitions:`),
463		},
464	}
465
466	for _, tc := range testCases {
467		t.Run(tc.name, func(t *testing.T) {
468			got := PartitionLabelListAttribute(tc.ctx, &tc.labelList, tc.filters)
469
470			if hasErrors, expectsErr := len(tc.ctx.errors) > 0, tc.expectedErrMsg != nil; hasErrors != expectsErr {
471				t.Errorf("Unexpected error(s): %q, expected: %q", tc.ctx.errors, *tc.expectedErrMsg)
472			} else if tc.expectedErrMsg != nil {
473				found := false
474				for _, err := range tc.ctx.errors {
475					if strings.Contains(err, *tc.expectedErrMsg) {
476						found = true
477						break
478					}
479				}
480
481				if !found {
482					t.Errorf("Expected error message: %q, got %q", *tc.expectedErrMsg, tc.ctx.errors)
483				}
484				return
485			}
486
487			if len(tc.expected) != len(got) {
488				t.Errorf("Expected %d partitions, got %d partitions", len(tc.expected), len(got))
489			}
490			for partition, expectedLla := range tc.expected {
491				gotLla, ok := got[partition]
492				if !ok {
493					t.Errorf("Expected partition %q, but it was not found %v", partition, got)
494					continue
495				}
496				expectedLabelList := expectedLla.Value
497				gotLabelList := gotLla.Value
498				if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) {
499					t.Errorf("Expected no config includes %v, got %v", expectedLabelList.Includes, gotLabelList.Includes)
500				}
501				expectedAxes := expectedLla.SortedConfigurationAxes()
502				gotAxes := gotLla.SortedConfigurationAxes()
503				if !reflect.DeepEqual(expectedAxes, gotAxes) {
504					t.Errorf("Expected axes %v, got %v (%#v)", expectedAxes, gotAxes, gotLla)
505				}
506				for _, axis := range expectedLla.SortedConfigurationAxes() {
507					if _, exists := gotLla.ConfigurableValues[axis]; !exists {
508						t.Errorf("Expected %s to be a supported axis, but it was not found", axis)
509					}
510					if expected, got := expectedLla.ConfigurableValues[axis], gotLla.ConfigurableValues[axis]; len(expected) != len(got) {
511						t.Errorf("For axis %q: expected configs %v, got %v", axis, expected, got)
512					}
513					for config, expectedLabelList := range expectedLla.ConfigurableValues[axis] {
514						gotLabelList, exists := gotLla.ConfigurableValues[axis][config]
515						if !exists {
516							t.Errorf("Expected %s to be a supported config, but config was not found", config)
517							continue
518						}
519						if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) {
520							t.Errorf("Expected %s %s includes %v, got %v", axis, config, expectedLabelList.Includes, gotLabelList.Includes)
521						}
522					}
523				}
524			}
525		})
526	}
527}
528
529func TestDeduplicateAxesFromBase(t *testing.T) {
530	attr := StringListAttribute{
531		Value: []string{
532			"all_include",
533			"arm_include",
534			"android_include",
535			"linux_x86_include",
536		},
537		ConfigurableValues: configurableStringLists{
538			ArchConfigurationAxis: stringListSelectValues{
539				"arm": []string{"arm_include"},
540				"x86": []string{"x86_include"},
541			},
542			OsConfigurationAxis: stringListSelectValues{
543				"android": []string{"android_include"},
544				"linux":   []string{"linux_include"},
545			},
546			OsArchConfigurationAxis: stringListSelectValues{
547				"linux_x86": {"linux_x86_include"},
548			},
549			ProductVariableConfigurationAxis("a"): stringListSelectValues{
550				"a": []string{"not_in_value"},
551			},
552		},
553	}
554
555	attr.DeduplicateAxesFromBase()
556
557	expectedBaseIncludes := []string{
558		"all_include",
559		"arm_include",
560		"android_include",
561		"linux_x86_include",
562	}
563	if !reflect.DeepEqual(expectedBaseIncludes, attr.Value) {
564		t.Errorf("Expected Value includes %q, got %q", attr.Value, expectedBaseIncludes)
565	}
566	expectedConfiguredIncludes := configurableStringLists{
567		ArchConfigurationAxis: stringListSelectValues{
568			"x86": []string{"x86_include"},
569		},
570		OsConfigurationAxis: stringListSelectValues{
571			"linux": []string{"linux_include"},
572		},
573		OsArchConfigurationAxis: stringListSelectValues{},
574		ProductVariableConfigurationAxis("a"): stringListSelectValues{
575			"a": []string{"not_in_value"},
576		},
577	}
578	for _, axis := range attr.SortedConfigurationAxes() {
579		if _, ok := expectedConfiguredIncludes[axis]; !ok {
580			t.Errorf("Found unexpected axis %s", axis)
581			continue
582		}
583		expectedForAxis := expectedConfiguredIncludes[axis]
584		gotForAxis := attr.ConfigurableValues[axis]
585		if len(expectedForAxis) != len(gotForAxis) {
586			t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis)
587		}
588		for config, value := range gotForAxis {
589			if expected, ok := expectedForAxis[config]; ok {
590				if !reflect.DeepEqual(expected, value) {
591					t.Errorf("For %s, expected: %#v, got %#v", axis, expected, value)
592				}
593			} else {
594				t.Errorf("Got unexpected config %q for %s", config, axis)
595			}
596		}
597	}
598}
599