blob: 1e4256ff5fae82c8cbfbb2f476890aa6937a9110 [file] [log] [blame]
Bob Badourdc62de42022-10-12 20:10:17 -07001// Copyright 2022 Google LLC
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 projectmetadata
16
17import (
18 "fmt"
19 "strings"
20 "testing"
21
22 "android/soong/tools/compliance/testfs"
23)
24
25const (
26 // EMPTY represents a METADATA file with no recognized fields
27 EMPTY = ``
28
29 // INVALID_NAME represents a METADATA file with the wrong type of name
30 INVALID_NAME = `name: a library\n`
31
32 // INVALID_DESCRIPTION represents a METADATA file with the wrong type of description
33 INVALID_DESCRIPTION = `description: unquoted text\n`
34
35 // INVALID_VERSION represents a METADATA file with the wrong type of version
36 INVALID_VERSION = `third_party { version: 1 }`
37
38 // MY_LIB_1_0 represents a METADATA file for version 1.0 of mylib
39 MY_LIB_1_0 = `name: "mylib" description: "my library" third_party { version: "1.0" }`
40
41 // NO_NAME_0_1 represents a METADATA file with a description but no name
42 NO_NAME_0_1 = `description: "my library" third_party { version: "0.1" }`
43)
44
45func TestReadMetadataForProjects(t *testing.T) {
46 tests := []struct {
47 name string
48 fs *testfs.TestFS
49 projects []string
50 expectedError string
51 expected []pmeta
52 }{
53 {
54 name: "trivial",
55 fs: &testfs.TestFS{
56 "/a/METADATA": []byte("name: \"Android\"\n"),
57 },
58 projects: []string{"/a"},
59 expected: []pmeta{{project: "/a", versionedName: "Android"}},
60 },
61 {
62 name: "versioned",
63 fs: &testfs.TestFS{
64 "/a/METADATA": []byte(MY_LIB_1_0),
65 },
66 projects: []string{"/a"},
67 expected: []pmeta{{project: "/a", versionedName: "mylib_v_1.0"}},
68 },
69 {
70 name: "versioneddesc",
71 fs: &testfs.TestFS{
72 "/a/METADATA": []byte(NO_NAME_0_1),
73 },
74 projects: []string{"/a"},
75 expected: []pmeta{{project: "/a", versionedName: "my library"}},
76 },
77 {
78 name: "unterminated",
79 fs: &testfs.TestFS{
80 "/a/METADATA": []byte("name: \"Android\n"),
81 },
82 projects: []string{"/a"},
83 expectedError: `invalid character '\n' in string`,
84 },
85 {
86 name: "abc",
87 fs: &testfs.TestFS{
88 "/a/METADATA": []byte(EMPTY),
89 "/b/METADATA": []byte(MY_LIB_1_0),
90 "/c/METADATA": []byte(NO_NAME_0_1),
91 },
92 projects: []string{"/a", "/b", "/c"},
93 expected: []pmeta{
94 {project: "/a", versionedName: ""},
95 {project: "/b", versionedName: "mylib_v_1.0"},
96 {project: "/c", versionedName: "my library"},
97 },
98 },
99 {
100 name: "ab",
101 fs: &testfs.TestFS{
102 "/a/METADATA": []byte(EMPTY),
103 "/b/METADATA": []byte(MY_LIB_1_0),
104 },
105 projects: []string{"/a", "/b", "/c"},
106 expected: []pmeta{
107 {project: "/a", versionedName: ""},
108 {project: "/b", versionedName: "mylib_v_1.0"},
109 },
110 },
111 {
112 name: "ac",
113 fs: &testfs.TestFS{
114 "/a/METADATA": []byte(EMPTY),
115 "/c/METADATA": []byte(NO_NAME_0_1),
116 },
117 projects: []string{"/a", "/b", "/c"},
118 expected: []pmeta{
119 {project: "/a", versionedName: ""},
120 {project: "/c", versionedName: "my library"},
121 },
122 },
123 {
124 name: "bc",
125 fs: &testfs.TestFS{
126 "/b/METADATA": []byte(MY_LIB_1_0),
127 "/c/METADATA": []byte(NO_NAME_0_1),
128 },
129 projects: []string{"/a", "/b", "/c"},
130 expected: []pmeta{
131 {project: "/b", versionedName: "mylib_v_1.0"},
132 {project: "/c", versionedName: "my library"},
133 },
134 },
135 {
136 name: "wrongnametype",
137 fs: &testfs.TestFS{
138 "/a/METADATA": []byte(INVALID_NAME),
139 },
140 projects: []string{"/a"},
141 expectedError: `invalid value for string type`,
142 },
143 {
144 name: "wrongdescriptiontype",
145 fs: &testfs.TestFS{
146 "/a/METADATA": []byte(INVALID_DESCRIPTION),
147 },
148 projects: []string{"/a"},
149 expectedError: `invalid value for string type`,
150 },
151 {
152 name: "wrongversiontype",
153 fs: &testfs.TestFS{
154 "/a/METADATA": []byte(INVALID_VERSION),
155 },
156 projects: []string{"/a"},
157 expectedError: `invalid value for string type`,
158 },
159 {
160 name: "wrongtype",
161 fs: &testfs.TestFS{
162 "/a/METADATA": []byte(INVALID_NAME + INVALID_DESCRIPTION + INVALID_VERSION),
163 },
164 projects: []string{"/a"},
165 expectedError: `invalid value for string type`,
166 },
167 {
168 name: "empty",
169 fs: &testfs.TestFS{
170 "/a/METADATA": []byte(EMPTY),
171 },
172 projects: []string{"/a"},
173 expected: []pmeta{{project: "/a", versionedName: ""}},
174 },
175 {
176 name: "emptyother",
177 fs: &testfs.TestFS{
178 "/a/METADATA.bp": []byte(EMPTY),
179 },
180 projects: []string{"/a"},
181 },
182 {
183 name: "emptyfs",
184 fs: &testfs.TestFS{},
185 projects: []string{"/a"},
186 },
187 {
188 name: "override",
189 fs: &testfs.TestFS{
190 "/a/METADATA": []byte(INVALID_NAME + INVALID_DESCRIPTION + INVALID_VERSION),
191 "/a/METADATA.android": []byte(MY_LIB_1_0),
192 },
193 projects: []string{"/a"},
194 expected: []pmeta{{project: "/a", versionedName: "mylib_v_1.0"}},
195 },
196 {
197 name: "enchilada",
198 fs: &testfs.TestFS{
199 "/a/METADATA": []byte(INVALID_NAME + INVALID_DESCRIPTION + INVALID_VERSION),
200 "/a/METADATA.android": []byte(EMPTY),
201 "/b/METADATA": []byte(MY_LIB_1_0),
202 "/c/METADATA": []byte(NO_NAME_0_1),
203 },
204 projects: []string{"/a", "/b", "/c"},
205 expected: []pmeta{
206 {project: "/a", versionedName: ""},
207 {project: "/b", versionedName: "mylib_v_1.0"},
208 {project: "/c", versionedName: "my library"},
209 },
210 },
211 }
212 for _, tt := range tests {
213 t.Run(tt.name, func(t *testing.T) {
214 ix := NewIndex(tt.fs)
215 pms, err := ix.MetadataForProjects(tt.projects...)
216 if err != nil {
217 if len(tt.expectedError) == 0 {
218 t.Errorf("unexpected error: got %s, want no error", err)
219 } else if !strings.Contains(err.Error(), tt.expectedError) {
220 t.Errorf("unexpected error: got %s, want %q", err, tt.expectedError)
221 }
222 return
223 }
224 t.Logf("actual %d project metadata", len(pms))
225 for _, pm := range pms {
226 t.Logf(" %v", pm.String())
227 }
228 t.Logf("expected %d project metadata", len(tt.expected))
229 for _, pm := range tt.expected {
230 t.Logf(" %s", pm.String())
231 }
232 if len(tt.expectedError) > 0 {
233 t.Errorf("unexpected success: got no error, want %q err", tt.expectedError)
234 return
235 }
236 if len(pms) != len(tt.expected) {
237 t.Errorf("missing project metadata: got %d project metadata, want %d", len(pms), len(tt.expected))
238 }
239 for i := 0; i < len(pms) && i < len(tt.expected); i++ {
240 if msg := tt.expected[i].difference(pms[i]); msg != "" {
241 t.Errorf("unexpected metadata starting at index %d: %s", i, msg)
242 return
243 }
244 }
245 if len(pms) < len(tt.expected) {
246 t.Errorf("missing metadata starting at index %d: got nothing, want %s", len(pms), tt.expected[len(pms)].String())
247 }
248 if len(tt.expected) < len(pms) {
249 t.Errorf("unexpected metadata starting at index %d: got %s, want nothing", len(tt.expected), pms[len(tt.expected)].String())
250 }
251 })
252 }
253}
254
255type pmeta struct {
256 project string
257 versionedName string
258}
259
260func (pm pmeta) String() string {
261 return fmt.Sprintf("project: %q versionedName: %q\n", pm.project, pm.versionedName)
262}
263
264func (pm pmeta) equals(other *ProjectMetadata) bool {
265 if pm.project != other.project {
266 return false
267 }
268 if pm.versionedName != other.VersionedName() {
269 return false
270 }
271 return true
272}
273
274func (pm pmeta) difference(other *ProjectMetadata) string {
275 if pm.equals(other) {
276 return ""
277 }
278 var sb strings.Builder
279 fmt.Fprintf(&sb, "got")
280 if pm.project != other.project {
281 fmt.Fprintf(&sb, " project: %q", other.project)
282 }
283 if pm.versionedName != other.VersionedName() {
284 fmt.Fprintf(&sb, " versionedName: %q", other.VersionedName())
285 }
286 fmt.Fprintf(&sb, ", want")
287 if pm.project != other.project {
288 fmt.Fprintf(&sb, " project: %q", pm.project)
289 }
290 if pm.versionedName != other.VersionedName() {
291 fmt.Fprintf(&sb, " versionedName: %q", pm.versionedName)
292 }
293 return sb.String()
294}