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	"strings"
23	"testing"
24
25	"android/soong/tools/compliance"
26)
27
28func TestMain(m *testing.M) {
29	// Change into the parent directory before running the tests
30	// so they can find the testdata directory.
31	if err := os.Chdir(".."); err != nil {
32		fmt.Printf("failed to change to testdata directory: %s\n", err)
33		os.Exit(1)
34	}
35	os.Exit(m.Run())
36}
37
38func Test(t *testing.T) {
39	tests := []struct {
40		condition   string
41		name        string
42		outDir      string
43		roots       []string
44		stripPrefix string
45		expectedOut []string
46	}{
47		{
48			condition:   "firstparty",
49			name:        "apex",
50			roots:       []string{"highest.apex.meta_lic"},
51			stripPrefix: "out/target/product/fictional",
52			expectedOut: []string{
53				"/system/apex/highest.apex",
54				"/system/apex/highest.apex/bin/bin1",
55				"/system/apex/highest.apex/bin/bin2",
56				"/system/apex/highest.apex/lib/liba.so",
57				"/system/apex/highest.apex/lib/libb.so",
58			},
59		},
60		{
61			condition:   "firstparty",
62			name:        "container",
63			roots:       []string{"container.zip.meta_lic"},
64			stripPrefix: "out/target/product/fictional/data/",
65			expectedOut: []string{
66				"container.zip",
67				"container.zip/bin1",
68				"container.zip/bin2",
69				"container.zip/liba.so",
70				"container.zip/libb.so",
71			},
72		},
73		{
74			condition:   "firstparty",
75			name:        "application",
76			roots:       []string{"application.meta_lic"},
77			stripPrefix: "out/target/product/fictional/bin/",
78			expectedOut: []string{"application"},
79		},
80		{
81			condition:   "firstparty",
82			name:        "binary",
83			roots:       []string{"bin/bin1.meta_lic"},
84			stripPrefix: "out/target/product/fictional/system/",
85			expectedOut: []string{"bin/bin1"},
86		},
87		{
88			condition:   "firstparty",
89			name:        "library",
90			roots:       []string{"lib/libd.so.meta_lic"},
91			stripPrefix: "out/target/product/fictional/system/",
92			expectedOut: []string{"lib/libd.so"},
93		},
94		{
95			condition: "notice",
96			name:      "apex",
97			roots:     []string{"highest.apex.meta_lic"},
98			expectedOut: []string{
99				"out/target/product/fictional/system/apex/highest.apex",
100				"out/target/product/fictional/system/apex/highest.apex/bin/bin1",
101				"out/target/product/fictional/system/apex/highest.apex/bin/bin2",
102				"out/target/product/fictional/system/apex/highest.apex/lib/liba.so",
103				"out/target/product/fictional/system/apex/highest.apex/lib/libb.so",
104			},
105		},
106		{
107			condition: "notice",
108			name:      "container",
109			roots:     []string{"container.zip.meta_lic"},
110			expectedOut: []string{
111				"out/target/product/fictional/data/container.zip",
112				"out/target/product/fictional/data/container.zip/bin1",
113				"out/target/product/fictional/data/container.zip/bin2",
114				"out/target/product/fictional/data/container.zip/liba.so",
115				"out/target/product/fictional/data/container.zip/libb.so",
116			},
117		},
118		{
119			condition:   "notice",
120			name:        "application",
121			roots:       []string{"application.meta_lic"},
122			expectedOut: []string{"out/target/product/fictional/bin/application"},
123		},
124		{
125			condition:   "notice",
126			name:        "binary",
127			roots:       []string{"bin/bin1.meta_lic"},
128			expectedOut: []string{"out/target/product/fictional/system/bin/bin1"},
129		},
130		{
131			condition:   "notice",
132			name:        "library",
133			roots:       []string{"lib/libd.so.meta_lic"},
134			expectedOut: []string{"out/target/product/fictional/system/lib/libd.so"},
135		},
136		{
137			condition:   "reciprocal",
138			name:        "apex",
139			roots:       []string{"highest.apex.meta_lic"},
140			stripPrefix: "out/target/product/fictional/system/apex/",
141			expectedOut: []string{
142				"highest.apex",
143				"highest.apex/bin/bin1",
144				"highest.apex/bin/bin2",
145				"highest.apex/lib/liba.so",
146				"highest.apex/lib/libb.so",
147			},
148		},
149		{
150			condition:   "reciprocal",
151			name:        "container",
152			roots:       []string{"container.zip.meta_lic"},
153			stripPrefix: "out/target/product/fictional/data/",
154			expectedOut: []string{
155				"container.zip",
156				"container.zip/bin1",
157				"container.zip/bin2",
158				"container.zip/liba.so",
159				"container.zip/libb.so",
160			},
161		},
162		{
163			condition:   "reciprocal",
164			name:        "application",
165			roots:       []string{"application.meta_lic"},
166			stripPrefix: "out/target/product/fictional/bin/",
167			expectedOut: []string{"application"},
168		},
169		{
170			condition:   "reciprocal",
171			name:        "binary",
172			roots:       []string{"bin/bin1.meta_lic"},
173			stripPrefix: "out/target/product/fictional/system/",
174			expectedOut: []string{"bin/bin1"},
175		},
176		{
177			condition:   "reciprocal",
178			name:        "library",
179			roots:       []string{"lib/libd.so.meta_lic"},
180			stripPrefix: "out/target/product/fictional/system/",
181			expectedOut: []string{"lib/libd.so"},
182		},
183		{
184			condition:   "restricted",
185			name:        "apex",
186			roots:       []string{"highest.apex.meta_lic"},
187			stripPrefix: "out/target/product/fictional/system/apex/",
188			expectedOut: []string{
189				"highest.apex",
190				"highest.apex/bin/bin1",
191				"highest.apex/bin/bin2",
192				"highest.apex/lib/liba.so",
193				"highest.apex/lib/libb.so",
194			},
195		},
196		{
197			condition:   "restricted",
198			name:        "container",
199			roots:       []string{"container.zip.meta_lic"},
200			stripPrefix: "out/target/product/fictional/data/",
201			expectedOut: []string{
202				"container.zip",
203				"container.zip/bin1",
204				"container.zip/bin2",
205				"container.zip/liba.so",
206				"container.zip/libb.so",
207			},
208		},
209		{
210			condition:   "restricted",
211			name:        "application",
212			roots:       []string{"application.meta_lic"},
213			stripPrefix: "out/target/product/fictional/bin/",
214			expectedOut: []string{"application"},
215		},
216		{
217			condition:   "restricted",
218			name:        "binary",
219			roots:       []string{"bin/bin1.meta_lic"},
220			stripPrefix: "out/target/product/fictional/system/",
221			expectedOut: []string{"bin/bin1"},
222		},
223		{
224			condition:   "restricted",
225			name:        "library",
226			roots:       []string{"lib/libd.so.meta_lic"},
227			stripPrefix: "out/target/product/fictional/system/",
228			expectedOut: []string{"lib/libd.so"},
229		},
230		{
231			condition:   "proprietary",
232			name:        "apex",
233			roots:       []string{"highest.apex.meta_lic"},
234			stripPrefix: "out/target/product/fictional/system/apex/",
235			expectedOut: []string{
236				"highest.apex",
237				"highest.apex/bin/bin1",
238				"highest.apex/bin/bin2",
239				"highest.apex/lib/liba.so",
240				"highest.apex/lib/libb.so",
241			},
242		},
243		{
244			condition:   "proprietary",
245			name:        "container",
246			roots:       []string{"container.zip.meta_lic"},
247			stripPrefix: "out/target/product/fictional/data/",
248			expectedOut: []string{
249				"container.zip",
250				"container.zip/bin1",
251				"container.zip/bin2",
252				"container.zip/liba.so",
253				"container.zip/libb.so",
254			},
255		},
256		{
257			condition:   "proprietary",
258			name:        "application",
259			roots:       []string{"application.meta_lic"},
260			stripPrefix: "out/target/product/fictional/bin/",
261			expectedOut: []string{"application"},
262		},
263		{
264			condition:   "proprietary",
265			name:        "binary",
266			roots:       []string{"bin/bin1.meta_lic"},
267			stripPrefix: "out/target/product/fictional/system/",
268			expectedOut: []string{"bin/bin1"},
269		},
270		{
271			condition:   "proprietary",
272			name:        "library",
273			roots:       []string{"lib/libd.so.meta_lic"},
274			stripPrefix: "out/target/product/fictional/system/",
275			expectedOut: []string{"lib/libd.so"},
276		},
277	}
278	for _, tt := range tests {
279		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
280			stdout := &bytes.Buffer{}
281			stderr := &bytes.Buffer{}
282
283			rootFiles := make([]string, 0, len(tt.roots))
284			for _, r := range tt.roots {
285				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
286			}
287
288			ctx := context{stdout, stderr, compliance.GetFS(tt.outDir), []string{tt.stripPrefix}}
289
290			err := billOfMaterials(&ctx, rootFiles...)
291			if err != nil {
292				t.Fatalf("bom: error = %v, stderr = %v", err, stderr)
293				return
294			}
295			if stderr.Len() > 0 {
296				t.Errorf("bom: gotStderr = %v, want none", stderr)
297			}
298
299			t.Logf("got stdout: %s", stdout.String())
300
301			t.Logf("want stdout: %s", strings.Join(tt.expectedOut, "\n"))
302
303			out := bufio.NewScanner(stdout)
304			lineno := 0
305			for out.Scan() {
306				line := out.Text()
307				if strings.TrimLeft(line, " ") == "" {
308					continue
309				}
310				if len(tt.expectedOut) <= lineno {
311					t.Errorf("bom: unexpected output at line %d: got %q, want nothing (wanted %d lines)", lineno+1, line, len(tt.expectedOut))
312				} else if tt.expectedOut[lineno] != line {
313					t.Errorf("bom: unexpected output at line %d: got %q, want %q", lineno+1, line, tt.expectedOut[lineno])
314				}
315				lineno++
316			}
317			for ; lineno < len(tt.expectedOut); lineno++ {
318				t.Errorf("bom: missing output line %d: ended early, want %q", lineno+1, tt.expectedOut[lineno])
319			}
320		})
321	}
322}
323