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