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