xref: /aosp_15_r20/build/blueprint/bootstrap/bpdoc/properties_test.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1// Copyright 2019 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 bpdoc
16
17import (
18	"reflect"
19	"strings"
20	"testing"
21)
22
23func TestExcludeByTag(t *testing.T) {
24	r := NewReader(pkgFiles)
25	ps, err := r.PropertyStruct(pkgPath, "tagTestProps", reflect.ValueOf(tagTestProps{}))
26	if err != nil {
27		t.Fatal(err)
28	}
29
30	ps.ExcludeByTag("tag1", "a")
31
32	expected := []string{"c", "d", "g"}
33	actual := actualProperties(t, ps.Properties)
34	if !reflect.DeepEqual(expected, actual) {
35		t.Errorf("unexpected ExcludeByTag result, expected: %q, actual: %q", expected, actual)
36	}
37}
38
39func TestIncludeByTag(t *testing.T) {
40	r := NewReader(pkgFiles)
41	ps, err := r.PropertyStruct(pkgPath, "tagTestProps", reflect.ValueOf(tagTestProps{A: "B"}))
42	if err != nil {
43		t.Fatal(err)
44	}
45
46	ps.IncludeByTag("tag1", "c")
47
48	expected := []string{"b", "c", "d", "f", "g"}
49	actual := actualProperties(t, ps.Properties)
50	if !reflect.DeepEqual(expected, actual) {
51		t.Errorf("unexpected IncludeByTag result, expected: %q, actual: %q", expected, actual)
52	}
53}
54
55func TestPropertiesOfReflectionStructs(t *testing.T) {
56	testCases := []struct {
57		fields             map[string]interface{}
58		expectedProperties map[string]Property
59		description        string
60	}{
61		{
62			fields: map[string]interface{}{
63				"A": "A is a string",
64				"B": 0, //B is an int
65			},
66			expectedProperties: map[string]Property{
67				"a": *createProperty("a", "string", ""),
68				"b": *createProperty("b", "int", ""),
69			},
70			description: "struct is composed of primitive types",
71		},
72		{
73			fields: map[string]interface{}{
74				"A": "A is a string",
75				"B": 0, //B is an int
76				"C": props{},
77			},
78			expectedProperties: map[string]Property{
79				"a": *createProperty("a", "string", ""),
80				"b": *createProperty("b", "int", ""),
81				"c": *createProperty("c", "props", "props docs."),
82			},
83			description: "struct is composed of primitive types and other structs",
84		},
85	}
86
87	r := NewReader(pkgFiles)
88	for _, testCase := range testCases {
89		structType := reflectionStructType(testCase.fields)
90		ps, err := r.PropertyStruct(structType.PkgPath(), structType.String(), reflect.New(structType).Elem())
91		if err != nil {
92			t.Fatal(err)
93		}
94		for _, actualProperty := range ps.Properties {
95			propName := actualProperty.Name
96			assertProperties(t, testCase.expectedProperties[propName], actualProperty)
97		}
98	}
99}
100
101func TestNestUnique(t *testing.T) {
102	testCases := []struct {
103		src         []Property
104		target      []Property
105		expected    []Property
106		description string
107	}{
108		{
109			src:         []Property{},
110			target:      []Property{},
111			expected:    []Property{},
112			description: "Nest Unique fails for empty slice",
113		},
114		{
115			src:         []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")},
116			target:      []Property{},
117			expected:    []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")},
118			description: "Nest Unique fails when all elements are unique",
119		},
120		{
121			src:         []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")},
122			target:      []Property{*createProperty("c", "string", "")},
123			expected:    []Property{*createProperty("a", "string", ""), *createProperty("b", "string", ""), *createProperty("c", "string", "")},
124			description: "Nest Unique fails when all elements are unique",
125		},
126		{
127			src:         []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")},
128			target:      []Property{*createProperty("a", "string", "")},
129			expected:    []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")},
130			description: "Nest Unique fails when nested elements are duplicate",
131		},
132	}
133
134	errMsgTemplate := "%s. Expected: %q, Actual: %q"
135	for _, testCase := range testCases {
136		actual := nestUnique(testCase.src, testCase.target)
137		if len(actual) != len(testCase.expected) {
138			t.Errorf(errMsgTemplate, testCase.description, testCase.expected, actual)
139		}
140		for i := 0; i < len(actual); i++ {
141			if !actual[i].Equal(testCase.expected[i]) {
142				t.Errorf(errMsgTemplate, testCase.description, testCase.expected[i], actual[i])
143			}
144		}
145	}
146}
147
148// Creates a struct using reflection and return its type
149func reflectionStructType(fields map[string]interface{}) reflect.Type {
150	var structFields []reflect.StructField
151	for fieldname, obj := range fields {
152		structField := reflect.StructField{
153			Name: fieldname,
154			Type: reflect.TypeOf(obj),
155		}
156		structFields = append(structFields, structField)
157	}
158	return reflect.StructOf(structFields)
159}
160
161// Creates a Property object with a subset of its props populated
162func createProperty(propName string, propType string, propDocs string) *Property {
163	return &Property{Name: propName, Type: propType, Text: formatText(propDocs)}
164}
165
166// Asserts that two Property objects are "similar"
167// Name, Type and Text properties are checked for similarity
168func assertProperties(t *testing.T, expected Property, actual Property) {
169	assertStrings(t, expected.Name, actual.Name)
170	assertStrings(t, expected.Type, actual.Type)
171	assertStrings(t, strings.TrimSpace(string(expected.Text)), strings.TrimSpace(string(actual.Text)))
172}
173
174func assertStrings(t *testing.T, expected string, actual string) {
175	if expected != actual {
176		t.Errorf("expected: %s, actual: %s", expected, actual)
177	}
178}
179
180func actualProperties(t *testing.T, props []Property) []string {
181	t.Helper()
182
183	actual := []string{}
184	for _, p := range props {
185		actual = append(actual, p.Name)
186		actual = append(actual, actualProperties(t, p.Properties)...)
187	}
188	return actual
189}
190