1// 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	"reflect"
23	"regexp"
24	"strings"
25	"testing"
26
27	"android/soong/tools/compliance"
28)
29
30var (
31	horizontalRule = regexp.MustCompile("^===[=]*===$")
32)
33
34func TestMain(m *testing.M) {
35	// Change into the parent directory before running the tests
36	// so they can find the testdata directory.
37	if err := os.Chdir(".."); err != nil {
38		fmt.Printf("failed to change to testdata directory: %s\n", err)
39		os.Exit(1)
40	}
41	os.Exit(m.Run())
42}
43
44func Test(t *testing.T) {
45	tests := []struct {
46		condition    string
47		name         string
48		outDir       string
49		roots        []string
50		stripPrefix  string
51		expectedOut  []matcher
52		expectedDeps []string
53	}{
54		{
55			condition: "firstparty",
56			name:      "apex",
57			roots:     []string{"highest.apex.meta_lic"},
58			expectedOut: []matcher{
59				hr{},
60				library{"Android"},
61				usedBy{"highest.apex"},
62				usedBy{"highest.apex/bin/bin1"},
63				usedBy{"highest.apex/bin/bin2"},
64				usedBy{"highest.apex/lib/liba.so"},
65				usedBy{"highest.apex/lib/libb.so"},
66				firstParty{},
67			},
68			expectedDeps: []string{
69				"testdata/firstparty/FIRST_PARTY_LICENSE",
70				"testdata/firstparty/bin/bin1.meta_lic",
71				"testdata/firstparty/bin/bin2.meta_lic",
72				"testdata/firstparty/highest.apex.meta_lic",
73				"testdata/firstparty/lib/liba.so.meta_lic",
74				"testdata/firstparty/lib/libb.so.meta_lic",
75				"testdata/firstparty/lib/libc.a.meta_lic",
76				"testdata/firstparty/lib/libd.so.meta_lic",
77			},
78		},
79		{
80			condition: "firstparty",
81			name:      "container",
82			roots:     []string{"container.zip.meta_lic"},
83			expectedOut: []matcher{
84				hr{},
85				library{"Android"},
86				usedBy{"container.zip"},
87				usedBy{"container.zip/bin1"},
88				usedBy{"container.zip/bin2"},
89				usedBy{"container.zip/liba.so"},
90				usedBy{"container.zip/libb.so"},
91				firstParty{},
92			},
93			expectedDeps: []string{
94				"testdata/firstparty/FIRST_PARTY_LICENSE",
95				"testdata/firstparty/bin/bin1.meta_lic",
96				"testdata/firstparty/bin/bin2.meta_lic",
97				"testdata/firstparty/container.zip.meta_lic",
98				"testdata/firstparty/lib/liba.so.meta_lic",
99				"testdata/firstparty/lib/libb.so.meta_lic",
100				"testdata/firstparty/lib/libc.a.meta_lic",
101				"testdata/firstparty/lib/libd.so.meta_lic",
102			},
103		},
104		{
105			condition: "firstparty",
106			name:      "application",
107			roots:     []string{"application.meta_lic"},
108			expectedOut: []matcher{
109				hr{},
110				library{"Android"},
111				usedBy{"application"},
112				firstParty{},
113			},
114			expectedDeps: []string{
115				"testdata/firstparty/FIRST_PARTY_LICENSE",
116				"testdata/firstparty/application.meta_lic",
117				"testdata/firstparty/bin/bin3.meta_lic",
118				"testdata/firstparty/lib/liba.so.meta_lic",
119				"testdata/firstparty/lib/libb.so.meta_lic",
120			},
121		},
122		{
123			condition: "firstparty",
124			name:      "binary",
125			roots:     []string{"bin/bin1.meta_lic"},
126			expectedOut: []matcher{
127				hr{},
128				library{"Android"},
129				usedBy{"bin/bin1"},
130				firstParty{},
131			},
132			expectedDeps: []string{
133				"testdata/firstparty/FIRST_PARTY_LICENSE",
134				"testdata/firstparty/bin/bin1.meta_lic",
135				"testdata/firstparty/lib/liba.so.meta_lic",
136				"testdata/firstparty/lib/libc.a.meta_lic",
137			},
138		},
139		{
140			condition: "firstparty",
141			name:      "library",
142			roots:     []string{"lib/libd.so.meta_lic"},
143			expectedOut: []matcher{
144				hr{},
145				library{"Android"},
146				usedBy{"lib/libd.so"},
147				firstParty{},
148			},
149			expectedDeps: []string{
150				"testdata/firstparty/FIRST_PARTY_LICENSE",
151				"testdata/firstparty/lib/libd.so.meta_lic",
152			},
153		},
154		{
155			condition: "notice",
156			name:      "apex",
157			roots:     []string{"highest.apex.meta_lic"},
158			expectedOut: []matcher{
159				hr{},
160				library{"Android"},
161				usedBy{"highest.apex"},
162				usedBy{"highest.apex/bin/bin1"},
163				usedBy{"highest.apex/bin/bin2"},
164				usedBy{"highest.apex/lib/libb.so"},
165				firstParty{},
166				hr{},
167				library{"Device"},
168				usedBy{"highest.apex/bin/bin1"},
169				usedBy{"highest.apex/lib/liba.so"},
170				library{"External"},
171				usedBy{"highest.apex/bin/bin1"},
172				notice{},
173			},
174			expectedDeps: []string{
175				"testdata/firstparty/FIRST_PARTY_LICENSE",
176				"testdata/notice/NOTICE_LICENSE",
177				"testdata/notice/bin/bin1.meta_lic",
178				"testdata/notice/bin/bin2.meta_lic",
179				"testdata/notice/highest.apex.meta_lic",
180				"testdata/notice/lib/liba.so.meta_lic",
181				"testdata/notice/lib/libb.so.meta_lic",
182				"testdata/notice/lib/libc.a.meta_lic",
183				"testdata/notice/lib/libd.so.meta_lic",
184			},
185		},
186		{
187			condition: "notice",
188			name:      "container",
189			roots:     []string{"container.zip.meta_lic"},
190			expectedOut: []matcher{
191				hr{},
192				library{"Android"},
193				usedBy{"container.zip"},
194				usedBy{"container.zip/bin1"},
195				usedBy{"container.zip/bin2"},
196				usedBy{"container.zip/libb.so"},
197				firstParty{},
198				hr{},
199				library{"Device"},
200				usedBy{"container.zip/bin1"},
201				usedBy{"container.zip/liba.so"},
202				library{"External"},
203				usedBy{"container.zip/bin1"},
204				notice{},
205			},
206			expectedDeps: []string{
207				"testdata/firstparty/FIRST_PARTY_LICENSE",
208				"testdata/notice/NOTICE_LICENSE",
209				"testdata/notice/bin/bin1.meta_lic",
210				"testdata/notice/bin/bin2.meta_lic",
211				"testdata/notice/container.zip.meta_lic",
212				"testdata/notice/lib/liba.so.meta_lic",
213				"testdata/notice/lib/libb.so.meta_lic",
214				"testdata/notice/lib/libc.a.meta_lic",
215				"testdata/notice/lib/libd.so.meta_lic",
216			},
217		},
218		{
219			condition: "notice",
220			name:      "application",
221			roots:     []string{"application.meta_lic"},
222			expectedOut: []matcher{
223				hr{},
224				library{"Android"},
225				usedBy{"application"},
226				firstParty{},
227				hr{},
228				library{"Device"},
229				usedBy{"application"},
230				notice{},
231			},
232			expectedDeps: []string{
233				"testdata/firstparty/FIRST_PARTY_LICENSE",
234				"testdata/notice/NOTICE_LICENSE",
235				"testdata/notice/application.meta_lic",
236				"testdata/notice/bin/bin3.meta_lic",
237				"testdata/notice/lib/liba.so.meta_lic",
238				"testdata/notice/lib/libb.so.meta_lic",
239			},
240		},
241		{
242			condition: "notice",
243			name:      "binary",
244			roots:     []string{"bin/bin1.meta_lic"},
245			expectedOut: []matcher{
246				hr{},
247				library{"Android"},
248				usedBy{"bin/bin1"},
249				firstParty{},
250				hr{},
251				library{"Device"},
252				usedBy{"bin/bin1"},
253				library{"External"},
254				usedBy{"bin/bin1"},
255				notice{},
256			},
257			expectedDeps: []string{
258				"testdata/firstparty/FIRST_PARTY_LICENSE",
259				"testdata/notice/NOTICE_LICENSE",
260				"testdata/notice/bin/bin1.meta_lic",
261				"testdata/notice/lib/liba.so.meta_lic",
262				"testdata/notice/lib/libc.a.meta_lic",
263			},
264		},
265		{
266			condition: "notice",
267			name:      "library",
268			roots:     []string{"lib/libd.so.meta_lic"},
269			expectedOut: []matcher{
270				hr{},
271				library{"External"},
272				usedBy{"lib/libd.so"},
273				notice{},
274			},
275			expectedDeps: []string{
276				"testdata/notice/NOTICE_LICENSE",
277				"testdata/notice/lib/libd.so.meta_lic",
278			},
279		},
280		{
281			condition: "reciprocal",
282			name:      "apex",
283			roots:     []string{"highest.apex.meta_lic"},
284			expectedOut: []matcher{
285				hr{},
286				library{"Android"},
287				usedBy{"highest.apex"},
288				usedBy{"highest.apex/bin/bin1"},
289				usedBy{"highest.apex/bin/bin2"},
290				usedBy{"highest.apex/lib/libb.so"},
291				firstParty{},
292				hr{},
293				library{"Device"},
294				usedBy{"highest.apex/bin/bin1"},
295				usedBy{"highest.apex/lib/liba.so"},
296				library{"External"},
297				usedBy{"highest.apex/bin/bin1"},
298				reciprocal{},
299			},
300			expectedDeps: []string{
301				"testdata/firstparty/FIRST_PARTY_LICENSE",
302				"testdata/reciprocal/RECIPROCAL_LICENSE",
303				"testdata/reciprocal/bin/bin1.meta_lic",
304				"testdata/reciprocal/bin/bin2.meta_lic",
305				"testdata/reciprocal/highest.apex.meta_lic",
306				"testdata/reciprocal/lib/liba.so.meta_lic",
307				"testdata/reciprocal/lib/libb.so.meta_lic",
308				"testdata/reciprocal/lib/libc.a.meta_lic",
309				"testdata/reciprocal/lib/libd.so.meta_lic",
310			},
311		},
312		{
313			condition: "reciprocal",
314			name:      "container",
315			roots:     []string{"container.zip.meta_lic"},
316			expectedOut: []matcher{
317				hr{},
318				library{"Android"},
319				usedBy{"container.zip"},
320				usedBy{"container.zip/bin1"},
321				usedBy{"container.zip/bin2"},
322				usedBy{"container.zip/libb.so"},
323				firstParty{},
324				hr{},
325				library{"Device"},
326				usedBy{"container.zip/bin1"},
327				usedBy{"container.zip/liba.so"},
328				library{"External"},
329				usedBy{"container.zip/bin1"},
330				reciprocal{},
331			},
332			expectedDeps: []string{
333				"testdata/firstparty/FIRST_PARTY_LICENSE",
334				"testdata/reciprocal/RECIPROCAL_LICENSE",
335				"testdata/reciprocal/bin/bin1.meta_lic",
336				"testdata/reciprocal/bin/bin2.meta_lic",
337				"testdata/reciprocal/container.zip.meta_lic",
338				"testdata/reciprocal/lib/liba.so.meta_lic",
339				"testdata/reciprocal/lib/libb.so.meta_lic",
340				"testdata/reciprocal/lib/libc.a.meta_lic",
341				"testdata/reciprocal/lib/libd.so.meta_lic",
342			},
343		},
344		{
345			condition: "reciprocal",
346			name:      "application",
347			roots:     []string{"application.meta_lic"},
348			expectedOut: []matcher{
349				hr{},
350				library{"Android"},
351				usedBy{"application"},
352				firstParty{},
353				hr{},
354				library{"Device"},
355				usedBy{"application"},
356				reciprocal{},
357			},
358			expectedDeps: []string{
359				"testdata/firstparty/FIRST_PARTY_LICENSE",
360				"testdata/reciprocal/RECIPROCAL_LICENSE",
361				"testdata/reciprocal/application.meta_lic",
362				"testdata/reciprocal/bin/bin3.meta_lic",
363				"testdata/reciprocal/lib/liba.so.meta_lic",
364				"testdata/reciprocal/lib/libb.so.meta_lic",
365			},
366		},
367		{
368			condition: "reciprocal",
369			name:      "binary",
370			roots:     []string{"bin/bin1.meta_lic"},
371			expectedOut: []matcher{
372				hr{},
373				library{"Android"},
374				usedBy{"bin/bin1"},
375				firstParty{},
376				hr{},
377				library{"Device"},
378				usedBy{"bin/bin1"},
379				library{"External"},
380				usedBy{"bin/bin1"},
381				reciprocal{},
382			},
383			expectedDeps: []string{
384				"testdata/firstparty/FIRST_PARTY_LICENSE",
385				"testdata/reciprocal/RECIPROCAL_LICENSE",
386				"testdata/reciprocal/bin/bin1.meta_lic",
387				"testdata/reciprocal/lib/liba.so.meta_lic",
388				"testdata/reciprocal/lib/libc.a.meta_lic",
389			},
390		},
391		{
392			condition: "reciprocal",
393			name:      "library",
394			roots:     []string{"lib/libd.so.meta_lic"},
395			expectedOut: []matcher{
396				hr{},
397				library{"External"},
398				usedBy{"lib/libd.so"},
399				notice{},
400			},
401			expectedDeps: []string{
402				"testdata/notice/NOTICE_LICENSE",
403				"testdata/reciprocal/lib/libd.so.meta_lic",
404			},
405		},
406		{
407			condition: "restricted",
408			name:      "apex",
409			roots:     []string{"highest.apex.meta_lic"},
410			expectedOut: []matcher{
411				hr{},
412				library{"Android"},
413				usedBy{"highest.apex"},
414				usedBy{"highest.apex/bin/bin1"},
415				usedBy{"highest.apex/bin/bin2"},
416				firstParty{},
417				hr{},
418				library{"Android"},
419				usedBy{"highest.apex/bin/bin2"},
420				usedBy{"highest.apex/lib/libb.so"},
421				library{"Device"},
422				usedBy{"highest.apex/bin/bin1"},
423				usedBy{"highest.apex/lib/liba.so"},
424				restricted{},
425				hr{},
426				library{"External"},
427				usedBy{"highest.apex/bin/bin1"},
428				reciprocal{},
429			},
430			expectedDeps: []string{
431				"testdata/firstparty/FIRST_PARTY_LICENSE",
432				"testdata/reciprocal/RECIPROCAL_LICENSE",
433				"testdata/restricted/RESTRICTED_LICENSE",
434				"testdata/restricted/bin/bin1.meta_lic",
435				"testdata/restricted/bin/bin2.meta_lic",
436				"testdata/restricted/highest.apex.meta_lic",
437				"testdata/restricted/lib/liba.so.meta_lic",
438				"testdata/restricted/lib/libb.so.meta_lic",
439				"testdata/restricted/lib/libc.a.meta_lic",
440				"testdata/restricted/lib/libd.so.meta_lic",
441			},
442		},
443		{
444			condition: "restricted",
445			name:      "container",
446			roots:     []string{"container.zip.meta_lic"},
447			expectedOut: []matcher{
448				hr{},
449				library{"Android"},
450				usedBy{"container.zip"},
451				usedBy{"container.zip/bin1"},
452				usedBy{"container.zip/bin2"},
453				firstParty{},
454				hr{},
455				library{"Android"},
456				usedBy{"container.zip/bin2"},
457				usedBy{"container.zip/libb.so"},
458				library{"Device"},
459				usedBy{"container.zip/bin1"},
460				usedBy{"container.zip/liba.so"},
461				restricted{},
462				hr{},
463				library{"External"},
464				usedBy{"container.zip/bin1"},
465				reciprocal{},
466			},
467			expectedDeps: []string{
468				"testdata/firstparty/FIRST_PARTY_LICENSE",
469				"testdata/reciprocal/RECIPROCAL_LICENSE",
470				"testdata/restricted/RESTRICTED_LICENSE",
471				"testdata/restricted/bin/bin1.meta_lic",
472				"testdata/restricted/bin/bin2.meta_lic",
473				"testdata/restricted/container.zip.meta_lic",
474				"testdata/restricted/lib/liba.so.meta_lic",
475				"testdata/restricted/lib/libb.so.meta_lic",
476				"testdata/restricted/lib/libc.a.meta_lic",
477				"testdata/restricted/lib/libd.so.meta_lic",
478			},
479		},
480		{
481			condition: "restricted",
482			name:      "application",
483			roots:     []string{"application.meta_lic"},
484			expectedOut: []matcher{
485				hr{},
486				library{"Android"},
487				usedBy{"application"},
488				firstParty{},
489				hr{},
490				library{"Device"},
491				usedBy{"application"},
492				restricted{},
493			},
494			expectedDeps: []string{
495				"testdata/firstparty/FIRST_PARTY_LICENSE",
496				"testdata/restricted/RESTRICTED_LICENSE",
497				"testdata/restricted/application.meta_lic",
498				"testdata/restricted/bin/bin3.meta_lic",
499				"testdata/restricted/lib/liba.so.meta_lic",
500				"testdata/restricted/lib/libb.so.meta_lic",
501			},
502		},
503		{
504			condition: "restricted",
505			name:      "binary",
506			roots:     []string{"bin/bin1.meta_lic"},
507			expectedOut: []matcher{
508				hr{},
509				library{"Android"},
510				usedBy{"bin/bin1"},
511				firstParty{},
512				hr{},
513				library{"Device"},
514				usedBy{"bin/bin1"},
515				restricted{},
516				hr{},
517				library{"External"},
518				usedBy{"bin/bin1"},
519				reciprocal{},
520			},
521			expectedDeps: []string{
522				"testdata/firstparty/FIRST_PARTY_LICENSE",
523				"testdata/reciprocal/RECIPROCAL_LICENSE",
524				"testdata/restricted/RESTRICTED_LICENSE",
525				"testdata/restricted/bin/bin1.meta_lic",
526				"testdata/restricted/lib/liba.so.meta_lic",
527				"testdata/restricted/lib/libc.a.meta_lic",
528			},
529		},
530		{
531			condition: "restricted",
532			name:      "library",
533			roots:     []string{"lib/libd.so.meta_lic"},
534			expectedOut: []matcher{
535				hr{},
536				library{"External"},
537				usedBy{"lib/libd.so"},
538				notice{},
539			},
540			expectedDeps: []string{
541				"testdata/notice/NOTICE_LICENSE",
542				"testdata/restricted/lib/libd.so.meta_lic",
543			},
544		},
545		{
546			condition: "proprietary",
547			name:      "apex",
548			roots:     []string{"highest.apex.meta_lic"},
549			expectedOut: []matcher{
550				hr{},
551				library{"Android"},
552				usedBy{"highest.apex/bin/bin2"},
553				usedBy{"highest.apex/lib/libb.so"},
554				restricted{},
555				hr{},
556				library{"Android"},
557				usedBy{"highest.apex"},
558				usedBy{"highest.apex/bin/bin1"},
559				firstParty{},
560				hr{},
561				library{"Android"},
562				usedBy{"highest.apex/bin/bin2"},
563				library{"Device"},
564				usedBy{"highest.apex/bin/bin1"},
565				usedBy{"highest.apex/lib/liba.so"},
566				library{"External"},
567				usedBy{"highest.apex/bin/bin1"},
568				proprietary{},
569			},
570			expectedDeps: []string{
571				"testdata/firstparty/FIRST_PARTY_LICENSE",
572				"testdata/proprietary/PROPRIETARY_LICENSE",
573				"testdata/proprietary/bin/bin1.meta_lic",
574				"testdata/proprietary/bin/bin2.meta_lic",
575				"testdata/proprietary/highest.apex.meta_lic",
576				"testdata/proprietary/lib/liba.so.meta_lic",
577				"testdata/proprietary/lib/libb.so.meta_lic",
578				"testdata/proprietary/lib/libc.a.meta_lic",
579				"testdata/proprietary/lib/libd.so.meta_lic",
580				"testdata/restricted/RESTRICTED_LICENSE",
581			},
582		},
583		{
584			condition: "proprietary",
585			name:      "container",
586			roots:     []string{"container.zip.meta_lic"},
587			expectedOut: []matcher{
588				hr{},
589				library{"Android"},
590				usedBy{"container.zip/bin2"},
591				usedBy{"container.zip/libb.so"},
592				restricted{},
593				hr{},
594				library{"Android"},
595				usedBy{"container.zip"},
596				usedBy{"container.zip/bin1"},
597				firstParty{},
598				hr{},
599				library{"Android"},
600				usedBy{"container.zip/bin2"},
601				library{"Device"},
602				usedBy{"container.zip/bin1"},
603				usedBy{"container.zip/liba.so"},
604				library{"External"},
605				usedBy{"container.zip/bin1"},
606				proprietary{},
607			},
608			expectedDeps: []string{
609				"testdata/firstparty/FIRST_PARTY_LICENSE",
610				"testdata/proprietary/PROPRIETARY_LICENSE",
611				"testdata/proprietary/bin/bin1.meta_lic",
612				"testdata/proprietary/bin/bin2.meta_lic",
613				"testdata/proprietary/container.zip.meta_lic",
614				"testdata/proprietary/lib/liba.so.meta_lic",
615				"testdata/proprietary/lib/libb.so.meta_lic",
616				"testdata/proprietary/lib/libc.a.meta_lic",
617				"testdata/proprietary/lib/libd.so.meta_lic",
618				"testdata/restricted/RESTRICTED_LICENSE",
619			},
620		},
621		{
622			condition: "proprietary",
623			name:      "application",
624			roots:     []string{"application.meta_lic"},
625			expectedOut: []matcher{
626				hr{},
627				library{"Android"},
628				usedBy{"application"},
629				firstParty{},
630				hr{},
631				library{"Device"},
632				usedBy{"application"},
633				proprietary{},
634			},
635			expectedDeps: []string{
636				"testdata/firstparty/FIRST_PARTY_LICENSE",
637				"testdata/proprietary/PROPRIETARY_LICENSE",
638				"testdata/proprietary/application.meta_lic",
639				"testdata/proprietary/bin/bin3.meta_lic",
640				"testdata/proprietary/lib/liba.so.meta_lic",
641				"testdata/proprietary/lib/libb.so.meta_lic",
642			},
643		},
644		{
645			condition: "proprietary",
646			name:      "binary",
647			roots:     []string{"bin/bin1.meta_lic"},
648			expectedOut: []matcher{
649				hr{},
650				library{"Android"},
651				usedBy{"bin/bin1"},
652				firstParty{},
653				hr{},
654				library{"Device"},
655				usedBy{"bin/bin1"},
656				library{"External"},
657				usedBy{"bin/bin1"},
658				proprietary{},
659			},
660			expectedDeps: []string{
661				"testdata/firstparty/FIRST_PARTY_LICENSE",
662				"testdata/proprietary/PROPRIETARY_LICENSE",
663				"testdata/proprietary/bin/bin1.meta_lic",
664				"testdata/proprietary/lib/liba.so.meta_lic",
665				"testdata/proprietary/lib/libc.a.meta_lic",
666			},
667		},
668		{
669			condition: "proprietary",
670			name:      "library",
671			roots:     []string{"lib/libd.so.meta_lic"},
672			expectedOut: []matcher{
673				hr{},
674				library{"External"},
675				usedBy{"lib/libd.so"},
676				notice{},
677			},
678			expectedDeps: []string{
679				"testdata/notice/NOTICE_LICENSE",
680				"testdata/proprietary/lib/libd.so.meta_lic",
681			},
682		},
683	}
684	for _, tt := range tests {
685		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
686			stdout := &bytes.Buffer{}
687			stderr := &bytes.Buffer{}
688
689			rootFiles := make([]string, 0, len(tt.roots))
690			for _, r := range tt.roots {
691				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
692			}
693
694			var deps []string
695
696			ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), "", []string{tt.stripPrefix}, "", &deps}
697
698			err := textNotice(&ctx, rootFiles...)
699			if err != nil {
700				t.Fatalf("textnotice: error = %v, stderr = %v", err, stderr)
701				return
702			}
703			if stderr.Len() > 0 {
704				t.Errorf("textnotice: gotStderr = %v, want none", stderr)
705			}
706
707			t.Logf("got stdout: %s", stdout.String())
708
709			t.Logf("want stdout: %s", matcherList(tt.expectedOut).String())
710
711			out := bufio.NewScanner(stdout)
712			lineno := 0
713			for out.Scan() {
714				line := out.Text()
715				if strings.TrimLeft(line, " ") == "" {
716					continue
717				}
718				if len(tt.expectedOut) <= lineno {
719					t.Errorf("unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut))
720				} else if !tt.expectedOut[lineno].isMatch(line) {
721					t.Errorf("unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno].String())
722				}
723				lineno++
724			}
725			for ; lineno < len(tt.expectedOut); lineno++ {
726				t.Errorf("textnotice: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno].String())
727			}
728
729			t.Logf("got deps: %q", deps)
730
731			t.Logf("want deps: %q", tt.expectedDeps)
732
733			if g, w := deps, tt.expectedDeps; !reflect.DeepEqual(g, w) {
734				t.Errorf("unexpected deps, wanted:\n%s\ngot:\n%s\n",
735					strings.Join(w, "\n"), strings.Join(g, "\n"))
736			}
737		})
738	}
739}
740
741type matcher interface {
742	isMatch(line string) bool
743	String() string
744}
745
746type hr struct{}
747
748func (m hr) isMatch(line string) bool {
749	return horizontalRule.MatchString(line)
750}
751
752func (m hr) String() string {
753	return " ================================================== "
754}
755
756type library struct {
757	name string
758}
759
760func (m library) isMatch(line string) bool {
761	return strings.HasPrefix(line, m.name+" ")
762}
763
764func (m library) String() string {
765	return m.name + " used by:"
766}
767
768type usedBy struct {
769	name string
770}
771
772func (m usedBy) isMatch(line string) bool {
773	return len(line) > 0 && line[0] == ' ' && strings.HasPrefix(strings.TrimLeft(line, " "), "out/") && strings.HasSuffix(line, "/"+m.name)
774}
775
776func (m usedBy) String() string {
777	return "  out/.../" + m.name
778}
779
780type firstParty struct{}
781
782func (m firstParty) isMatch(line string) bool {
783	return strings.HasPrefix(strings.TrimLeft(line, " "), "&&&First Party License&&&")
784}
785
786func (m firstParty) String() string {
787	return "&&&First Party License&&&"
788}
789
790type notice struct{}
791
792func (m notice) isMatch(line string) bool {
793	return strings.HasPrefix(strings.TrimLeft(line, " "), "%%%Notice License%%%")
794}
795
796func (m notice) String() string {
797	return "%%%Notice License%%%"
798}
799
800type reciprocal struct{}
801
802func (m reciprocal) isMatch(line string) bool {
803	return strings.HasPrefix(strings.TrimLeft(line, " "), "$$$Reciprocal License$$$")
804}
805
806func (m reciprocal) String() string {
807	return "$$$Reciprocal License$$$"
808}
809
810type restricted struct{}
811
812func (m restricted) isMatch(line string) bool {
813	return strings.HasPrefix(strings.TrimLeft(line, " "), "###Restricted License###")
814}
815
816func (m restricted) String() string {
817	return "###Restricted License###"
818}
819
820type proprietary struct{}
821
822func (m proprietary) isMatch(line string) bool {
823	return strings.HasPrefix(strings.TrimLeft(line, " "), "@@@Proprietary License@@@")
824}
825
826func (m proprietary) String() string {
827	return "@@@Proprietary License@@@"
828}
829
830type matcherList []matcher
831
832func (l matcherList) String() string {
833	var sb strings.Builder
834	for _, m := range l {
835		s := m.String()
836		if s[:3] == s[len(s)-3:] {
837			fmt.Fprintln(&sb)
838		}
839		fmt.Fprintf(&sb, "%s\n", s)
840		if s[:3] == s[len(s)-3:] {
841			fmt.Fprintln(&sb)
842		}
843	}
844	return sb.String()
845}
846