• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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{"testdata/firstparty/FIRST_PARTY_LICENSE"},
69		},
70		{
71			condition: "firstparty",
72			name:      "container",
73			roots:     []string{"container.zip.meta_lic"},
74			expectedOut: []matcher{
75				hr{},
76				library{"Android"},
77				usedBy{"container.zip"},
78				usedBy{"container.zip/bin1"},
79				usedBy{"container.zip/bin2"},
80				usedBy{"container.zip/liba.so"},
81				usedBy{"container.zip/libb.so"},
82				firstParty{},
83			},
84			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
85		},
86		{
87			condition: "firstparty",
88			name:      "application",
89			roots:     []string{"application.meta_lic"},
90			expectedOut: []matcher{
91				hr{},
92				library{"Android"},
93				usedBy{"application"},
94				firstParty{},
95			},
96			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
97		},
98		{
99			condition: "firstparty",
100			name:      "binary",
101			roots:     []string{"bin/bin1.meta_lic"},
102			expectedOut: []matcher{
103				hr{},
104				library{"Android"},
105				usedBy{"bin/bin1"},
106				firstParty{},
107			},
108			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
109		},
110		{
111			condition: "firstparty",
112			name:      "library",
113			roots:     []string{"lib/libd.so.meta_lic"},
114			expectedOut: []matcher{
115				hr{},
116				library{"Android"},
117				usedBy{"lib/libd.so"},
118				firstParty{},
119			},
120			expectedDeps: []string{"testdata/firstparty/FIRST_PARTY_LICENSE"},
121		},
122		{
123			condition: "notice",
124			name:      "apex",
125			roots:     []string{"highest.apex.meta_lic"},
126			expectedOut: []matcher{
127				hr{},
128				library{"Android"},
129				usedBy{"highest.apex"},
130				usedBy{"highest.apex/bin/bin1"},
131				usedBy{"highest.apex/bin/bin2"},
132				usedBy{"highest.apex/lib/libb.so"},
133				firstParty{},
134				hr{},
135				library{"Device"},
136				usedBy{"highest.apex/bin/bin1"},
137				usedBy{"highest.apex/lib/liba.so"},
138				library{"External"},
139				usedBy{"highest.apex/bin/bin1"},
140				notice{},
141			},
142			expectedDeps: []string{
143				"testdata/firstparty/FIRST_PARTY_LICENSE",
144				"testdata/notice/NOTICE_LICENSE",
145			},
146		},
147		{
148			condition: "notice",
149			name:      "container",
150			roots:     []string{"container.zip.meta_lic"},
151			expectedOut: []matcher{
152				hr{},
153				library{"Android"},
154				usedBy{"container.zip"},
155				usedBy{"container.zip/bin1"},
156				usedBy{"container.zip/bin2"},
157				usedBy{"container.zip/libb.so"},
158				firstParty{},
159				hr{},
160				library{"Device"},
161				usedBy{"container.zip/bin1"},
162				usedBy{"container.zip/liba.so"},
163				library{"External"},
164				usedBy{"container.zip/bin1"},
165				notice{},
166			},
167			expectedDeps: []string{
168				"testdata/firstparty/FIRST_PARTY_LICENSE",
169				"testdata/notice/NOTICE_LICENSE",
170			},
171		},
172		{
173			condition: "notice",
174			name:      "application",
175			roots:     []string{"application.meta_lic"},
176			expectedOut: []matcher{
177				hr{},
178				library{"Android"},
179				usedBy{"application"},
180				firstParty{},
181				hr{},
182				library{"Device"},
183				usedBy{"application"},
184				notice{},
185			},
186			expectedDeps: []string{
187				"testdata/firstparty/FIRST_PARTY_LICENSE",
188				"testdata/notice/NOTICE_LICENSE",
189			},
190		},
191		{
192			condition: "notice",
193			name:      "binary",
194			roots:     []string{"bin/bin1.meta_lic"},
195			expectedOut: []matcher{
196				hr{},
197				library{"Android"},
198				usedBy{"bin/bin1"},
199				firstParty{},
200				hr{},
201				library{"Device"},
202				usedBy{"bin/bin1"},
203				library{"External"},
204				usedBy{"bin/bin1"},
205				notice{},
206			},
207			expectedDeps: []string{
208				"testdata/firstparty/FIRST_PARTY_LICENSE",
209				"testdata/notice/NOTICE_LICENSE",
210			},
211		},
212		{
213			condition: "notice",
214			name:      "library",
215			roots:     []string{"lib/libd.so.meta_lic"},
216			expectedOut: []matcher{
217				hr{},
218				library{"External"},
219				usedBy{"lib/libd.so"},
220				notice{},
221			},
222			expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
223		},
224		{
225			condition: "reciprocal",
226			name:      "apex",
227			roots:     []string{"highest.apex.meta_lic"},
228			expectedOut: []matcher{
229				hr{},
230				library{"Android"},
231				usedBy{"highest.apex"},
232				usedBy{"highest.apex/bin/bin1"},
233				usedBy{"highest.apex/bin/bin2"},
234				usedBy{"highest.apex/lib/libb.so"},
235				firstParty{},
236				hr{},
237				library{"Device"},
238				usedBy{"highest.apex/bin/bin1"},
239				usedBy{"highest.apex/lib/liba.so"},
240				library{"External"},
241				usedBy{"highest.apex/bin/bin1"},
242				reciprocal{},
243			},
244			expectedDeps: []string{
245				"testdata/firstparty/FIRST_PARTY_LICENSE",
246				"testdata/reciprocal/RECIPROCAL_LICENSE",
247			},
248		},
249		{
250			condition: "reciprocal",
251			name:      "container",
252			roots:     []string{"container.zip.meta_lic"},
253			expectedOut: []matcher{
254				hr{},
255				library{"Android"},
256				usedBy{"container.zip"},
257				usedBy{"container.zip/bin1"},
258				usedBy{"container.zip/bin2"},
259				usedBy{"container.zip/libb.so"},
260				firstParty{},
261				hr{},
262				library{"Device"},
263				usedBy{"container.zip/bin1"},
264				usedBy{"container.zip/liba.so"},
265				library{"External"},
266				usedBy{"container.zip/bin1"},
267				reciprocal{},
268			},
269			expectedDeps: []string{
270				"testdata/firstparty/FIRST_PARTY_LICENSE",
271				"testdata/reciprocal/RECIPROCAL_LICENSE",
272			},
273		},
274		{
275			condition: "reciprocal",
276			name:      "application",
277			roots:     []string{"application.meta_lic"},
278			expectedOut: []matcher{
279				hr{},
280				library{"Android"},
281				usedBy{"application"},
282				firstParty{},
283				hr{},
284				library{"Device"},
285				usedBy{"application"},
286				reciprocal{},
287			},
288			expectedDeps: []string{
289				"testdata/firstparty/FIRST_PARTY_LICENSE",
290				"testdata/reciprocal/RECIPROCAL_LICENSE",
291			},
292		},
293		{
294			condition: "reciprocal",
295			name:      "binary",
296			roots:     []string{"bin/bin1.meta_lic"},
297			expectedOut: []matcher{
298				hr{},
299				library{"Android"},
300				usedBy{"bin/bin1"},
301				firstParty{},
302				hr{},
303				library{"Device"},
304				usedBy{"bin/bin1"},
305				library{"External"},
306				usedBy{"bin/bin1"},
307				reciprocal{},
308			},
309			expectedDeps: []string{
310				"testdata/firstparty/FIRST_PARTY_LICENSE",
311				"testdata/reciprocal/RECIPROCAL_LICENSE",
312			},
313		},
314		{
315			condition: "reciprocal",
316			name:      "library",
317			roots:     []string{"lib/libd.so.meta_lic"},
318			expectedOut: []matcher{
319				hr{},
320				library{"External"},
321				usedBy{"lib/libd.so"},
322				notice{},
323			},
324			expectedDeps: []string{
325				"testdata/notice/NOTICE_LICENSE",
326			},
327		},
328		{
329			condition: "restricted",
330			name:      "apex",
331			roots:     []string{"highest.apex.meta_lic"},
332			expectedOut: []matcher{
333				hr{},
334				library{"Android"},
335				usedBy{"highest.apex"},
336				usedBy{"highest.apex/bin/bin1"},
337				usedBy{"highest.apex/bin/bin2"},
338				firstParty{},
339				hr{},
340				library{"Android"},
341				usedBy{"highest.apex/bin/bin2"},
342				usedBy{"highest.apex/lib/libb.so"},
343				library{"Device"},
344				usedBy{"highest.apex/bin/bin1"},
345				usedBy{"highest.apex/lib/liba.so"},
346				restricted{},
347				hr{},
348				library{"External"},
349				usedBy{"highest.apex/bin/bin1"},
350				reciprocal{},
351			},
352			expectedDeps: []string{
353				"testdata/firstparty/FIRST_PARTY_LICENSE",
354				"testdata/reciprocal/RECIPROCAL_LICENSE",
355				"testdata/restricted/RESTRICTED_LICENSE",
356			},
357		},
358		{
359			condition: "restricted",
360			name:      "container",
361			roots:     []string{"container.zip.meta_lic"},
362			expectedOut: []matcher{
363				hr{},
364				library{"Android"},
365				usedBy{"container.zip"},
366				usedBy{"container.zip/bin1"},
367				usedBy{"container.zip/bin2"},
368				firstParty{},
369				hr{},
370				library{"Android"},
371				usedBy{"container.zip/bin2"},
372				usedBy{"container.zip/libb.so"},
373				library{"Device"},
374				usedBy{"container.zip/bin1"},
375				usedBy{"container.zip/liba.so"},
376				restricted{},
377				hr{},
378				library{"External"},
379				usedBy{"container.zip/bin1"},
380				reciprocal{},
381			},
382			expectedDeps: []string{
383				"testdata/firstparty/FIRST_PARTY_LICENSE",
384				"testdata/reciprocal/RECIPROCAL_LICENSE",
385				"testdata/restricted/RESTRICTED_LICENSE",
386			},
387		},
388		{
389			condition: "restricted",
390			name:      "application",
391			roots:     []string{"application.meta_lic"},
392			expectedOut: []matcher{
393				hr{},
394				library{"Android"},
395				usedBy{"application"},
396				firstParty{},
397				hr{},
398				library{"Device"},
399				usedBy{"application"},
400				restricted{},
401			},
402			expectedDeps: []string{
403				"testdata/firstparty/FIRST_PARTY_LICENSE",
404				"testdata/restricted/RESTRICTED_LICENSE",
405			},
406		},
407		{
408			condition: "restricted",
409			name:      "binary",
410			roots:     []string{"bin/bin1.meta_lic"},
411			expectedOut: []matcher{
412				hr{},
413				library{"Android"},
414				usedBy{"bin/bin1"},
415				firstParty{},
416				hr{},
417				library{"Device"},
418				usedBy{"bin/bin1"},
419				restricted{},
420				hr{},
421				library{"External"},
422				usedBy{"bin/bin1"},
423				reciprocal{},
424			},
425			expectedDeps: []string{
426				"testdata/firstparty/FIRST_PARTY_LICENSE",
427				"testdata/reciprocal/RECIPROCAL_LICENSE",
428				"testdata/restricted/RESTRICTED_LICENSE",
429			},
430		},
431		{
432			condition: "restricted",
433			name:      "library",
434			roots:     []string{"lib/libd.so.meta_lic"},
435			expectedOut: []matcher{
436				hr{},
437				library{"External"},
438				usedBy{"lib/libd.so"},
439				notice{},
440			},
441			expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
442		},
443		{
444			condition: "proprietary",
445			name:      "apex",
446			roots:     []string{"highest.apex.meta_lic"},
447			expectedOut: []matcher{
448				hr{},
449				library{"Android"},
450				usedBy{"highest.apex/bin/bin2"},
451				usedBy{"highest.apex/lib/libb.so"},
452				restricted{},
453				hr{},
454				library{"Android"},
455				usedBy{"highest.apex"},
456				usedBy{"highest.apex/bin/bin1"},
457				firstParty{},
458				hr{},
459				library{"Android"},
460				usedBy{"highest.apex/bin/bin2"},
461				library{"Device"},
462				usedBy{"highest.apex/bin/bin1"},
463				usedBy{"highest.apex/lib/liba.so"},
464				library{"External"},
465				usedBy{"highest.apex/bin/bin1"},
466				proprietary{},
467			},
468			expectedDeps: []string{
469				"testdata/firstparty/FIRST_PARTY_LICENSE",
470				"testdata/proprietary/PROPRIETARY_LICENSE",
471				"testdata/restricted/RESTRICTED_LICENSE",
472			},
473		},
474		{
475			condition: "proprietary",
476			name:      "container",
477			roots:     []string{"container.zip.meta_lic"},
478			expectedOut: []matcher{
479				hr{},
480				library{"Android"},
481				usedBy{"container.zip/bin2"},
482				usedBy{"container.zip/libb.so"},
483				restricted{},
484				hr{},
485				library{"Android"},
486				usedBy{"container.zip"},
487				usedBy{"container.zip/bin1"},
488				firstParty{},
489				hr{},
490				library{"Android"},
491				usedBy{"container.zip/bin2"},
492				library{"Device"},
493				usedBy{"container.zip/bin1"},
494				usedBy{"container.zip/liba.so"},
495				library{"External"},
496				usedBy{"container.zip/bin1"},
497				proprietary{},
498			},
499			expectedDeps: []string{
500				"testdata/firstparty/FIRST_PARTY_LICENSE",
501				"testdata/proprietary/PROPRIETARY_LICENSE",
502				"testdata/restricted/RESTRICTED_LICENSE",
503			},
504		},
505		{
506			condition: "proprietary",
507			name:      "application",
508			roots:     []string{"application.meta_lic"},
509			expectedOut: []matcher{
510				hr{},
511				library{"Android"},
512				usedBy{"application"},
513				firstParty{},
514				hr{},
515				library{"Device"},
516				usedBy{"application"},
517				proprietary{},
518			},
519			expectedDeps: []string{
520				"testdata/firstparty/FIRST_PARTY_LICENSE",
521				"testdata/proprietary/PROPRIETARY_LICENSE",
522			},
523		},
524		{
525			condition: "proprietary",
526			name:      "binary",
527			roots:     []string{"bin/bin1.meta_lic"},
528			expectedOut: []matcher{
529				hr{},
530				library{"Android"},
531				usedBy{"bin/bin1"},
532				firstParty{},
533				hr{},
534				library{"Device"},
535				usedBy{"bin/bin1"},
536				library{"External"},
537				usedBy{"bin/bin1"},
538				proprietary{},
539			},
540			expectedDeps: []string{
541				"testdata/firstparty/FIRST_PARTY_LICENSE",
542				"testdata/proprietary/PROPRIETARY_LICENSE",
543			},
544		},
545		{
546			condition: "proprietary",
547			name:      "library",
548			roots:     []string{"lib/libd.so.meta_lic"},
549			expectedOut: []matcher{
550				hr{},
551				library{"External"},
552				usedBy{"lib/libd.so"},
553				notice{},
554			},
555			expectedDeps: []string{"testdata/notice/NOTICE_LICENSE"},
556		},
557	}
558	for _, tt := range tests {
559		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
560			stdout := &bytes.Buffer{}
561			stderr := &bytes.Buffer{}
562
563			rootFiles := make([]string, 0, len(tt.roots))
564			for _, r := range tt.roots {
565				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
566			}
567
568			var deps []string
569
570			ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), "", []string{tt.stripPrefix}, "", &deps}
571
572			err := textNotice(&ctx, rootFiles...)
573			if err != nil {
574				t.Fatalf("textnotice: error = %v, stderr = %v", err, stderr)
575				return
576			}
577			if stderr.Len() > 0 {
578				t.Errorf("textnotice: gotStderr = %v, want none", stderr)
579			}
580
581			t.Logf("got stdout: %s", stdout.String())
582
583			t.Logf("want stdout: %s", matcherList(tt.expectedOut).String())
584
585			out := bufio.NewScanner(stdout)
586			lineno := 0
587			for out.Scan() {
588				line := out.Text()
589				if strings.TrimLeft(line, " ") == "" {
590					continue
591				}
592				if len(tt.expectedOut) <= lineno {
593					t.Errorf("unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut))
594				} else if !tt.expectedOut[lineno].isMatch(line) {
595					t.Errorf("unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno].String())
596				}
597				lineno++
598			}
599			for ; lineno < len(tt.expectedOut); lineno++ {
600				t.Errorf("textnotice: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno].String())
601			}
602
603			t.Logf("got deps: %q", deps)
604
605			t.Logf("want deps: %q", tt.expectedDeps)
606
607			if g, w := deps, tt.expectedDeps; !reflect.DeepEqual(g, w) {
608				t.Errorf("unexpected deps, wanted:\n%s\ngot:\n%s\n",
609					strings.Join(w, "\n"), strings.Join(g, "\n"))
610			}
611		})
612	}
613}
614
615type matcher interface {
616	isMatch(line string) bool
617	String() string
618}
619
620type hr struct{}
621
622func (m hr) isMatch(line string) bool {
623	return horizontalRule.MatchString(line)
624}
625
626func (m hr) String() string {
627	return " ================================================== "
628}
629
630type library struct {
631	name string
632}
633
634func (m library) isMatch(line string) bool {
635	return strings.HasPrefix(line, m.name+" ")
636}
637
638func (m library) String() string {
639	return m.name + " used by:"
640}
641
642type usedBy struct {
643	name string
644}
645
646func (m usedBy) isMatch(line string) bool {
647	return len(line) > 0 && line[0] == ' ' && strings.HasPrefix(strings.TrimLeft(line, " "), "out/") && strings.HasSuffix(line, "/"+m.name)
648}
649
650func (m usedBy) String() string {
651	return "  out/.../" + m.name
652}
653
654type firstParty struct{}
655
656func (m firstParty) isMatch(line string) bool {
657	return strings.HasPrefix(strings.TrimLeft(line, " "), "&&&First Party License&&&")
658}
659
660func (m firstParty) String() string {
661	return "&&&First Party License&&&"
662}
663
664type notice struct{}
665
666func (m notice) isMatch(line string) bool {
667	return strings.HasPrefix(strings.TrimLeft(line, " "), "%%%Notice License%%%")
668}
669
670func (m notice) String() string {
671	return "%%%Notice License%%%"
672}
673
674type reciprocal struct{}
675
676func (m reciprocal) isMatch(line string) bool {
677	return strings.HasPrefix(strings.TrimLeft(line, " "), "$$$Reciprocal License$$$")
678}
679
680func (m reciprocal) String() string {
681	return "$$$Reciprocal License$$$"
682}
683
684type restricted struct{}
685
686func (m restricted) isMatch(line string) bool {
687	return strings.HasPrefix(strings.TrimLeft(line, " "), "###Restricted License###")
688}
689
690func (m restricted) String() string {
691	return "###Restricted License###"
692}
693
694type proprietary struct{}
695
696func (m proprietary) isMatch(line string) bool {
697	return strings.HasPrefix(strings.TrimLeft(line, " "), "@@@Proprietary License@@@")
698}
699
700func (m proprietary) String() string {
701	return "@@@Proprietary License@@@"
702}
703
704type matcherList []matcher
705
706func (l matcherList) String() string {
707	var sb strings.Builder
708	for _, m := range l {
709		s := m.String()
710		if s[:3] == s[len(s)-3:] {
711			fmt.Fprintln(&sb)
712		}
713		fmt.Fprintf(&sb, "%s\n", s)
714		if s[:3] == s[len(s)-3:] {
715			fmt.Fprintln(&sb)
716		}
717	}
718	return sb.String()
719}
720