blob: 156fb90486ba855a608067603060cc22e9c5c927 [file] [log] [blame]
Bob Badoure6fdd142021-12-09 22:10:43 -08001// Copyright 2021 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 main
16
17import (
18 "bufio"
19 "bytes"
20 "fmt"
21 "os"
22 "regexp"
23 "strings"
24 "testing"
25)
26
27var (
28 horizontalRule = regexp.MustCompile("^===[=]*===$")
29)
30
31func Test(t *testing.T) {
32 tests := []struct {
33 condition string
34 name string
35 roots []string
36 stripPrefix string
37 expectedOut []matcher
38 }{
39 {
40 condition: "firstparty",
41 name: "apex",
42 roots: []string{"highest.apex.meta_lic"},
43 expectedOut: []matcher{
44 hr{},
45 library{"Android"},
46 usedBy{"highest.apex"},
47 usedBy{"highest.apex/bin/bin1"},
48 usedBy{"highest.apex/bin/bin2"},
49 usedBy{"highest.apex/lib/liba.so"},
50 usedBy{"highest.apex/lib/libb.so"},
51 firstParty{},
52 },
53 },
54 {
55 condition: "firstparty",
56 name: "container",
57 roots: []string{"container.zip.meta_lic"},
58 expectedOut: []matcher{
59 hr{},
60 library{"Android"},
61 usedBy{"container.zip"},
62 usedBy{"container.zip/bin1"},
63 usedBy{"container.zip/bin2"},
64 usedBy{"container.zip/liba.so"},
65 usedBy{"container.zip/libb.so"},
66 firstParty{},
67 },
68 },
69 {
70 condition: "firstparty",
71 name: "application",
72 roots: []string{"application.meta_lic"},
73 expectedOut: []matcher{
74 hr{},
75 library{"Android"},
76 usedBy{"application"},
77 firstParty{},
78 },
79 },
80 {
81 condition: "firstparty",
82 name: "binary",
83 roots: []string{"bin/bin1.meta_lic"},
84 expectedOut: []matcher{
85 hr{},
86 library{"Android"},
87 usedBy{"bin/bin1"},
88 firstParty{},
89 },
90 },
91 {
92 condition: "firstparty",
93 name: "library",
94 roots: []string{"lib/libd.so.meta_lic"},
95 expectedOut: []matcher{
96 hr{},
97 library{"Android"},
98 usedBy{"lib/libd.so"},
99 firstParty{},
100 },
101 },
102 {
103 condition: "notice",
104 name: "apex",
105 roots: []string{"highest.apex.meta_lic"},
106 expectedOut: []matcher{
107 hr{},
108 library{"Android"},
109 usedBy{"highest.apex"},
110 usedBy{"highest.apex/bin/bin1"},
111 usedBy{"highest.apex/bin/bin2"},
112 usedBy{"highest.apex/lib/libb.so"},
113 firstParty{},
114 hr{},
115 library{"Device"},
116 usedBy{"highest.apex/bin/bin1"},
117 usedBy{"highest.apex/lib/liba.so"},
118 library{"External"},
119 usedBy{"highest.apex/bin/bin1"},
120 notice{},
121 },
122 },
123 {
124 condition: "notice",
125 name: "container",
126 roots: []string{"container.zip.meta_lic"},
127 expectedOut: []matcher{
128 hr{},
129 library{"Android"},
130 usedBy{"container.zip"},
131 usedBy{"container.zip/bin1"},
132 usedBy{"container.zip/bin2"},
133 usedBy{"container.zip/libb.so"},
134 firstParty{},
135 hr{},
136 library{"Device"},
137 usedBy{"container.zip/bin1"},
138 usedBy{"container.zip/liba.so"},
139 library{"External"},
140 usedBy{"container.zip/bin1"},
141 notice{},
142 },
143 },
144 {
145 condition: "notice",
146 name: "application",
147 roots: []string{"application.meta_lic"},
148 expectedOut: []matcher{
149 hr{},
150 library{"Android"},
151 usedBy{"application"},
152 firstParty{},
153 hr{},
154 library{"Device"},
155 usedBy{"application"},
156 notice{},
157 },
158 },
159 {
160 condition: "notice",
161 name: "binary",
162 roots: []string{"bin/bin1.meta_lic"},
163 expectedOut: []matcher{
164 hr{},
165 library{"Android"},
166 usedBy{"bin/bin1"},
167 firstParty{},
168 hr{},
169 library{"Device"},
170 usedBy{"bin/bin1"},
171 library{"External"},
172 usedBy{"bin/bin1"},
173 notice{},
174 },
175 },
176 {
177 condition: "notice",
178 name: "library",
179 roots: []string{"lib/libd.so.meta_lic"},
180 expectedOut: []matcher{
181 hr{},
182 library{"External"},
183 usedBy{"lib/libd.so"},
184 notice{},
185 },
186 },
187 {
188 condition: "reciprocal",
189 name: "apex",
190 roots: []string{"highest.apex.meta_lic"},
191 expectedOut: []matcher{
192 hr{},
193 library{"Android"},
194 usedBy{"highest.apex"},
195 usedBy{"highest.apex/bin/bin1"},
196 usedBy{"highest.apex/bin/bin2"},
197 usedBy{"highest.apex/lib/libb.so"},
198 firstParty{},
199 hr{},
200 library{"Device"},
201 usedBy{"highest.apex/bin/bin1"},
202 usedBy{"highest.apex/lib/liba.so"},
203 library{"External"},
204 usedBy{"highest.apex/bin/bin1"},
205 reciprocal{},
206 },
207 },
208 {
209 condition: "reciprocal",
210 name: "container",
211 roots: []string{"container.zip.meta_lic"},
212 expectedOut: []matcher{
213 hr{},
214 library{"Android"},
215 usedBy{"container.zip"},
216 usedBy{"container.zip/bin1"},
217 usedBy{"container.zip/bin2"},
218 usedBy{"container.zip/libb.so"},
219 firstParty{},
220 hr{},
221 library{"Device"},
222 usedBy{"container.zip/bin1"},
223 usedBy{"container.zip/liba.so"},
224 library{"External"},
225 usedBy{"container.zip/bin1"},
226 reciprocal{},
227 },
228 },
229 {
230 condition: "reciprocal",
231 name: "application",
232 roots: []string{"application.meta_lic"},
233 expectedOut: []matcher{
234 hr{},
235 library{"Android"},
236 usedBy{"application"},
237 firstParty{},
238 hr{},
239 library{"Device"},
240 usedBy{"application"},
241 reciprocal{},
242 },
243 },
244 {
245 condition: "reciprocal",
246 name: "binary",
247 roots: []string{"bin/bin1.meta_lic"},
248 expectedOut: []matcher{
249 hr{},
250 library{"Android"},
251 usedBy{"bin/bin1"},
252 firstParty{},
253 hr{},
254 library{"Device"},
255 usedBy{"bin/bin1"},
256 library{"External"},
257 usedBy{"bin/bin1"},
258 reciprocal{},
259 },
260 },
261 {
262 condition: "reciprocal",
263 name: "library",
264 roots: []string{"lib/libd.so.meta_lic"},
265 expectedOut: []matcher{
266 hr{},
267 library{"External"},
268 usedBy{"lib/libd.so"},
269 notice{},
270 },
271 },
272 {
273 condition: "restricted",
274 name: "apex",
275 roots: []string{"highest.apex.meta_lic"},
276 expectedOut: []matcher{
277 hr{},
278 library{"Android"},
279 usedBy{"highest.apex"},
280 usedBy{"highest.apex/bin/bin1"},
281 usedBy{"highest.apex/bin/bin2"},
282 firstParty{},
283 hr{},
284 library{"Android"},
285 usedBy{"highest.apex/bin/bin2"},
286 usedBy{"highest.apex/lib/libb.so"},
287 library{"Device"},
288 usedBy{"highest.apex/bin/bin1"},
289 usedBy{"highest.apex/lib/liba.so"},
290 restricted{},
291 hr{},
292 library{"External"},
293 usedBy{"highest.apex/bin/bin1"},
294 reciprocal{},
295 },
296 },
297 {
298 condition: "restricted",
299 name: "container",
300 roots: []string{"container.zip.meta_lic"},
301 expectedOut: []matcher{
302 hr{},
303 library{"Android"},
304 usedBy{"container.zip"},
305 usedBy{"container.zip/bin1"},
306 usedBy{"container.zip/bin2"},
307 firstParty{},
308 hr{},
309 library{"Android"},
310 usedBy{"container.zip/bin2"},
311 usedBy{"container.zip/libb.so"},
312 library{"Device"},
313 usedBy{"container.zip/bin1"},
314 usedBy{"container.zip/liba.so"},
315 restricted{},
316 hr{},
317 library{"External"},
318 usedBy{"container.zip/bin1"},
319 reciprocal{},
320 },
321 },
322 {
323 condition: "restricted",
324 name: "application",
325 roots: []string{"application.meta_lic"},
326 expectedOut: []matcher{
327 hr{},
328 library{"Android"},
329 usedBy{"application"},
330 firstParty{},
331 hr{},
332 library{"Device"},
333 usedBy{"application"},
334 restricted{},
335 },
336 },
337 {
338 condition: "restricted",
339 name: "binary",
340 roots: []string{"bin/bin1.meta_lic"},
341 expectedOut: []matcher{
342 hr{},
343 library{"Android"},
344 usedBy{"bin/bin1"},
345 firstParty{},
346 hr{},
347 library{"Device"},
348 usedBy{"bin/bin1"},
349 restricted{},
350 hr{},
351 library{"External"},
352 usedBy{"bin/bin1"},
353 reciprocal{},
354 },
355 },
356 {
357 condition: "restricted",
358 name: "library",
359 roots: []string{"lib/libd.so.meta_lic"},
360 expectedOut: []matcher{
361 hr{},
362 library{"External"},
363 usedBy{"lib/libd.so"},
364 notice{},
365 },
366 },
367 {
368 condition: "proprietary",
369 name: "apex",
370 roots: []string{"highest.apex.meta_lic"},
371 expectedOut: []matcher{
372 hr{},
373 library{"Android"},
374 usedBy{"highest.apex/bin/bin2"},
375 usedBy{"highest.apex/lib/libb.so"},
376 restricted{},
377 hr{},
378 library{"Android"},
379 usedBy{"highest.apex"},
380 usedBy{"highest.apex/bin/bin1"},
381 firstParty{},
382 hr{},
383 library{"Android"},
384 usedBy{"highest.apex/bin/bin2"},
385 library{"Device"},
386 usedBy{"highest.apex/bin/bin1"},
387 usedBy{"highest.apex/lib/liba.so"},
388 library{"External"},
389 usedBy{"highest.apex/bin/bin1"},
390 proprietary{},
391 },
392 },
393 {
394 condition: "proprietary",
395 name: "container",
396 roots: []string{"container.zip.meta_lic"},
397 expectedOut: []matcher{
398 hr{},
399 library{"Android"},
400 usedBy{"container.zip/bin2"},
401 usedBy{"container.zip/libb.so"},
402 restricted{},
403 hr{},
404 library{"Android"},
405 usedBy{"container.zip"},
406 usedBy{"container.zip/bin1"},
407 firstParty{},
408 hr{},
409 library{"Android"},
410 usedBy{"container.zip/bin2"},
411 library{"Device"},
412 usedBy{"container.zip/bin1"},
413 usedBy{"container.zip/liba.so"},
414 library{"External"},
415 usedBy{"container.zip/bin1"},
416 proprietary{},
417 },
418 },
419 {
420 condition: "proprietary",
421 name: "application",
422 roots: []string{"application.meta_lic"},
423 expectedOut: []matcher{
424 hr{},
425 library{"Android"},
426 usedBy{"application"},
427 firstParty{},
428 hr{},
429 library{"Device"},
430 usedBy{"application"},
431 proprietary{},
432 },
433 },
434 {
435 condition: "proprietary",
436 name: "binary",
437 roots: []string{"bin/bin1.meta_lic"},
438 expectedOut: []matcher{
439 hr{},
440 library{"Android"},
441 usedBy{"bin/bin1"},
442 firstParty{},
443 hr{},
444 library{"Device"},
445 usedBy{"bin/bin1"},
446 library{"External"},
447 usedBy{"bin/bin1"},
448 proprietary{},
449 },
450 },
451 {
452 condition: "proprietary",
453 name: "library",
454 roots: []string{"lib/libd.so.meta_lic"},
455 expectedOut: []matcher{
456 hr{},
457 library{"External"},
458 usedBy{"lib/libd.so"},
459 notice{},
460 },
461 },
462 }
463 for _, tt := range tests {
464 t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
465 stdout := &bytes.Buffer{}
466 stderr := &bytes.Buffer{}
467
468 rootFiles := make([]string, 0, len(tt.roots))
469 for _, r := range tt.roots {
470 rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
471 }
472
473 ctx := context{stdout, stderr, os.DirFS("."), tt.stripPrefix}
474
475 err := textNotice(&ctx, rootFiles...)
476 if err != nil {
477 t.Fatalf("textnotice: error = %w, stderr = %v", err, stderr)
478 return
479 }
480 if stderr.Len() > 0 {
481 t.Errorf("textnotice: gotStderr = %v, want none", stderr)
482 }
483
484 t.Logf("got stdout: %s", stdout.String())
485
486 t.Logf("want stdout: %s", matcherList(tt.expectedOut).String())
487
488 out := bufio.NewScanner(stdout)
489 lineno := 0
490 for out.Scan() {
491 line := out.Text()
492 if strings.TrimLeft(line, " ") == "" {
493 continue
494 }
495 if len(tt.expectedOut) <= lineno {
496 t.Errorf("unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut))
497 } else if !tt.expectedOut[lineno].isMatch(line) {
498 t.Errorf("unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno].String())
499 }
500 lineno++
501 }
502 for ; lineno < len(tt.expectedOut); lineno++ {
503 t.Errorf("textnotice: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno].String())
504 }
505 })
506 }
507}
508
509type matcher interface {
510 isMatch(line string) bool
511 String() string
512}
513
514type hr struct{}
515
516func (m hr) isMatch(line string) bool {
517 return horizontalRule.MatchString(line)
518}
519
520func (m hr) String() string {
521 return " ================================================== "
522}
523
524type library struct {
525 name string
526}
527
528func (m library) isMatch(line string) bool {
529 return strings.HasPrefix(line, m.name+" ")
530}
531
532func (m library) String() string {
533 return m.name + " used by:"
534}
535
536type usedBy struct {
537 name string
538}
539
540func (m usedBy) isMatch(line string) bool {
541 return len(line) > 0 && line[0] == ' ' && strings.HasPrefix(strings.TrimLeft(line, " "), "out/") && strings.HasSuffix(line, "/"+m.name)
542}
543
544func (m usedBy) String() string {
545 return " out/.../" + m.name
546}
547
548type firstParty struct{}
549
550func (m firstParty) isMatch(line string) bool {
551 return strings.HasPrefix(strings.TrimLeft(line, " "), "&&&First Party License&&&")
552}
553
554func (m firstParty) String() string {
555 return "&&&First Party License&&&"
556}
557
558type notice struct{}
559
560func (m notice) isMatch(line string) bool {
561 return strings.HasPrefix(strings.TrimLeft(line, " "), "%%%Notice License%%%")
562}
563
564func (m notice) String() string {
565 return "%%%Notice License%%%"
566}
567
568type reciprocal struct{}
569
570func (m reciprocal) isMatch(line string) bool {
571 return strings.HasPrefix(strings.TrimLeft(line, " "), "$$$Reciprocal License$$$")
572}
573
574func (m reciprocal) String() string {
575 return "$$$Reciprocal License$$$"
576}
577
578type restricted struct{}
579
580func (m restricted) isMatch(line string) bool {
581 return strings.HasPrefix(strings.TrimLeft(line, " "), "###Restricted License###")
582}
583
584func (m restricted) String() string {
585 return "###Restricted License###"
586}
587
588type proprietary struct{}
589
590func (m proprietary) isMatch(line string) bool {
591 return strings.HasPrefix(strings.TrimLeft(line, " "), "@@@Proprietary License@@@")
592}
593
594func (m proprietary) String() string {
595 return "@@@Proprietary License@@@"
596}
597
598type matcherList []matcher
599
600func (l matcherList) String() string {
601 var sb strings.Builder
602 for _, m := range l {
603 s := m.String()
604 if s[:3] == s[len(s)-3:] {
605 fmt.Fprintln(&sb)
606 }
607 fmt.Fprintf(&sb, "%s\n", s)
608 if s[:3] == s[len(s)-3:] {
609 fmt.Fprintln(&sb)
610 }
611 }
612 return sb.String()
613}