blob: b9270181734325fef1a9c5e2807fb89236dc0832 [file] [log] [blame]
Bob Badour6ea14572022-01-23 17:15:46 -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 "html"
22 "os"
Colin Crossbb45f8c2022-01-28 15:18:19 -080023 "reflect"
Bob Badour6ea14572022-01-23 17:15:46 -080024 "regexp"
25 "strings"
26 "testing"
Bob Badourc778e4c2022-03-22 13:05:19 -070027
28 "android/soong/tools/compliance"
Bob Badour6ea14572022-01-23 17:15:46 -080029)
30
31var (
32 horizontalRule = regexp.MustCompile(`^\s*<hr>\s*$`)
33 bodyTag = regexp.MustCompile(`^\s*<body>\s*$`)
34 boilerPlate = regexp.MustCompile(`^\s*(?:<ul class="file-list">|<ul>|</.*)\s*$`)
35 tocTag = regexp.MustCompile(`^\s*<ul class="toc">\s*$`)
36 libraryName = regexp.MustCompile(`^\s*<strong>(.*)</strong>\s\s*used\s\s*by\s*:\s*$`)
37 licenseText = regexp.MustCompile(`^\s*<a id="[^"]{32}"/><pre class="license-text">(.*)$`)
38 titleTag = regexp.MustCompile(`^\s*<title>(.*)</title>\s*$`)
39 h1Tag = regexp.MustCompile(`^\s*<h1>(.*)</h1>\s*$`)
40 usedByTarget = regexp.MustCompile(`^\s*<li>(?:<a href="#id[0-9]+">)?((?:out/(?:[^/<]*/)+)[^/<]*)(?:</a>)?\s*$`)
41 installTarget = regexp.MustCompile(`^\s*<li id="id[0-9]+"><strong>(.*)</strong>\s*$`)
42 libReference = regexp.MustCompile(`^\s*<li><a href="#[^"]{32}">(.*)</a>\s*$`)
43)
44
Colin Crossd0f05c92022-01-27 15:40:29 -080045func TestMain(m *testing.M) {
46 // Change into the parent directory before running the tests
47 // so they can find the testdata directory.
48 if err := os.Chdir(".."); err != nil {
49 fmt.Printf("failed to change to testdata directory: %s\n", err)
50 os.Exit(1)
51 }
52 os.Exit(m.Run())
53}
54
Bob Badour6ea14572022-01-23 17:15:46 -080055func Test(t *testing.T) {
56 tests := []struct {
Colin Crossbb45f8c2022-01-28 15:18:19 -080057 condition string
58 name string
Bob Badourc778e4c2022-03-22 13:05:19 -070059 outDir string
Colin Crossbb45f8c2022-01-28 15:18:19 -080060 roots []string
61 includeTOC bool
62 stripPrefix string
63 title string
64 expectedOut []matcher
65 expectedDeps []string
Bob Badour6ea14572022-01-23 17:15:46 -080066 }{
67 {
68 condition: "firstparty",
69 name: "apex",
70 roots: []string{"highest.apex.meta_lic"},
71 expectedOut: []matcher{
72 hr{},
73 library{"Android"},
74 usedBy{"highest.apex"},
75 usedBy{"highest.apex/bin/bin1"},
76 usedBy{"highest.apex/bin/bin2"},
77 usedBy{"highest.apex/lib/liba.so"},
78 usedBy{"highest.apex/lib/libb.so"},
79 firstParty{},
80 },
Colin Crossbb45f8c2022-01-28 15:18:19 -080081 expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
Bob Badour6ea14572022-01-23 17:15:46 -080082 },
83 {
84 condition: "firstparty",
85 name: "apex+toc",
86 roots: []string{"highest.apex.meta_lic"},
87 includeTOC: true,
88 expectedOut: []matcher{
89 toc{},
90 target{"highest.apex"},
91 uses{"Android"},
92 target{"highest.apex/bin/bin1"},
93 uses{"Android"},
94 target{"highest.apex/bin/bin2"},
95 uses{"Android"},
96 target{"highest.apex/lib/liba.so"},
97 uses{"Android"},
98 target{"highest.apex/lib/libb.so"},
99 uses{"Android"},
100 hr{},
101 library{"Android"},
102 usedBy{"highest.apex"},
103 usedBy{"highest.apex/bin/bin1"},
104 usedBy{"highest.apex/bin/bin2"},
105 usedBy{"highest.apex/lib/liba.so"},
106 usedBy{"highest.apex/lib/libb.so"},
107 firstParty{},
108 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800109 expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
Bob Badour6ea14572022-01-23 17:15:46 -0800110 },
111 {
112 condition: "firstparty",
113 name: "apex-with-title",
114 roots: []string{"highest.apex.meta_lic"},
115 title: "Emperor",
116 expectedOut: []matcher{
117 pageTitle{"Emperor"},
118 hr{},
119 library{"Android"},
120 usedBy{"highest.apex"},
121 usedBy{"highest.apex/bin/bin1"},
122 usedBy{"highest.apex/bin/bin2"},
123 usedBy{"highest.apex/lib/liba.so"},
124 usedBy{"highest.apex/lib/libb.so"},
125 firstParty{},
126 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800127 expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
Bob Badour6ea14572022-01-23 17:15:46 -0800128 },
129 {
130 condition: "firstparty",
131 name: "apex-with-title+toc",
132 roots: []string{"highest.apex.meta_lic"},
133 includeTOC: true,
134 title: "Emperor",
135 expectedOut: []matcher{
136 pageTitle{"Emperor"},
137 toc{},
138 target{"highest.apex"},
139 uses{"Android"},
140 target{"highest.apex/bin/bin1"},
141 uses{"Android"},
142 target{"highest.apex/bin/bin2"},
143 uses{"Android"},
144 target{"highest.apex/lib/liba.so"},
145 uses{"Android"},
146 target{"highest.apex/lib/libb.so"},
147 uses{"Android"},
148 hr{},
149 library{"Android"},
150 usedBy{"highest.apex"},
151 usedBy{"highest.apex/bin/bin1"},
152 usedBy{"highest.apex/bin/bin2"},
153 usedBy{"highest.apex/lib/liba.so"},
154 usedBy{"highest.apex/lib/libb.so"},
155 firstParty{},
156 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800157 expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
Bob Badour6ea14572022-01-23 17:15:46 -0800158 },
159 {
160 condition: "firstparty",
161 name: "container",
162 roots: []string{"container.zip.meta_lic"},
163 expectedOut: []matcher{
164 hr{},
165 library{"Android"},
166 usedBy{"container.zip"},
167 usedBy{"container.zip/bin1"},
168 usedBy{"container.zip/bin2"},
169 usedBy{"container.zip/liba.so"},
170 usedBy{"container.zip/libb.so"},
171 firstParty{},
172 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800173 expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
Bob Badour6ea14572022-01-23 17:15:46 -0800174 },
175 {
176 condition: "firstparty",
177 name: "application",
178 roots: []string{"application.meta_lic"},
179 expectedOut: []matcher{
180 hr{},
181 library{"Android"},
182 usedBy{"application"},
183 firstParty{},
184 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800185 expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
Bob Badour6ea14572022-01-23 17:15:46 -0800186 },
187 {
188 condition: "firstparty",
189 name: "binary",
190 roots: []string{"bin/bin1.meta_lic"},
191 expectedOut: []matcher{
192 hr{},
193 library{"Android"},
194 usedBy{"bin/bin1"},
195 firstParty{},
196 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800197 expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
Bob Badour6ea14572022-01-23 17:15:46 -0800198 },
199 {
200 condition: "firstparty",
201 name: "library",
202 roots: []string{"lib/libd.so.meta_lic"},
203 expectedOut: []matcher{
204 hr{},
205 library{"Android"},
206 usedBy{"lib/libd.so"},
207 firstParty{},
208 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800209 expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
Bob Badour6ea14572022-01-23 17:15:46 -0800210 },
211 {
212 condition: "notice",
213 name: "apex",
214 roots: []string{"highest.apex.meta_lic"},
215 expectedOut: []matcher{
216 hr{},
217 library{"Android"},
218 usedBy{"highest.apex"},
219 usedBy{"highest.apex/bin/bin1"},
220 usedBy{"highest.apex/bin/bin2"},
221 usedBy{"highest.apex/lib/libb.so"},
222 firstParty{},
223 hr{},
224 library{"Device"},
225 usedBy{"highest.apex/bin/bin1"},
226 usedBy{"highest.apex/lib/liba.so"},
227 library{"External"},
228 usedBy{"highest.apex/bin/bin1"},
229 notice{},
230 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800231 expectedDeps: []string{
232 "testdata/firstparty/FIRST_PARTY_LICENSE",
233 "testdata/notice/NOTICE_LICENSE",
234 },
Bob Badour6ea14572022-01-23 17:15:46 -0800235 },
236 {
237 condition: "notice",
238 name: "container",
239 roots: []string{"container.zip.meta_lic"},
240 expectedOut: []matcher{
241 hr{},
242 library{"Android"},
243 usedBy{"container.zip"},
244 usedBy{"container.zip/bin1"},
245 usedBy{"container.zip/bin2"},
246 usedBy{"container.zip/libb.so"},
247 firstParty{},
248 hr{},
249 library{"Device"},
250 usedBy{"container.zip/bin1"},
251 usedBy{"container.zip/liba.so"},
252 library{"External"},
253 usedBy{"container.zip/bin1"},
254 notice{},
255 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800256 expectedDeps: []string{
257 "testdata/firstparty/FIRST_PARTY_LICENSE",
258 "testdata/notice/NOTICE_LICENSE",
259 },
Bob Badour6ea14572022-01-23 17:15:46 -0800260 },
261 {
262 condition: "notice",
263 name: "application",
264 roots: []string{"application.meta_lic"},
265 expectedOut: []matcher{
266 hr{},
267 library{"Android"},
268 usedBy{"application"},
269 firstParty{},
270 hr{},
271 library{"Device"},
272 usedBy{"application"},
273 notice{},
274 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800275 expectedDeps: []string{
276 "testdata/firstparty/FIRST_PARTY_LICENSE",
277 "testdata/notice/NOTICE_LICENSE",
278 },
Bob Badour6ea14572022-01-23 17:15:46 -0800279 },
280 {
281 condition: "notice",
282 name: "binary",
283 roots: []string{"bin/bin1.meta_lic"},
284 expectedOut: []matcher{
285 hr{},
286 library{"Android"},
287 usedBy{"bin/bin1"},
288 firstParty{},
289 hr{},
290 library{"Device"},
291 usedBy{"bin/bin1"},
292 library{"External"},
293 usedBy{"bin/bin1"},
294 notice{},
295 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800296 expectedDeps: []string{
297 "testdata/firstparty/FIRST_PARTY_LICENSE",
298 "testdata/notice/NOTICE_LICENSE",
299 },
Bob Badour6ea14572022-01-23 17:15:46 -0800300 },
301 {
302 condition: "notice",
303 name: "library",
304 roots: []string{"lib/libd.so.meta_lic"},
305 expectedOut: []matcher{
306 hr{},
307 library{"External"},
308 usedBy{"lib/libd.so"},
309 notice{},
310 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800311 expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
Bob Badour6ea14572022-01-23 17:15:46 -0800312 },
313 {
314 condition: "reciprocal",
315 name: "apex",
316 roots: []string{"highest.apex.meta_lic"},
317 expectedOut: []matcher{
318 hr{},
319 library{"Android"},
320 usedBy{"highest.apex"},
321 usedBy{"highest.apex/bin/bin1"},
322 usedBy{"highest.apex/bin/bin2"},
323 usedBy{"highest.apex/lib/libb.so"},
324 firstParty{},
325 hr{},
326 library{"Device"},
327 usedBy{"highest.apex/bin/bin1"},
328 usedBy{"highest.apex/lib/liba.so"},
329 library{"External"},
330 usedBy{"highest.apex/bin/bin1"},
331 reciprocal{},
332 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800333 expectedDeps: []string{
334 "testdata/firstparty/FIRST_PARTY_LICENSE",
335 "testdata/reciprocal/RECIPROCAL_LICENSE",
336 },
Bob Badour6ea14572022-01-23 17:15:46 -0800337 },
338 {
339 condition: "reciprocal",
340 name: "container",
341 roots: []string{"container.zip.meta_lic"},
342 expectedOut: []matcher{
343 hr{},
344 library{"Android"},
345 usedBy{"container.zip"},
346 usedBy{"container.zip/bin1"},
347 usedBy{"container.zip/bin2"},
348 usedBy{"container.zip/libb.so"},
349 firstParty{},
350 hr{},
351 library{"Device"},
352 usedBy{"container.zip/bin1"},
353 usedBy{"container.zip/liba.so"},
354 library{"External"},
355 usedBy{"container.zip/bin1"},
356 reciprocal{},
357 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800358 expectedDeps: []string{
359 "testdata/firstparty/FIRST_PARTY_LICENSE",
360 "testdata/reciprocal/RECIPROCAL_LICENSE",
361 },
Bob Badour6ea14572022-01-23 17:15:46 -0800362 },
363 {
364 condition: "reciprocal",
365 name: "application",
366 roots: []string{"application.meta_lic"},
367 expectedOut: []matcher{
368 hr{},
369 library{"Android"},
370 usedBy{"application"},
371 firstParty{},
372 hr{},
373 library{"Device"},
374 usedBy{"application"},
375 reciprocal{},
376 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800377 expectedDeps: []string{
378 "testdata/firstparty/FIRST_PARTY_LICENSE",
379 "testdata/reciprocal/RECIPROCAL_LICENSE",
380 },
Bob Badour6ea14572022-01-23 17:15:46 -0800381 },
382 {
383 condition: "reciprocal",
384 name: "binary",
385 roots: []string{"bin/bin1.meta_lic"},
386 expectedOut: []matcher{
387 hr{},
388 library{"Android"},
389 usedBy{"bin/bin1"},
390 firstParty{},
391 hr{},
392 library{"Device"},
393 usedBy{"bin/bin1"},
394 library{"External"},
395 usedBy{"bin/bin1"},
396 reciprocal{},
397 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800398 expectedDeps: []string{
399 "testdata/firstparty/FIRST_PARTY_LICENSE",
400 "testdata/reciprocal/RECIPROCAL_LICENSE",
401 },
Bob Badour6ea14572022-01-23 17:15:46 -0800402 },
403 {
404 condition: "reciprocal",
405 name: "library",
406 roots: []string{"lib/libd.so.meta_lic"},
407 expectedOut: []matcher{
408 hr{},
409 library{"External"},
410 usedBy{"lib/libd.so"},
411 notice{},
412 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800413 expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
Bob Badour6ea14572022-01-23 17:15:46 -0800414 },
415 {
416 condition: "restricted",
417 name: "apex",
418 roots: []string{"highest.apex.meta_lic"},
419 expectedOut: []matcher{
420 hr{},
421 library{"Android"},
422 usedBy{"highest.apex"},
423 usedBy{"highest.apex/bin/bin1"},
424 usedBy{"highest.apex/bin/bin2"},
425 firstParty{},
426 hr{},
427 library{"Android"},
428 usedBy{"highest.apex/bin/bin2"},
429 usedBy{"highest.apex/lib/libb.so"},
430 library{"Device"},
431 usedBy{"highest.apex/bin/bin1"},
432 usedBy{"highest.apex/lib/liba.so"},
433 restricted{},
434 hr{},
435 library{"External"},
436 usedBy{"highest.apex/bin/bin1"},
437 reciprocal{},
438 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800439 expectedDeps: []string{
440 "testdata/firstparty/FIRST_PARTY_LICENSE",
441 "testdata/reciprocal/RECIPROCAL_LICENSE",
442 "testdata/restricted/RESTRICTED_LICENSE",
443 },
Bob Badour6ea14572022-01-23 17:15:46 -0800444 },
445 {
446 condition: "restricted",
447 name: "container",
448 roots: []string{"container.zip.meta_lic"},
449 expectedOut: []matcher{
450 hr{},
451 library{"Android"},
452 usedBy{"container.zip"},
453 usedBy{"container.zip/bin1"},
454 usedBy{"container.zip/bin2"},
455 firstParty{},
456 hr{},
457 library{"Android"},
458 usedBy{"container.zip/bin2"},
459 usedBy{"container.zip/libb.so"},
460 library{"Device"},
461 usedBy{"container.zip/bin1"},
462 usedBy{"container.zip/liba.so"},
463 restricted{},
464 hr{},
465 library{"External"},
466 usedBy{"container.zip/bin1"},
467 reciprocal{},
468 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800469 expectedDeps: []string{
470 "testdata/firstparty/FIRST_PARTY_LICENSE",
471 "testdata/reciprocal/RECIPROCAL_LICENSE",
472 "testdata/restricted/RESTRICTED_LICENSE",
473 },
Bob Badour6ea14572022-01-23 17:15:46 -0800474 },
475 {
476 condition: "restricted",
477 name: "application",
478 roots: []string{"application.meta_lic"},
479 expectedOut: []matcher{
480 hr{},
481 library{"Android"},
482 usedBy{"application"},
483 firstParty{},
484 hr{},
485 library{"Device"},
486 usedBy{"application"},
487 restricted{},
488 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800489 expectedDeps: []string{
490 "testdata/firstparty/FIRST_PARTY_LICENSE",
491 "testdata/restricted/RESTRICTED_LICENSE",
492 },
Bob Badour6ea14572022-01-23 17:15:46 -0800493 },
494 {
495 condition: "restricted",
496 name: "binary",
497 roots: []string{"bin/bin1.meta_lic"},
498 expectedOut: []matcher{
499 hr{},
500 library{"Android"},
501 usedBy{"bin/bin1"},
502 firstParty{},
503 hr{},
504 library{"Device"},
505 usedBy{"bin/bin1"},
506 restricted{},
507 hr{},
508 library{"External"},
509 usedBy{"bin/bin1"},
510 reciprocal{},
511 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800512 expectedDeps: []string{
513 "testdata/firstparty/FIRST_PARTY_LICENSE",
514 "testdata/reciprocal/RECIPROCAL_LICENSE",
515 "testdata/restricted/RESTRICTED_LICENSE",
516 },
Bob Badour6ea14572022-01-23 17:15:46 -0800517 },
518 {
519 condition: "restricted",
520 name: "library",
521 roots: []string{"lib/libd.so.meta_lic"},
522 expectedOut: []matcher{
523 hr{},
524 library{"External"},
525 usedBy{"lib/libd.so"},
526 notice{},
527 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800528 expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
Bob Badour6ea14572022-01-23 17:15:46 -0800529 },
530 {
531 condition: "proprietary",
532 name: "apex",
533 roots: []string{"highest.apex.meta_lic"},
534 expectedOut: []matcher{
535 hr{},
536 library{"Android"},
537 usedBy{"highest.apex/bin/bin2"},
538 usedBy{"highest.apex/lib/libb.so"},
539 restricted{},
540 hr{},
541 library{"Android"},
542 usedBy{"highest.apex"},
543 usedBy{"highest.apex/bin/bin1"},
544 firstParty{},
545 hr{},
546 library{"Android"},
547 usedBy{"highest.apex/bin/bin2"},
548 library{"Device"},
549 usedBy{"highest.apex/bin/bin1"},
550 usedBy{"highest.apex/lib/liba.so"},
551 library{"External"},
552 usedBy{"highest.apex/bin/bin1"},
553 proprietary{},
554 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800555 expectedDeps: []string{
556 "testdata/firstparty/FIRST_PARTY_LICENSE",
557 "testdata/proprietary/PROPRIETARY_LICENSE",
558 "testdata/restricted/RESTRICTED_LICENSE",
559 },
Bob Badour6ea14572022-01-23 17:15:46 -0800560 },
561 {
562 condition: "proprietary",
563 name: "container",
564 roots: []string{"container.zip.meta_lic"},
565 expectedOut: []matcher{
566 hr{},
567 library{"Android"},
568 usedBy{"container.zip/bin2"},
569 usedBy{"container.zip/libb.so"},
570 restricted{},
571 hr{},
572 library{"Android"},
573 usedBy{"container.zip"},
574 usedBy{"container.zip/bin1"},
575 firstParty{},
576 hr{},
577 library{"Android"},
578 usedBy{"container.zip/bin2"},
579 library{"Device"},
580 usedBy{"container.zip/bin1"},
581 usedBy{"container.zip/liba.so"},
582 library{"External"},
583 usedBy{"container.zip/bin1"},
584 proprietary{},
585 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800586 expectedDeps: []string{
587 "testdata/firstparty/FIRST_PARTY_LICENSE",
588 "testdata/proprietary/PROPRIETARY_LICENSE",
589 "testdata/restricted/RESTRICTED_LICENSE",
590 },
Bob Badour6ea14572022-01-23 17:15:46 -0800591 },
592 {
593 condition: "proprietary",
594 name: "application",
595 roots: []string{"application.meta_lic"},
596 expectedOut: []matcher{
597 hr{},
598 library{"Android"},
599 usedBy{"application"},
600 firstParty{},
601 hr{},
602 library{"Device"},
603 usedBy{"application"},
604 proprietary{},
605 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800606 expectedDeps: []string{
607 "testdata/firstparty/FIRST_PARTY_LICENSE",
608 "testdata/proprietary/PROPRIETARY_LICENSE",
609 },
Bob Badour6ea14572022-01-23 17:15:46 -0800610 },
611 {
612 condition: "proprietary",
613 name: "binary",
614 roots: []string{"bin/bin1.meta_lic"},
615 expectedOut: []matcher{
616 hr{},
617 library{"Android"},
618 usedBy{"bin/bin1"},
619 firstParty{},
620 hr{},
621 library{"Device"},
622 usedBy{"bin/bin1"},
623 library{"External"},
624 usedBy{"bin/bin1"},
625 proprietary{},
626 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800627 expectedDeps: []string{
628 "testdata/firstparty/FIRST_PARTY_LICENSE",
629 "testdata/proprietary/PROPRIETARY_LICENSE",
630 },
Bob Badour6ea14572022-01-23 17:15:46 -0800631 },
632 {
633 condition: "proprietary",
634 name: "library",
635 roots: []string{"lib/libd.so.meta_lic"},
636 expectedOut: []matcher{
637 hr{},
638 library{"External"},
639 usedBy{"lib/libd.so"},
640 notice{},
641 },
Colin Crossbb45f8c2022-01-28 15:18:19 -0800642 expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
Bob Badour6ea14572022-01-23 17:15:46 -0800643 },
644 }
645 for _, tt := range tests {
646 t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
647 stdout := &bytes.Buffer{}
648 stderr := &bytes.Buffer{}
649
650 rootFiles := make([]string, 0, len(tt.roots))
651 for _, r := range tt.roots {
652 rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
653 }
654
Colin Crossbb45f8c2022-01-28 15:18:19 -0800655 var deps []string
656
Bob Badourc778e4c2022-03-22 13:05:19 -0700657 ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), tt.includeTOC, "", []string{tt.stripPrefix}, tt.title, &deps}
Bob Badour6ea14572022-01-23 17:15:46 -0800658
659 err := htmlNotice(&ctx, rootFiles...)
660 if err != nil {
Colin Cross179ec3e2022-01-27 15:47:09 -0800661 t.Fatalf("htmlnotice: error = %v, stderr = %v", err, stderr)
Bob Badour6ea14572022-01-23 17:15:46 -0800662 return
663 }
664 if stderr.Len() > 0 {
665 t.Errorf("htmlnotice: gotStderr = %v, want none", stderr)
666 }
667
668 t.Logf("got stdout: %s", stdout.String())
669
670 t.Logf("want stdout: %s", matcherList(tt.expectedOut).String())
671
672 out := bufio.NewScanner(stdout)
673 lineno := 0
674 inBody := false
675 hasTitle := false
676 ttle, expectTitle := tt.expectedOut[0].(pageTitle)
677 for out.Scan() {
678 line := out.Text()
679 if strings.TrimLeft(line, " ") == "" {
680 continue
681 }
682 if !inBody {
683 if expectTitle {
Bob Badoure9b38c12022-02-09 15:56:59 -0800684 if tl := checkTitle(line); len(tl) > 0 {
Bob Badour6ea14572022-01-23 17:15:46 -0800685 if tl != ttle.t {
686 t.Errorf("htmlnotice: unexpected title: got %q, want %q", tl, ttle.t)
687 }
688 hasTitle = true
689 }
690 }
691 if bodyTag.MatchString(line) {
692 inBody = true
693 if expectTitle && !hasTitle {
694 t.Errorf("htmlnotice: missing title: got no <title> tag, want <title>%s</title>", ttle.t)
695 }
696 }
697 continue
698 }
699 if boilerPlate.MatchString(line) {
700 continue
701 }
702 if len(tt.expectedOut) <= lineno {
703 t.Errorf("htmlnotice: unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut))
704 } else if !tt.expectedOut[lineno].isMatch(line) {
705 t.Errorf("htmlnotice: unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno].String())
706 }
707 lineno++
708 }
709 if !inBody {
710 t.Errorf("htmlnotice: missing body: got no <body> tag, want <body> tag followed by %s", matcherList(tt.expectedOut).String())
711 return
712 }
713 for ; lineno < len(tt.expectedOut); lineno++ {
714 t.Errorf("htmlnotice: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno].String())
715 }
Colin Crossbb45f8c2022-01-28 15:18:19 -0800716
717 t.Logf("got deps: %q", deps)
718
719 t.Logf("want deps: %q", tt.expectedDeps)
720
721 if g, w := deps, tt.expectedDeps; !reflect.DeepEqual(g, w) {
722 t.Errorf("unexpected deps, wanted:\n%s\ngot:\n%s\n",
723 strings.Join(w, "\n"), strings.Join(g, "\n"))
724 }
Bob Badour6ea14572022-01-23 17:15:46 -0800725 })
726 }
727}
728
729func checkTitle(line string) string {
730 groups := titleTag.FindStringSubmatch(line)
731 if len(groups) != 2 {
732 return ""
733 }
734 return groups[1]
735}
736
737type matcher interface {
738 isMatch(line string) bool
739 String() string
740}
741
742type pageTitle struct {
743 t string
744}
745
746func (m pageTitle) isMatch(line string) bool {
747 groups := h1Tag.FindStringSubmatch(line)
748 if len(groups) != 2 {
749 return false
750 }
751 return groups[1] == html.EscapeString(m.t)
752}
753
754func (m pageTitle) String() string {
755 return " <h1>" + html.EscapeString(m.t) + "</h1>"
756}
757
758type toc struct{}
759
760func (m toc) isMatch(line string) bool {
761 return tocTag.MatchString(line)
762}
763
764func (m toc) String() string {
765 return ` <ul class="toc">`
766}
767
768type target struct {
769 name string
770}
771
772func (m target) isMatch(line string) bool {
773 groups := installTarget.FindStringSubmatch(line)
774 if len(groups) != 2 {
775 return false
776 }
777 return strings.HasPrefix(groups[1], "out/") && strings.HasSuffix(groups[1], "/"+html.EscapeString(m.name))
778}
779
780func (m target) String() string {
781 return ` <li id="id#"><strong>` + html.EscapeString(m.name) + `</strong>`
782}
783
784type uses struct {
785 name string
786}
787
788func (m uses) isMatch(line string) bool {
789 groups := libReference.FindStringSubmatch(line)
790 if len(groups) != 2 {
791 return false
792 }
793 return groups[1] == html.EscapeString(m.name)
794}
795
796func (m uses) String() string {
797 return ` <li><a href="#hash">` + html.EscapeString(m.name) + `</a>`
798}
799
800type hr struct{}
801
802func (m hr) isMatch(line string) bool {
803 return horizontalRule.MatchString(line)
804}
805
806func (m hr) String() string {
807 return " <hr>"
808}
809
810type library struct {
811 name string
812}
813
814func (m library) isMatch(line string) bool {
815 groups := libraryName.FindStringSubmatch(line)
816 if len(groups) != 2 {
817 return false
818 }
819 return groups[1] == html.EscapeString(m.name)
820}
821
822func (m library) String() string {
823 return " <strong>" + html.EscapeString(m.name) + "</strong> used by:"
824}
825
826type usedBy struct {
827 name string
828}
829
830func (m usedBy) isMatch(line string) bool {
831 groups := usedByTarget.FindStringSubmatch(line)
832 if len(groups) != 2 {
833 return false
834 }
835 return strings.HasPrefix(groups[1], "out/") && strings.HasSuffix(groups[1], "/"+html.EscapeString(m.name))
836}
837
838func (m usedBy) String() string {
839 return " <li>out/.../" + html.EscapeString(m.name)
840}
841
842func matchesText(line, text string) bool {
843 groups := licenseText.FindStringSubmatch(line)
844 if len(groups) != 2 {
845 return false
846 }
847 return groups[1] == html.EscapeString(text)
848}
849
850func expectedText(text string) string {
851 return ` <a href="#hash"/><pre class="license-text">` + html.EscapeString(text)
852}
853
854type firstParty struct{}
855
856func (m firstParty) isMatch(line string) bool {
857 return matchesText(line, "&&&First Party License&&&")
858}
859
860func (m firstParty) String() string {
861 return expectedText("&&&First Party License&&&")
862}
863
864type notice struct{}
865
866func (m notice) isMatch(line string) bool {
867 return matchesText(line, "%%%Notice License%%%")
868}
869
870func (m notice) String() string {
871 return expectedText("%%%Notice License%%%")
872}
873
874type reciprocal struct{}
875
876func (m reciprocal) isMatch(line string) bool {
877 return matchesText(line, "$$$Reciprocal License$$$")
878}
879
880func (m reciprocal) String() string {
881 return expectedText("$$$Reciprocal License$$$")
882}
883
884type restricted struct{}
885
886func (m restricted) isMatch(line string) bool {
887 return matchesText(line, "###Restricted License###")
888}
889
890func (m restricted) String() string {
891 return expectedText("###Restricted License###")
892}
893
894type proprietary struct{}
895
896func (m proprietary) isMatch(line string) bool {
897 return matchesText(line, "@@@Proprietary License@@@")
898}
899
900func (m proprietary) String() string {
901 return expectedText("@@@Proprietary License@@@")
902}
903
904type matcherList []matcher
905
906func (l matcherList) String() string {
907 var sb strings.Builder
908 for _, m := range l {
909 s := m.String()
910 if s[:3] == s[len(s)-3:] {
911 fmt.Fprintln(&sb)
912 }
913 fmt.Fprintf(&sb, "%s\n", s)
914 if s[:3] == s[len(s)-3:] {
915 fmt.Fprintln(&sb)
916 }
917 }
918 return sb.String()
919}