• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 Google Inc. All rights reserved.
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 android
16
17import (
18	"errors"
19	"fmt"
20	"reflect"
21	"strconv"
22	"strings"
23	"testing"
24
25	"github.com/google/blueprint/proptools"
26)
27
28type strsTestCase struct {
29	in  []string
30	out string
31	err []error
32}
33
34var commonValidatePathTestCases = []strsTestCase{
35	{
36		in:  []string{""},
37		out: "",
38	},
39	{
40		in:  []string{"a/b"},
41		out: "a/b",
42	},
43	{
44		in:  []string{"a/b", "c"},
45		out: "a/b/c",
46	},
47	{
48		in:  []string{"a/.."},
49		out: ".",
50	},
51	{
52		in:  []string{"."},
53		out: ".",
54	},
55	{
56		in:  []string{".."},
57		out: "",
58		err: []error{errors.New("Path is outside directory: ..")},
59	},
60	{
61		in:  []string{"../a"},
62		out: "",
63		err: []error{errors.New("Path is outside directory: ../a")},
64	},
65	{
66		in:  []string{"b/../../a"},
67		out: "",
68		err: []error{errors.New("Path is outside directory: ../a")},
69	},
70	{
71		in:  []string{"/a"},
72		out: "",
73		err: []error{errors.New("Path is outside directory: /a")},
74	},
75	{
76		in:  []string{"a", "../b"},
77		out: "",
78		err: []error{errors.New("Path is outside directory: ../b")},
79	},
80	{
81		in:  []string{"a", "b/../../c"},
82		out: "",
83		err: []error{errors.New("Path is outside directory: ../c")},
84	},
85	{
86		in:  []string{"a", "./.."},
87		out: "",
88		err: []error{errors.New("Path is outside directory: ..")},
89	},
90}
91
92var validateSafePathTestCases = append(commonValidatePathTestCases, []strsTestCase{
93	{
94		in:  []string{"$host/../$a"},
95		out: "$a",
96	},
97}...)
98
99var validatePathTestCases = append(commonValidatePathTestCases, []strsTestCase{
100	{
101		in:  []string{"$host/../$a"},
102		out: "",
103		err: []error{errors.New("Path contains invalid character($): $host/../$a")},
104	},
105	{
106		in:  []string{"$host/.."},
107		out: "",
108		err: []error{errors.New("Path contains invalid character($): $host/..")},
109	},
110}...)
111
112func TestValidateSafePath(t *testing.T) {
113	for _, testCase := range validateSafePathTestCases {
114		t.Run(strings.Join(testCase.in, ","), func(t *testing.T) {
115			ctx := &configErrorWrapper{}
116			out, err := validateSafePath(testCase.in...)
117			if err != nil {
118				reportPathError(ctx, err)
119			}
120			check(t, "validateSafePath", p(testCase.in), out, ctx.errors, testCase.out, testCase.err)
121		})
122	}
123}
124
125func TestValidatePath(t *testing.T) {
126	for _, testCase := range validatePathTestCases {
127		t.Run(strings.Join(testCase.in, ","), func(t *testing.T) {
128			ctx := &configErrorWrapper{}
129			out, err := validatePath(testCase.in...)
130			if err != nil {
131				reportPathError(ctx, err)
132			}
133			check(t, "validatePath", p(testCase.in), out, ctx.errors, testCase.out, testCase.err)
134		})
135	}
136}
137
138func TestOptionalPath(t *testing.T) {
139	var path OptionalPath
140	checkInvalidOptionalPath(t, path)
141
142	path = OptionalPathForPath(nil)
143	checkInvalidOptionalPath(t, path)
144
145	path = OptionalPathForPath(PathForTesting("path"))
146	checkValidOptionalPath(t, path, "path")
147}
148
149func checkInvalidOptionalPath(t *testing.T, path OptionalPath) {
150	t.Helper()
151	if path.Valid() {
152		t.Errorf("Uninitialized OptionalPath should not be valid")
153	}
154	if path.String() != "" {
155		t.Errorf("Uninitialized OptionalPath String() should return \"\", not %q", path.String())
156	}
157	paths := path.AsPaths()
158	if len(paths) != 0 {
159		t.Errorf("Uninitialized OptionalPath AsPaths() should return empty Paths, not %q", paths)
160	}
161	defer func() {
162		if r := recover(); r == nil {
163			t.Errorf("Expected a panic when calling Path() on an uninitialized OptionalPath")
164		}
165	}()
166	path.Path()
167}
168
169func checkValidOptionalPath(t *testing.T, path OptionalPath, expectedString string) {
170	t.Helper()
171	if !path.Valid() {
172		t.Errorf("Initialized OptionalPath should not be invalid")
173	}
174	if path.String() != expectedString {
175		t.Errorf("Initialized OptionalPath String() should return %q, not %q", expectedString, path.String())
176	}
177	paths := path.AsPaths()
178	if len(paths) != 1 {
179		t.Errorf("Initialized OptionalPath AsPaths() should return Paths with length 1, not %q", paths)
180	}
181	path.Path()
182}
183
184func check(t *testing.T, testType, testString string,
185	got interface{}, err []error,
186	expected interface{}, expectedErr []error) {
187	t.Helper()
188
189	printedTestCase := false
190	e := func(s string, expected, got interface{}) {
191		t.Helper()
192		if !printedTestCase {
193			t.Errorf("test case %s: %s", testType, testString)
194			printedTestCase = true
195		}
196		t.Errorf("incorrect %s", s)
197		t.Errorf("  expected: %s", p(expected))
198		t.Errorf("       got: %s", p(got))
199	}
200
201	if !reflect.DeepEqual(expectedErr, err) {
202		e("errors:", expectedErr, err)
203	}
204
205	if !reflect.DeepEqual(expected, got) {
206		e("output:", expected, got)
207	}
208}
209
210func p(in interface{}) string {
211	if v, ok := in.([]interface{}); ok {
212		s := make([]string, len(v))
213		for i := range v {
214			s[i] = fmt.Sprintf("%#v", v[i])
215		}
216		return "[" + strings.Join(s, ", ") + "]"
217	} else {
218		return fmt.Sprintf("%#v", in)
219	}
220}
221
222func pathTestConfig(buildDir string) Config {
223	return TestConfig(buildDir, nil, "", nil)
224}
225
226func TestPathForModuleInstall(t *testing.T) {
227	testConfig := pathTestConfig("")
228
229	hostTarget := Target{Os: Linux, Arch: Arch{ArchType: X86}}
230	deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
231
232	testCases := []struct {
233		name         string
234		ctx          *testModuleInstallPathContext
235		in           []string
236		out          string
237		partitionDir string
238	}{
239		{
240			name: "host binary",
241			ctx: &testModuleInstallPathContext{
242				baseModuleContext: baseModuleContext{
243					os:     hostTarget.Os,
244					target: hostTarget,
245				},
246			},
247			in:           []string{"bin", "my_test"},
248			out:          "host/linux-x86/bin/my_test",
249			partitionDir: "host/linux-x86",
250		},
251
252		{
253			name: "system binary",
254			ctx: &testModuleInstallPathContext{
255				baseModuleContext: baseModuleContext{
256					os:     deviceTarget.Os,
257					target: deviceTarget,
258				},
259			},
260			in:           []string{"bin", "my_test"},
261			out:          "target/product/test_device/system/bin/my_test",
262			partitionDir: "target/product/test_device/system",
263		},
264		{
265			name: "vendor binary",
266			ctx: &testModuleInstallPathContext{
267				baseModuleContext: baseModuleContext{
268					os:     deviceTarget.Os,
269					target: deviceTarget,
270					earlyModuleContext: earlyModuleContext{
271						kind: socSpecificModule,
272					},
273				},
274			},
275			in:           []string{"bin", "my_test"},
276			out:          "target/product/test_device/vendor/bin/my_test",
277			partitionDir: "target/product/test_device/vendor",
278		},
279		{
280			name: "odm binary",
281			ctx: &testModuleInstallPathContext{
282				baseModuleContext: baseModuleContext{
283					os:     deviceTarget.Os,
284					target: deviceTarget,
285					earlyModuleContext: earlyModuleContext{
286						kind: deviceSpecificModule,
287					},
288				},
289			},
290			in:           []string{"bin", "my_test"},
291			out:          "target/product/test_device/odm/bin/my_test",
292			partitionDir: "target/product/test_device/odm",
293		},
294		{
295			name: "product binary",
296			ctx: &testModuleInstallPathContext{
297				baseModuleContext: baseModuleContext{
298					os:     deviceTarget.Os,
299					target: deviceTarget,
300					earlyModuleContext: earlyModuleContext{
301						kind: productSpecificModule,
302					},
303				},
304			},
305			in:           []string{"bin", "my_test"},
306			out:          "target/product/test_device/product/bin/my_test",
307			partitionDir: "target/product/test_device/product",
308		},
309		{
310			name: "system_ext binary",
311			ctx: &testModuleInstallPathContext{
312				baseModuleContext: baseModuleContext{
313					os:     deviceTarget.Os,
314					target: deviceTarget,
315					earlyModuleContext: earlyModuleContext{
316						kind: systemExtSpecificModule,
317					},
318				},
319			},
320			in:           []string{"bin", "my_test"},
321			out:          "target/product/test_device/system_ext/bin/my_test",
322			partitionDir: "target/product/test_device/system_ext",
323		},
324		{
325			name: "root binary",
326			ctx: &testModuleInstallPathContext{
327				baseModuleContext: baseModuleContext{
328					os:     deviceTarget.Os,
329					target: deviceTarget,
330				},
331				inRoot: true,
332			},
333			in:           []string{"my_test"},
334			out:          "target/product/test_device/root/my_test",
335			partitionDir: "target/product/test_device/root",
336		},
337		{
338			name: "recovery binary",
339			ctx: &testModuleInstallPathContext{
340				baseModuleContext: baseModuleContext{
341					os:     deviceTarget.Os,
342					target: deviceTarget,
343				},
344				inRecovery: true,
345			},
346			in:           []string{"bin/my_test"},
347			out:          "target/product/test_device/recovery/root/system/bin/my_test",
348			partitionDir: "target/product/test_device/recovery/root/system",
349		},
350		{
351			name: "recovery root binary",
352			ctx: &testModuleInstallPathContext{
353				baseModuleContext: baseModuleContext{
354					os:     deviceTarget.Os,
355					target: deviceTarget,
356				},
357				inRecovery: true,
358				inRoot:     true,
359			},
360			in:           []string{"my_test"},
361			out:          "target/product/test_device/recovery/root/my_test",
362			partitionDir: "target/product/test_device/recovery/root",
363		},
364
365		{
366			name: "ramdisk binary",
367			ctx: &testModuleInstallPathContext{
368				baseModuleContext: baseModuleContext{
369					os:     deviceTarget.Os,
370					target: deviceTarget,
371				},
372				inRamdisk: true,
373			},
374			in:           []string{"my_test"},
375			out:          "target/product/test_device/ramdisk/system/my_test",
376			partitionDir: "target/product/test_device/ramdisk/system",
377		},
378		{
379			name: "ramdisk root binary",
380			ctx: &testModuleInstallPathContext{
381				baseModuleContext: baseModuleContext{
382					os:     deviceTarget.Os,
383					target: deviceTarget,
384				},
385				inRamdisk: true,
386				inRoot:    true,
387			},
388			in:           []string{"my_test"},
389			out:          "target/product/test_device/ramdisk/my_test",
390			partitionDir: "target/product/test_device/ramdisk",
391		},
392		{
393			name: "vendor_ramdisk binary",
394			ctx: &testModuleInstallPathContext{
395				baseModuleContext: baseModuleContext{
396					os:     deviceTarget.Os,
397					target: deviceTarget,
398				},
399				inVendorRamdisk: true,
400			},
401			in:           []string{"my_test"},
402			out:          "target/product/test_device/vendor_ramdisk/system/my_test",
403			partitionDir: "target/product/test_device/vendor_ramdisk/system",
404		},
405		{
406			name: "vendor_ramdisk root binary",
407			ctx: &testModuleInstallPathContext{
408				baseModuleContext: baseModuleContext{
409					os:     deviceTarget.Os,
410					target: deviceTarget,
411				},
412				inVendorRamdisk: true,
413				inRoot:          true,
414			},
415			in:           []string{"my_test"},
416			out:          "target/product/test_device/vendor_ramdisk/my_test",
417			partitionDir: "target/product/test_device/vendor_ramdisk",
418		},
419		{
420			name: "debug_ramdisk binary",
421			ctx: &testModuleInstallPathContext{
422				baseModuleContext: baseModuleContext{
423					os:     deviceTarget.Os,
424					target: deviceTarget,
425				},
426				inDebugRamdisk: true,
427			},
428			in:           []string{"my_test"},
429			out:          "target/product/test_device/debug_ramdisk/my_test",
430			partitionDir: "target/product/test_device/debug_ramdisk",
431		},
432		{
433			name: "system native test binary",
434			ctx: &testModuleInstallPathContext{
435				baseModuleContext: baseModuleContext{
436					os:     deviceTarget.Os,
437					target: deviceTarget,
438				},
439				inData: true,
440			},
441			in:           []string{"nativetest", "my_test"},
442			out:          "target/product/test_device/data/nativetest/my_test",
443			partitionDir: "target/product/test_device/data",
444		},
445		{
446			name: "vendor native test binary",
447			ctx: &testModuleInstallPathContext{
448				baseModuleContext: baseModuleContext{
449					os:     deviceTarget.Os,
450					target: deviceTarget,
451					earlyModuleContext: earlyModuleContext{
452						kind: socSpecificModule,
453					},
454				},
455				inData: true,
456			},
457			in:           []string{"nativetest", "my_test"},
458			out:          "target/product/test_device/data/nativetest/my_test",
459			partitionDir: "target/product/test_device/data",
460		},
461		{
462			name: "odm native test binary",
463			ctx: &testModuleInstallPathContext{
464				baseModuleContext: baseModuleContext{
465					os:     deviceTarget.Os,
466					target: deviceTarget,
467					earlyModuleContext: earlyModuleContext{
468						kind: deviceSpecificModule,
469					},
470				},
471				inData: true,
472			},
473			in:           []string{"nativetest", "my_test"},
474			out:          "target/product/test_device/data/nativetest/my_test",
475			partitionDir: "target/product/test_device/data",
476		},
477		{
478			name: "product native test binary",
479			ctx: &testModuleInstallPathContext{
480				baseModuleContext: baseModuleContext{
481					os:     deviceTarget.Os,
482					target: deviceTarget,
483					earlyModuleContext: earlyModuleContext{
484						kind: productSpecificModule,
485					},
486				},
487				inData: true,
488			},
489			in:           []string{"nativetest", "my_test"},
490			out:          "target/product/test_device/data/nativetest/my_test",
491			partitionDir: "target/product/test_device/data",
492		},
493
494		{
495			name: "system_ext native test binary",
496			ctx: &testModuleInstallPathContext{
497				baseModuleContext: baseModuleContext{
498					os:     deviceTarget.Os,
499					target: deviceTarget,
500					earlyModuleContext: earlyModuleContext{
501						kind: systemExtSpecificModule,
502					},
503				},
504				inData: true,
505			},
506			in:           []string{"nativetest", "my_test"},
507			out:          "target/product/test_device/data/nativetest/my_test",
508			partitionDir: "target/product/test_device/data",
509		},
510
511		{
512			name: "sanitized system binary",
513			ctx: &testModuleInstallPathContext{
514				baseModuleContext: baseModuleContext{
515					os:     deviceTarget.Os,
516					target: deviceTarget,
517				},
518				inSanitizerDir: true,
519			},
520			in:           []string{"bin", "my_test"},
521			out:          "target/product/test_device/data/asan/system/bin/my_test",
522			partitionDir: "target/product/test_device/data/asan/system",
523		},
524		{
525			name: "sanitized vendor binary",
526			ctx: &testModuleInstallPathContext{
527				baseModuleContext: baseModuleContext{
528					os:     deviceTarget.Os,
529					target: deviceTarget,
530					earlyModuleContext: earlyModuleContext{
531						kind: socSpecificModule,
532					},
533				},
534				inSanitizerDir: true,
535			},
536			in:           []string{"bin", "my_test"},
537			out:          "target/product/test_device/data/asan/vendor/bin/my_test",
538			partitionDir: "target/product/test_device/data/asan/vendor",
539		},
540		{
541			name: "sanitized odm binary",
542			ctx: &testModuleInstallPathContext{
543				baseModuleContext: baseModuleContext{
544					os:     deviceTarget.Os,
545					target: deviceTarget,
546					earlyModuleContext: earlyModuleContext{
547						kind: deviceSpecificModule,
548					},
549				},
550				inSanitizerDir: true,
551			},
552			in:           []string{"bin", "my_test"},
553			out:          "target/product/test_device/data/asan/odm/bin/my_test",
554			partitionDir: "target/product/test_device/data/asan/odm",
555		},
556		{
557			name: "sanitized product binary",
558			ctx: &testModuleInstallPathContext{
559				baseModuleContext: baseModuleContext{
560					os:     deviceTarget.Os,
561					target: deviceTarget,
562					earlyModuleContext: earlyModuleContext{
563						kind: productSpecificModule,
564					},
565				},
566				inSanitizerDir: true,
567			},
568			in:           []string{"bin", "my_test"},
569			out:          "target/product/test_device/data/asan/product/bin/my_test",
570			partitionDir: "target/product/test_device/data/asan/product",
571		},
572
573		{
574			name: "sanitized system_ext binary",
575			ctx: &testModuleInstallPathContext{
576				baseModuleContext: baseModuleContext{
577					os:     deviceTarget.Os,
578					target: deviceTarget,
579					earlyModuleContext: earlyModuleContext{
580						kind: systemExtSpecificModule,
581					},
582				},
583				inSanitizerDir: true,
584			},
585			in:           []string{"bin", "my_test"},
586			out:          "target/product/test_device/data/asan/system_ext/bin/my_test",
587			partitionDir: "target/product/test_device/data/asan/system_ext",
588		},
589
590		{
591			name: "sanitized system native test binary",
592			ctx: &testModuleInstallPathContext{
593				baseModuleContext: baseModuleContext{
594					os:     deviceTarget.Os,
595					target: deviceTarget,
596				},
597				inData:         true,
598				inSanitizerDir: true,
599			},
600			in:           []string{"nativetest", "my_test"},
601			out:          "target/product/test_device/data/asan/data/nativetest/my_test",
602			partitionDir: "target/product/test_device/data/asan/data",
603		},
604		{
605			name: "sanitized vendor native test binary",
606			ctx: &testModuleInstallPathContext{
607				baseModuleContext: baseModuleContext{
608					os:     deviceTarget.Os,
609					target: deviceTarget,
610					earlyModuleContext: earlyModuleContext{
611						kind: socSpecificModule,
612					},
613				},
614				inData:         true,
615				inSanitizerDir: true,
616			},
617			in:           []string{"nativetest", "my_test"},
618			out:          "target/product/test_device/data/asan/data/nativetest/my_test",
619			partitionDir: "target/product/test_device/data/asan/data",
620		},
621		{
622			name: "sanitized odm native test binary",
623			ctx: &testModuleInstallPathContext{
624				baseModuleContext: baseModuleContext{
625					os:     deviceTarget.Os,
626					target: deviceTarget,
627					earlyModuleContext: earlyModuleContext{
628						kind: deviceSpecificModule,
629					},
630				},
631				inData:         true,
632				inSanitizerDir: true,
633			},
634			in:           []string{"nativetest", "my_test"},
635			out:          "target/product/test_device/data/asan/data/nativetest/my_test",
636			partitionDir: "target/product/test_device/data/asan/data",
637		},
638		{
639			name: "sanitized product native test binary",
640			ctx: &testModuleInstallPathContext{
641				baseModuleContext: baseModuleContext{
642					os:     deviceTarget.Os,
643					target: deviceTarget,
644					earlyModuleContext: earlyModuleContext{
645						kind: productSpecificModule,
646					},
647				},
648				inData:         true,
649				inSanitizerDir: true,
650			},
651			in:           []string{"nativetest", "my_test"},
652			out:          "target/product/test_device/data/asan/data/nativetest/my_test",
653			partitionDir: "target/product/test_device/data/asan/data",
654		},
655		{
656			name: "sanitized system_ext native test binary",
657			ctx: &testModuleInstallPathContext{
658				baseModuleContext: baseModuleContext{
659					os:     deviceTarget.Os,
660					target: deviceTarget,
661					earlyModuleContext: earlyModuleContext{
662						kind: systemExtSpecificModule,
663					},
664				},
665				inData:         true,
666				inSanitizerDir: true,
667			},
668			in:           []string{"nativetest", "my_test"},
669			out:          "target/product/test_device/data/asan/data/nativetest/my_test",
670			partitionDir: "target/product/test_device/data/asan/data",
671		}, {
672			name: "device testcases",
673			ctx: &testModuleInstallPathContext{
674				baseModuleContext: baseModuleContext{
675					os:     deviceTarget.Os,
676					target: deviceTarget,
677				},
678				inTestcases: true,
679			},
680			in:           []string{"my_test", "my_test_bin"},
681			out:          "target/product/test_device/testcases/my_test/my_test_bin",
682			partitionDir: "target/product/test_device/testcases",
683		}, {
684			name: "host testcases",
685			ctx: &testModuleInstallPathContext{
686				baseModuleContext: baseModuleContext{
687					os:     hostTarget.Os,
688					target: hostTarget,
689				},
690				inTestcases: true,
691			},
692			in:           []string{"my_test", "my_test_bin"},
693			out:          "host/linux-x86/testcases/my_test/my_test_bin",
694			partitionDir: "host/linux-x86/testcases",
695		}, {
696			name: "forced host testcases",
697			ctx: &testModuleInstallPathContext{
698				baseModuleContext: baseModuleContext{
699					os:     deviceTarget.Os,
700					target: deviceTarget,
701				},
702				inTestcases: true,
703				forceOS:     &Linux,
704				forceArch:   &X86,
705			},
706			in:           []string{"my_test", "my_test_bin"},
707			out:          "host/linux-x86/testcases/my_test/my_test_bin",
708			partitionDir: "host/linux-x86/testcases",
709		},
710	}
711
712	for _, tc := range testCases {
713		t.Run(tc.name, func(t *testing.T) {
714			tc.ctx.baseModuleContext.config = testConfig
715			output := PathForModuleInstall(tc.ctx, tc.in...)
716			if output.basePath.path != tc.out {
717				t.Errorf("unexpected path:\n got: %q\nwant: %q\n",
718					output.basePath.path,
719					tc.out)
720			}
721			if output.partitionDir != tc.partitionDir {
722				t.Errorf("unexpected partitionDir:\n got: %q\nwant: %q\n",
723					output.partitionDir, tc.partitionDir)
724			}
725		})
726	}
727}
728
729func TestPathForModuleInstallRecoveryAsBoot(t *testing.T) {
730	testConfig := pathTestConfig("")
731	testConfig.TestProductVariables.BoardUsesRecoveryAsBoot = proptools.BoolPtr(true)
732	testConfig.TestProductVariables.BoardMoveRecoveryResourcesToVendorBoot = proptools.BoolPtr(true)
733	deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
734
735	testCases := []struct {
736		name         string
737		ctx          *testModuleInstallPathContext
738		in           []string
739		out          string
740		partitionDir string
741	}{
742		{
743			name: "ramdisk binary",
744			ctx: &testModuleInstallPathContext{
745				baseModuleContext: baseModuleContext{
746					os:     deviceTarget.Os,
747					target: deviceTarget,
748				},
749				inRamdisk: true,
750				inRoot:    true,
751			},
752			in:           []string{"my_test"},
753			out:          "target/product/test_device/recovery/root/first_stage_ramdisk/my_test",
754			partitionDir: "target/product/test_device/recovery/root/first_stage_ramdisk",
755		},
756
757		{
758			name: "vendor_ramdisk binary",
759			ctx: &testModuleInstallPathContext{
760				baseModuleContext: baseModuleContext{
761					os:     deviceTarget.Os,
762					target: deviceTarget,
763				},
764				inVendorRamdisk: true,
765				inRoot:          true,
766			},
767			in:           []string{"my_test"},
768			out:          "target/product/test_device/vendor_ramdisk/first_stage_ramdisk/my_test",
769			partitionDir: "target/product/test_device/vendor_ramdisk/first_stage_ramdisk",
770		},
771	}
772
773	for _, tc := range testCases {
774		t.Run(tc.name, func(t *testing.T) {
775			tc.ctx.baseModuleContext.config = testConfig
776			output := PathForModuleInstall(tc.ctx, tc.in...)
777			if output.basePath.path != tc.out {
778				t.Errorf("unexpected path:\n got: %q\nwant: %q\n",
779					output.basePath.path,
780					tc.out)
781			}
782			if output.partitionDir != tc.partitionDir {
783				t.Errorf("unexpected partitionDir:\n got: %q\nwant: %q\n",
784					output.partitionDir, tc.partitionDir)
785			}
786		})
787	}
788}
789
790func TestBaseDirForInstallPath(t *testing.T) {
791	testConfig := pathTestConfig("")
792	deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
793
794	ctx := &testModuleInstallPathContext{
795		baseModuleContext: baseModuleContext{
796			os:     deviceTarget.Os,
797			target: deviceTarget,
798		},
799	}
800	ctx.baseModuleContext.config = testConfig
801
802	actual := PathForModuleInstall(ctx, "foo", "bar")
803	expectedBaseDir := "target/product/test_device/system"
804	if actual.partitionDir != expectedBaseDir {
805		t.Errorf("unexpected partitionDir:\n got: %q\nwant: %q\n", actual.partitionDir, expectedBaseDir)
806	}
807	expectedRelPath := "foo/bar"
808	if actual.Rel() != expectedRelPath {
809		t.Errorf("unexpected Rel():\n got: %q\nwant: %q\n", actual.Rel(), expectedRelPath)
810	}
811
812	actualAfterJoin := actual.Join(ctx, "baz")
813	// partitionDir is preserved even after joining
814	if actualAfterJoin.partitionDir != expectedBaseDir {
815		t.Errorf("unexpected partitionDir after joining:\n got: %q\nwant: %q\n", actualAfterJoin.partitionDir, expectedBaseDir)
816	}
817	// Rel() is updated though
818	expectedRelAfterJoin := "baz"
819	if actualAfterJoin.Rel() != expectedRelAfterJoin {
820		t.Errorf("unexpected Rel() after joining:\n got: %q\nwant: %q\n", actualAfterJoin.Rel(), expectedRelAfterJoin)
821	}
822}
823
824func TestDirectorySortedPaths(t *testing.T) {
825	config := TestConfig("out", nil, "", map[string][]byte{
826		"Android.bp": nil,
827		"a.txt":      nil,
828		"a/txt":      nil,
829		"a/b/c":      nil,
830		"a/b/d":      nil,
831		"b":          nil,
832		"b/b.txt":    nil,
833		"a/a.txt":    nil,
834	})
835
836	ctx := PathContextForTesting(config)
837
838	makePaths := func() Paths {
839		return Paths{
840			PathForSource(ctx, "a.txt"),
841			PathForSource(ctx, "a/txt"),
842			PathForSource(ctx, "a/b/c"),
843			PathForSource(ctx, "a/b/d"),
844			PathForSource(ctx, "b"),
845			PathForSource(ctx, "b/b.txt"),
846			PathForSource(ctx, "a/a.txt"),
847		}
848	}
849
850	expected := []string{
851		"a.txt",
852		"a/a.txt",
853		"a/b/c",
854		"a/b/d",
855		"a/txt",
856		"b",
857		"b/b.txt",
858	}
859
860	paths := makePaths()
861	reversePaths := ReversePaths(paths)
862
863	sortedPaths := PathsToDirectorySortedPaths(paths)
864	reverseSortedPaths := PathsToDirectorySortedPaths(reversePaths)
865
866	if !reflect.DeepEqual(Paths(sortedPaths).Strings(), expected) {
867		t.Fatalf("sorted paths:\n %#v\n != \n %#v", paths.Strings(), expected)
868	}
869
870	if !reflect.DeepEqual(Paths(reverseSortedPaths).Strings(), expected) {
871		t.Fatalf("sorted reversed paths:\n %#v\n !=\n %#v", reversePaths.Strings(), expected)
872	}
873
874	expectedA := []string{
875		"a/a.txt",
876		"a/b/c",
877		"a/b/d",
878		"a/txt",
879	}
880
881	inA := sortedPaths.PathsInDirectory("a")
882	if !reflect.DeepEqual(inA.Strings(), expectedA) {
883		t.Errorf("FilesInDirectory(a):\n %#v\n != \n %#v", inA.Strings(), expectedA)
884	}
885
886	expectedA_B := []string{
887		"a/b/c",
888		"a/b/d",
889	}
890
891	inA_B := sortedPaths.PathsInDirectory("a/b")
892	if !reflect.DeepEqual(inA_B.Strings(), expectedA_B) {
893		t.Errorf("FilesInDirectory(a/b):\n %#v\n != \n %#v", inA_B.Strings(), expectedA_B)
894	}
895
896	expectedB := []string{
897		"b/b.txt",
898	}
899
900	inB := sortedPaths.PathsInDirectory("b")
901	if !reflect.DeepEqual(inB.Strings(), expectedB) {
902		t.Errorf("FilesInDirectory(b):\n %#v\n != \n %#v", inA.Strings(), expectedA)
903	}
904}
905
906func TestMaybeRel(t *testing.T) {
907	testCases := []struct {
908		name   string
909		base   string
910		target string
911		out    string
912		isRel  bool
913	}{
914		{
915			name:   "normal",
916			base:   "a/b/c",
917			target: "a/b/c/d",
918			out:    "d",
919			isRel:  true,
920		},
921		{
922			name:   "parent",
923			base:   "a/b/c/d",
924			target: "a/b/c",
925			isRel:  false,
926		},
927		{
928			name:   "not relative",
929			base:   "a/b",
930			target: "c/d",
931			isRel:  false,
932		},
933		{
934			name:   "abs1",
935			base:   "/a",
936			target: "a",
937			isRel:  false,
938		},
939		{
940			name:   "abs2",
941			base:   "a",
942			target: "/a",
943			isRel:  false,
944		},
945	}
946
947	for _, testCase := range testCases {
948		t.Run(testCase.name, func(t *testing.T) {
949			ctx := &configErrorWrapper{}
950			out, isRel := MaybeRel(ctx, testCase.base, testCase.target)
951			if len(ctx.errors) > 0 {
952				t.Errorf("MaybeRel(..., %s, %s) reported unexpected errors %v",
953					testCase.base, testCase.target, ctx.errors)
954			}
955			if isRel != testCase.isRel || out != testCase.out {
956				t.Errorf("MaybeRel(..., %s, %s) want %v, %v got %v, %v",
957					testCase.base, testCase.target, testCase.out, testCase.isRel, out, isRel)
958			}
959		})
960	}
961}
962
963func TestPathForSource(t *testing.T) {
964	testCases := []struct {
965		name     string
966		buildDir string
967		src      string
968		err      string
969	}{
970		{
971			name:     "normal",
972			buildDir: "out",
973			src:      "a/b/c",
974		},
975		{
976			name:     "abs",
977			buildDir: "out",
978			src:      "/a/b/c",
979			err:      "is outside directory",
980		},
981		{
982			name:     "in out dir",
983			buildDir: "out",
984			src:      "out/a/b/c",
985			err:      "is in output",
986		},
987	}
988
989	funcs := []struct {
990		name string
991		f    func(ctx PathContext, pathComponents ...string) (SourcePath, error)
992	}{
993		{"pathForSource", pathForSource},
994		{"safePathForSource", safePathForSource},
995	}
996
997	for _, f := range funcs {
998		t.Run(f.name, func(t *testing.T) {
999			for _, test := range testCases {
1000				t.Run(test.name, func(t *testing.T) {
1001					testConfig := pathTestConfig(test.buildDir)
1002					ctx := &configErrorWrapper{config: testConfig}
1003					_, err := f.f(ctx, test.src)
1004					if len(ctx.errors) > 0 {
1005						t.Fatalf("unexpected errors %v", ctx.errors)
1006					}
1007					if err != nil {
1008						if test.err == "" {
1009							t.Fatalf("unexpected error %q", err.Error())
1010						} else if !strings.Contains(err.Error(), test.err) {
1011							t.Fatalf("incorrect error, want substring %q got %q", test.err, err.Error())
1012						}
1013					} else {
1014						if test.err != "" {
1015							t.Fatalf("missing error %q", test.err)
1016						}
1017					}
1018				})
1019			}
1020		})
1021	}
1022}
1023
1024type pathForModuleSrcTestModule struct {
1025	ModuleBase
1026	props struct {
1027		Srcs         []string `android:"path"`
1028		Exclude_srcs []string `android:"path"`
1029
1030		Src *string `android:"path"`
1031
1032		Module_handles_missing_deps bool
1033	}
1034
1035	src string
1036	rel string
1037
1038	srcs []string
1039	rels []string
1040
1041	missingDeps []string
1042}
1043
1044func pathForModuleSrcTestModuleFactory() Module {
1045	module := &pathForModuleSrcTestModule{}
1046	module.AddProperties(&module.props)
1047	InitAndroidModule(module)
1048	return module
1049}
1050
1051func (p *pathForModuleSrcTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
1052	var srcs Paths
1053	if p.props.Module_handles_missing_deps {
1054		srcs, p.missingDeps = PathsAndMissingDepsForModuleSrcExcludes(ctx, p.props.Srcs, p.props.Exclude_srcs)
1055	} else {
1056		srcs = PathsForModuleSrcExcludes(ctx, p.props.Srcs, p.props.Exclude_srcs)
1057	}
1058	p.srcs = srcs.Strings()
1059
1060	for _, src := range srcs {
1061		p.rels = append(p.rels, src.Rel())
1062	}
1063
1064	if p.props.Src != nil {
1065		src := PathForModuleSrc(ctx, *p.props.Src)
1066		if src != nil {
1067			p.src = src.String()
1068			p.rel = src.Rel()
1069		}
1070	}
1071
1072	if !p.props.Module_handles_missing_deps {
1073		p.missingDeps = ctx.GetMissingDependencies()
1074	}
1075
1076	ctx.Build(pctx, BuildParams{
1077		Rule:   Touch,
1078		Output: PathForModuleOut(ctx, "output"),
1079	})
1080}
1081
1082type pathForModuleSrcOutputFileProviderModule struct {
1083	ModuleBase
1084	props struct {
1085		Outs   []string
1086		Tagged []string
1087	}
1088
1089	outs   Paths
1090	tagged Paths
1091}
1092
1093func pathForModuleSrcOutputFileProviderModuleFactory() Module {
1094	module := &pathForModuleSrcOutputFileProviderModule{}
1095	module.AddProperties(&module.props)
1096	InitAndroidModule(module)
1097	return module
1098}
1099
1100func (p *pathForModuleSrcOutputFileProviderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
1101	for _, out := range p.props.Outs {
1102		p.outs = append(p.outs, PathForModuleOut(ctx, out))
1103	}
1104
1105	for _, tagged := range p.props.Tagged {
1106		p.tagged = append(p.tagged, PathForModuleOut(ctx, tagged))
1107	}
1108}
1109
1110func (p *pathForModuleSrcOutputFileProviderModule) OutputFiles(tag string) (Paths, error) {
1111	switch tag {
1112	case "":
1113		return p.outs, nil
1114	case ".tagged":
1115		return p.tagged, nil
1116	default:
1117		return nil, fmt.Errorf("unsupported tag %q", tag)
1118	}
1119}
1120
1121type pathForModuleSrcTestCase struct {
1122	name string
1123	bp   string
1124	srcs []string
1125	rels []string
1126	src  string
1127	rel  string
1128}
1129
1130func testPathForModuleSrc(t *testing.T, tests []pathForModuleSrcTestCase) {
1131	for _, test := range tests {
1132		t.Run(test.name, func(t *testing.T) {
1133			fgBp := `
1134				filegroup {
1135					name: "a",
1136					srcs: ["src/a"],
1137				}
1138			`
1139
1140			ofpBp := `
1141				output_file_provider {
1142					name: "b",
1143					outs: ["gen/b"],
1144					tagged: ["gen/c"],
1145				}
1146			`
1147
1148			mockFS := MockFS{
1149				"fg/Android.bp":     []byte(fgBp),
1150				"foo/Android.bp":    []byte(test.bp),
1151				"ofp/Android.bp":    []byte(ofpBp),
1152				"fg/src/a":          nil,
1153				"foo/src/b":         nil,
1154				"foo/src/c":         nil,
1155				"foo/src/d":         nil,
1156				"foo/src/e/e":       nil,
1157				"foo/src_special/$": nil,
1158			}
1159
1160			result := GroupFixturePreparers(
1161				FixtureRegisterWithContext(func(ctx RegistrationContext) {
1162					ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
1163					ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory)
1164					ctx.RegisterModuleType("filegroup", FileGroupFactory)
1165				}),
1166				mockFS.AddToFixture(),
1167			).RunTest(t)
1168
1169			m := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
1170
1171			AssertStringPathsRelativeToTopEquals(t, "srcs", result.Config, test.srcs, m.srcs)
1172			AssertStringPathsRelativeToTopEquals(t, "rels", result.Config, test.rels, m.rels)
1173			AssertStringPathRelativeToTopEquals(t, "src", result.Config, test.src, m.src)
1174			AssertStringPathRelativeToTopEquals(t, "rel", result.Config, test.rel, m.rel)
1175		})
1176	}
1177}
1178
1179func TestPathsForModuleSrc(t *testing.T) {
1180	tests := []pathForModuleSrcTestCase{
1181		{
1182			name: "path",
1183			bp: `
1184			test {
1185				name: "foo",
1186				srcs: ["src/b"],
1187			}`,
1188			srcs: []string{"foo/src/b"},
1189			rels: []string{"src/b"},
1190		},
1191		{
1192			name: "glob",
1193			bp: `
1194			test {
1195				name: "foo",
1196				srcs: [
1197					"src/*",
1198					"src/e/*",
1199				],
1200			}`,
1201			srcs: []string{"foo/src/b", "foo/src/c", "foo/src/d", "foo/src/e/e"},
1202			rels: []string{"src/b", "src/c", "src/d", "src/e/e"},
1203		},
1204		{
1205			name: "recursive glob",
1206			bp: `
1207			test {
1208				name: "foo",
1209				srcs: ["src/**/*"],
1210			}`,
1211			srcs: []string{"foo/src/b", "foo/src/c", "foo/src/d", "foo/src/e/e"},
1212			rels: []string{"src/b", "src/c", "src/d", "src/e/e"},
1213		},
1214		{
1215			name: "filegroup",
1216			bp: `
1217			test {
1218				name: "foo",
1219				srcs: [":a"],
1220			}`,
1221			srcs: []string{"fg/src/a"},
1222			rels: []string{"src/a"},
1223		},
1224		{
1225			name: "output file provider",
1226			bp: `
1227			test {
1228				name: "foo",
1229				srcs: [":b"],
1230			}`,
1231			srcs: []string{"out/soong/.intermediates/ofp/b/gen/b"},
1232			rels: []string{"gen/b"},
1233		},
1234		{
1235			name: "output file provider tagged",
1236			bp: `
1237			test {
1238				name: "foo",
1239				srcs: [":b{.tagged}"],
1240			}`,
1241			srcs: []string{"out/soong/.intermediates/ofp/b/gen/c"},
1242			rels: []string{"gen/c"},
1243		},
1244		{
1245			name: "output file provider with exclude",
1246			bp: `
1247			test {
1248				name: "foo",
1249				srcs: [":b", ":c"],
1250				exclude_srcs: [":c"]
1251			}
1252			output_file_provider {
1253				name: "c",
1254				outs: ["gen/c"],
1255			}`,
1256			srcs: []string{"out/soong/.intermediates/ofp/b/gen/b"},
1257			rels: []string{"gen/b"},
1258		},
1259		{
1260			name: "special characters glob",
1261			bp: `
1262			test {
1263				name: "foo",
1264				srcs: ["src_special/*"],
1265			}`,
1266			srcs: []string{"foo/src_special/$"},
1267			rels: []string{"src_special/$"},
1268		},
1269	}
1270
1271	testPathForModuleSrc(t, tests)
1272}
1273
1274func TestPathForModuleSrc(t *testing.T) {
1275	tests := []pathForModuleSrcTestCase{
1276		{
1277			name: "path",
1278			bp: `
1279			test {
1280				name: "foo",
1281				src: "src/b",
1282			}`,
1283			src: "foo/src/b",
1284			rel: "src/b",
1285		},
1286		{
1287			name: "glob",
1288			bp: `
1289			test {
1290				name: "foo",
1291				src: "src/e/*",
1292			}`,
1293			src: "foo/src/e/e",
1294			rel: "src/e/e",
1295		},
1296		{
1297			name: "filegroup",
1298			bp: `
1299			test {
1300				name: "foo",
1301				src: ":a",
1302			}`,
1303			src: "fg/src/a",
1304			rel: "src/a",
1305		},
1306		{
1307			name: "output file provider",
1308			bp: `
1309			test {
1310				name: "foo",
1311				src: ":b",
1312			}`,
1313			src: "out/soong/.intermediates/ofp/b/gen/b",
1314			rel: "gen/b",
1315		},
1316		{
1317			name: "output file provider tagged",
1318			bp: `
1319			test {
1320				name: "foo",
1321				src: ":b{.tagged}",
1322			}`,
1323			src: "out/soong/.intermediates/ofp/b/gen/c",
1324			rel: "gen/c",
1325		},
1326		{
1327			name: "special characters glob",
1328			bp: `
1329			test {
1330				name: "foo",
1331				src: "src_special/*",
1332			}`,
1333			src: "foo/src_special/$",
1334			rel: "src_special/$",
1335		},
1336	}
1337
1338	testPathForModuleSrc(t, tests)
1339}
1340
1341func TestPathsForModuleSrc_AllowMissingDependencies(t *testing.T) {
1342	bp := `
1343		test {
1344			name: "foo",
1345			srcs: [":a"],
1346			exclude_srcs: [":b"],
1347			src: ":c",
1348		}
1349
1350		test {
1351			name: "bar",
1352			srcs: [":d"],
1353			exclude_srcs: [":e"],
1354			module_handles_missing_deps: true,
1355		}
1356	`
1357
1358	result := GroupFixturePreparers(
1359		PrepareForTestWithAllowMissingDependencies,
1360		FixtureRegisterWithContext(func(ctx RegistrationContext) {
1361			ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
1362		}),
1363		FixtureWithRootAndroidBp(bp),
1364	).RunTest(t)
1365
1366	foo := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
1367
1368	AssertArrayString(t, "foo missing deps", []string{"a", "b", "c"}, foo.missingDeps)
1369	AssertArrayString(t, "foo srcs", []string{}, foo.srcs)
1370	AssertStringEquals(t, "foo src", "", foo.src)
1371
1372	bar := result.ModuleForTests("bar", "").Module().(*pathForModuleSrcTestModule)
1373
1374	AssertArrayString(t, "bar missing deps", []string{"d", "e"}, bar.missingDeps)
1375	AssertArrayString(t, "bar srcs", []string{}, bar.srcs)
1376}
1377
1378func TestPathRelativeToTop(t *testing.T) {
1379	testConfig := pathTestConfig("/tmp/build/top")
1380	deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
1381
1382	ctx := &testModuleInstallPathContext{
1383		baseModuleContext: baseModuleContext{
1384			os:     deviceTarget.Os,
1385			target: deviceTarget,
1386		},
1387	}
1388	ctx.baseModuleContext.config = testConfig
1389
1390	t.Run("install for soong", func(t *testing.T) {
1391		p := PathForModuleInstall(ctx, "install/path")
1392		AssertPathRelativeToTopEquals(t, "install path for soong", "out/soong/target/product/test_device/system/install/path", p)
1393	})
1394	t.Run("install for make", func(t *testing.T) {
1395		p := PathForModuleInstall(ctx, "install/path").ToMakePath()
1396		AssertPathRelativeToTopEquals(t, "install path for make", "out/target/product/test_device/system/install/path", p)
1397	})
1398	t.Run("output", func(t *testing.T) {
1399		p := PathForOutput(ctx, "output/path")
1400		AssertPathRelativeToTopEquals(t, "output path", "out/soong/output/path", p)
1401	})
1402	t.Run("source", func(t *testing.T) {
1403		p := PathForSource(ctx, "source/path")
1404		AssertPathRelativeToTopEquals(t, "source path", "source/path", p)
1405	})
1406	t.Run("mixture", func(t *testing.T) {
1407		paths := Paths{
1408			PathForModuleInstall(ctx, "install/path"),
1409			PathForModuleInstall(ctx, "install/path").ToMakePath(),
1410			PathForOutput(ctx, "output/path"),
1411			PathForSource(ctx, "source/path"),
1412		}
1413
1414		expected := []string{
1415			"out/soong/target/product/test_device/system/install/path",
1416			"out/target/product/test_device/system/install/path",
1417			"out/soong/output/path",
1418			"source/path",
1419		}
1420		AssertPathsRelativeToTopEquals(t, "mixture", expected, paths)
1421	})
1422}
1423
1424func ExampleOutputPath_ReplaceExtension() {
1425	ctx := &configErrorWrapper{
1426		config: TestConfig("out", nil, "", nil),
1427	}
1428	p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art")
1429	p2 := p.ReplaceExtension(ctx, "oat")
1430	fmt.Println(p, p2)
1431	fmt.Println(p.Rel(), p2.Rel())
1432
1433	// Output:
1434	// out/system/framework/boot.art out/system/framework/boot.oat
1435	// boot.art boot.oat
1436}
1437
1438func ExampleOutputPath_InSameDir() {
1439	ctx := &configErrorWrapper{
1440		config: TestConfig("out", nil, "", nil),
1441	}
1442	p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art")
1443	p2 := p.InSameDir(ctx, "oat", "arm", "boot.vdex")
1444	fmt.Println(p, p2)
1445	fmt.Println(p.Rel(), p2.Rel())
1446
1447	// Output:
1448	// out/system/framework/boot.art out/system/framework/oat/arm/boot.vdex
1449	// boot.art oat/arm/boot.vdex
1450}
1451
1452func BenchmarkFirstUniquePaths(b *testing.B) {
1453	implementations := []struct {
1454		name string
1455		f    func(Paths) Paths
1456	}{
1457		{
1458			name: "list",
1459			f:    firstUniquePathsList,
1460		},
1461		{
1462			name: "map",
1463			f:    firstUniquePathsMap,
1464		},
1465	}
1466	const maxSize = 1024
1467	uniquePaths := make(Paths, maxSize)
1468	for i := range uniquePaths {
1469		uniquePaths[i] = PathForTesting(strconv.Itoa(i))
1470	}
1471	samePath := make(Paths, maxSize)
1472	for i := range samePath {
1473		samePath[i] = uniquePaths[0]
1474	}
1475
1476	f := func(b *testing.B, imp func(Paths) Paths, paths Paths) {
1477		for i := 0; i < b.N; i++ {
1478			b.ReportAllocs()
1479			paths = append(Paths(nil), paths...)
1480			imp(paths)
1481		}
1482	}
1483
1484	for n := 1; n <= maxSize; n <<= 1 {
1485		b.Run(strconv.Itoa(n), func(b *testing.B) {
1486			for _, implementation := range implementations {
1487				b.Run(implementation.name, func(b *testing.B) {
1488					b.Run("same", func(b *testing.B) {
1489						f(b, implementation.f, samePath[:n])
1490					})
1491					b.Run("unique", func(b *testing.B) {
1492						f(b, implementation.f, uniquePaths[:n])
1493					})
1494				})
1495			}
1496		})
1497	}
1498}
1499