xref: /aosp_15_r20/build/blueprint/proptools/repack_test.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1// Copyright 2024 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 proptools
16
17import (
18	"regexp"
19	"strings"
20	"testing"
21
22	"github.com/google/blueprint/parser"
23)
24
25type testSymlinkStruct struct {
26	Target string
27	Name   string
28}
29
30type testPropStructNested struct {
31	My_string_ptr *string
32}
33
34type testPropStruct struct {
35	My_string                   string
36	My_configurable_string      Configurable[string]
37	My_configurable_string_list Configurable[[]string]
38	My_string_ptr               *string
39	My_string_list              []string
40	My_struct_list              []testSymlinkStruct
41	My_bool                     bool
42	My_int                      int
43	My_int64                    int64
44	Nested                      testPropStructNested
45}
46
47type testPropStructOnlyConfigurableStringList struct {
48	My_configurable_string_list Configurable[[]string]
49}
50
51func TestRepack(t *testing.T) {
52	testCases := []struct {
53		name        string
54		propStructs []interface{}
55		expectedBp  string
56		expectedErr string
57	}{
58		{
59			name: "Simple prop struct",
60			propStructs: []interface{}{&testPropStruct{
61				My_string:                   "foo",
62				My_configurable_string:      NewSimpleConfigurable("qux"),
63				My_configurable_string_list: NewSimpleConfigurable([]string{"a", "b", "c"}),
64				My_string_ptr:               StringPtr("bar"),
65				My_string_list:              []string{"foo", "bar"},
66				My_struct_list: []testSymlinkStruct{
67					{Name: "foo", Target: "foo_target"},
68					{Name: "bar", Target: "bar_target"},
69				},
70				My_bool:  true,
71				My_int:   5,
72				My_int64: 64,
73				Nested: testPropStructNested{
74					My_string_ptr: StringPtr("baz"),
75				},
76			}},
77			expectedBp: `
78module {
79    my_string: "foo",
80    my_configurable_string: "qux",
81    my_configurable_string_list: [
82        "a",
83        "b",
84        "c",
85    ],
86    my_string_ptr: "bar",
87    my_string_list: [
88        "foo",
89        "bar",
90    ],
91    my_struct_list: [
92        {
93            Target: "foo_target",
94            Name: "foo",
95        },
96        {
97            Target: "bar_target",
98            Name: "bar",
99        },
100    ],
101    my_bool: true,
102    my_int: 5,
103    my_int64: 64,
104    nested: {
105        my_string_ptr: "baz",
106    },
107}`,
108		},
109		{
110			name: "Complicated select",
111			propStructs: []interface{}{&testPropStructOnlyConfigurableStringList{
112				My_configurable_string_list: createComplicatedSelect(),
113			}},
114			expectedBp: `
115module {
116    my_configurable_string_list: ["a"] + select((os(), arch()), {
117        ("android", "x86"): [
118            "android",
119            "x86",
120        ],
121        ("android", "arm64"): [
122            "android",
123            "arm64",
124        ],
125        (default, "x86"): [
126            "default",
127            "x86",
128        ],
129        (default, default): [
130            "default",
131            "default",
132        ],
133    }) + ["b"],
134}`,
135		},
136		{
137			name: "Multiple property structs",
138			propStructs: []interface{}{
139				&testPropStruct{
140					My_string:      "foo",
141					My_string_ptr:  nil,
142					My_string_list: []string{"foo", "bar"},
143					My_bool:        true,
144					My_int:         5,
145				},
146				&testPropStructNested{
147					My_string_ptr: StringPtr("bar"),
148				},
149			},
150			expectedBp: `
151module {
152    my_string: "foo",
153    my_string_ptr: "bar",
154    my_string_list: [
155        "foo",
156        "bar",
157    ],
158    my_struct_list: [],
159    my_bool: true,
160    my_int: 5,
161    my_int64: 0,
162}`,
163		},
164		{
165			name: "Multiple conflicting property structs",
166			propStructs: []interface{}{
167				&testPropStruct{
168					My_string:      "foo",
169					My_string_ptr:  StringPtr("foo"),
170					My_string_list: []string{"foo", "bar"},
171					My_bool:        true,
172					My_int:         5,
173				},
174				&testPropStructNested{
175					My_string_ptr: StringPtr("bar"),
176				},
177			},
178			expectedErr: `Conflicting fields in property structs had values "foo" and "bar"`,
179		},
180	}
181
182	for _, tc := range testCases {
183		t.Run(tc.name, func(t *testing.T) {
184			result, err := RepackProperties(tc.propStructs)
185			if err != nil {
186				if tc.expectedErr != "" {
187					match, err2 := regexp.MatchString(tc.expectedErr, err.Error())
188					if err2 != nil {
189						t.Fatal(err2)
190					}
191					if !match {
192						t.Fatalf("Expected error matching %q, found %q", tc.expectedErr, err.Error())
193					}
194					return
195				} else {
196					t.Fatal(err)
197				}
198			} else if tc.expectedErr != "" {
199				t.Fatalf("Expected error matching %q, but got success", tc.expectedErr)
200			}
201			file := &parser.File{
202				Defs: []parser.Definition{
203					&parser.Module{
204						Type: "module",
205						Map:  *result,
206					},
207				},
208			}
209			bytes, err := parser.Print(file)
210			if err != nil {
211				t.Fatal(err)
212			}
213			expected := strings.TrimSpace(tc.expectedBp)
214			actual := strings.TrimSpace(string(bytes))
215			if expected != actual {
216				t.Fatalf("Expected:\n%s\nBut found:\n%s\n", expected, actual)
217			}
218		})
219	}
220}
221
222func createComplicatedSelect() Configurable[[]string] {
223	result := NewSimpleConfigurable([]string{"a"})
224	result.Append(NewConfigurable([]ConfigurableCondition{
225		NewConfigurableCondition("os", nil),
226		NewConfigurableCondition("arch", nil),
227	}, []ConfigurableCase[[]string]{
228		NewConfigurableCase([]ConfigurablePattern{
229			NewStringConfigurablePattern("android"),
230			NewStringConfigurablePattern("x86"),
231		}, &[]string{"android", "x86"}),
232		NewConfigurableCase([]ConfigurablePattern{
233			NewStringConfigurablePattern("android"),
234			NewStringConfigurablePattern("arm64"),
235		}, &[]string{"android", "arm64"}),
236		NewConfigurableCase([]ConfigurablePattern{
237			NewDefaultConfigurablePattern(),
238			NewStringConfigurablePattern("x86"),
239		}, &[]string{"default", "x86"}),
240		NewConfigurableCase([]ConfigurablePattern{
241			NewDefaultConfigurablePattern(),
242			NewDefaultConfigurablePattern(),
243		}, &[]string{"default", "default"}),
244	}))
245	result.Append(NewSimpleConfigurable([]string{"b"}))
246	return result
247}
248