xref: /aosp_15_r20/build/soong/android/paths_test.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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
1188func pathForModuleSrcOutputFileProviderModuleFactory() Module {
1189	module := &pathForModuleSrcOutputFileProviderModule{}
1190	module.AddProperties(&module.props)
1191	InitAndroidModule(module)
1192	return module
1193}
1194
1195func (p *pathForModuleSrcOutputFileProviderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
1196	var outs, taggedOuts Paths
1197	for _, out := range p.props.Outs {
1198		outs = append(outs, PathForModuleOut(ctx, out))
1199	}
1200
1201	for _, tagged := range p.props.Tagged {
1202		taggedOuts = append(taggedOuts, PathForModuleOut(ctx, tagged))
1203	}
1204
1205	ctx.SetOutputFiles(outs, "")
1206	ctx.SetOutputFiles(taggedOuts, ".tagged")
1207}
1208
1209type pathForModuleSrcTestCase struct {
1210	name string
1211	bp   string
1212	srcs []string
1213	rels []string
1214	src  string
1215	rel  string
1216
1217	// Make test specific preparations to the test fixture.
1218	preparer FixturePreparer
1219
1220	// A test specific error handler.
1221	errorHandler FixtureErrorHandler
1222}
1223
1224func testPathForModuleSrc(t *testing.T, tests []pathForModuleSrcTestCase) {
1225	for _, test := range tests {
1226		t.Run(test.name, func(t *testing.T) {
1227			fgBp := `
1228				filegroup {
1229					name: "a",
1230					srcs: ["src/a"],
1231				}
1232			`
1233
1234			ofpBp := `
1235				output_file_provider {
1236					name: "b",
1237					outs: ["gen/b"],
1238					tagged: ["gen/c"],
1239				}
1240			`
1241
1242			mockFS := MockFS{
1243				"fg/Android.bp":     []byte(fgBp),
1244				"foo/Android.bp":    []byte(test.bp),
1245				"ofp/Android.bp":    []byte(ofpBp),
1246				"fg/src/a":          nil,
1247				"foo/src/b":         nil,
1248				"foo/src/c":         nil,
1249				"foo/src/d":         nil,
1250				"foo/src/e/e":       nil,
1251				"foo/src_special/$": nil,
1252			}
1253
1254			errorHandler := test.errorHandler
1255			if errorHandler == nil {
1256				errorHandler = FixtureExpectsNoErrors
1257			}
1258
1259			result := GroupFixturePreparers(
1260				FixtureRegisterWithContext(func(ctx RegistrationContext) {
1261					ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
1262					ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory)
1263				}),
1264				PrepareForTestWithFilegroup,
1265				PrepareForTestWithNamespace,
1266				mockFS.AddToFixture(),
1267				OptionalFixturePreparer(test.preparer),
1268			).
1269				ExtendWithErrorHandler(errorHandler).
1270				RunTest(t)
1271
1272			m := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
1273
1274			AssertStringPathsRelativeToTopEquals(t, "srcs", result.Config, test.srcs, m.srcs)
1275			AssertStringPathsRelativeToTopEquals(t, "rels", result.Config, test.rels, m.rels)
1276			AssertStringPathRelativeToTopEquals(t, "src", result.Config, test.src, m.src)
1277			AssertStringPathRelativeToTopEquals(t, "rel", result.Config, test.rel, m.rel)
1278		})
1279	}
1280}
1281
1282func TestPathsForModuleSrc(t *testing.T) {
1283	tests := []pathForModuleSrcTestCase{
1284		{
1285			name: "path",
1286			bp: `
1287			test {
1288				name: "foo",
1289				srcs: ["src/b"],
1290			}`,
1291			srcs: []string{"foo/src/b"},
1292			rels: []string{"src/b"},
1293		},
1294		{
1295			name: "glob",
1296			bp: `
1297			test {
1298				name: "foo",
1299				srcs: [
1300					"src/*",
1301					"src/e/*",
1302				],
1303			}`,
1304			srcs: []string{"foo/src/b", "foo/src/c", "foo/src/d", "foo/src/e/e"},
1305			rels: []string{"src/b", "src/c", "src/d", "src/e/e"},
1306		},
1307		{
1308			name: "recursive glob",
1309			bp: `
1310			test {
1311				name: "foo",
1312				srcs: ["src/**/*"],
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: "filegroup",
1319			bp: `
1320			test {
1321				name: "foo",
1322				srcs: [":a"],
1323			}`,
1324			srcs: []string{"fg/src/a"},
1325			rels: []string{"src/a"},
1326		},
1327		{
1328			name: "output file provider",
1329			bp: `
1330			test {
1331				name: "foo",
1332				srcs: [":b"],
1333			}`,
1334			srcs: []string{"out/soong/.intermediates/ofp/b/gen/b"},
1335			rels: []string{"gen/b"},
1336		},
1337		{
1338			name: "output file provider tagged",
1339			bp: `
1340			test {
1341				name: "foo",
1342				srcs: [":b{.tagged}"],
1343			}`,
1344			srcs: []string{"out/soong/.intermediates/ofp/b/gen/c"},
1345			rels: []string{"gen/c"},
1346		},
1347		{
1348			name: "output file provider with exclude",
1349			bp: `
1350			test {
1351				name: "foo",
1352				srcs: [":b", ":c"],
1353				exclude_srcs: [":c"]
1354			}
1355			output_file_provider {
1356				name: "c",
1357				outs: ["gen/c"],
1358			}`,
1359			srcs: []string{"out/soong/.intermediates/ofp/b/gen/b"},
1360			rels: []string{"gen/b"},
1361		},
1362		{
1363			name: "special characters glob",
1364			bp: `
1365			test {
1366				name: "foo",
1367				srcs: ["src_special/*"],
1368			}`,
1369			srcs: []string{"foo/src_special/$"},
1370			rels: []string{"src_special/$"},
1371		},
1372	}
1373
1374	testPathForModuleSrc(t, tests)
1375}
1376
1377func TestPathForModuleSrc(t *testing.T) {
1378	tests := []pathForModuleSrcTestCase{
1379		{
1380			name: "path",
1381			bp: `
1382			test {
1383				name: "foo",
1384				src: "src/b",
1385			}`,
1386			src: "foo/src/b",
1387			rel: "src/b",
1388		},
1389		{
1390			name: "glob",
1391			bp: `
1392			test {
1393				name: "foo",
1394				src: "src/e/*",
1395			}`,
1396			src: "foo/src/e/e",
1397			rel: "src/e/e",
1398		},
1399		{
1400			name: "filegroup",
1401			bp: `
1402			test {
1403				name: "foo",
1404				src: ":a",
1405			}`,
1406			src: "fg/src/a",
1407			rel: "src/a",
1408		},
1409		{
1410			name: "output file provider",
1411			bp: `
1412			test {
1413				name: "foo",
1414				src: ":b",
1415			}`,
1416			src: "out/soong/.intermediates/ofp/b/gen/b",
1417			rel: "gen/b",
1418		},
1419		{
1420			name: "output file provider tagged",
1421			bp: `
1422			test {
1423				name: "foo",
1424				src: ":b{.tagged}",
1425			}`,
1426			src: "out/soong/.intermediates/ofp/b/gen/c",
1427			rel: "gen/c",
1428		},
1429		{
1430			name: "special characters glob",
1431			bp: `
1432			test {
1433				name: "foo",
1434				src: "src_special/*",
1435			}`,
1436			src: "foo/src_special/$",
1437			rel: "src_special/$",
1438		},
1439		{
1440			// This test makes sure that an unqualified module name cannot contain characters that make
1441			// it appear as a qualified module name.
1442			name: "output file provider, invalid fully qualified name",
1443			bp: `
1444			test {
1445				name: "foo",
1446				src: "://other:b",
1447				srcs: ["://other:c"],
1448			}`,
1449			preparer: FixtureAddTextFile("other/Android.bp", `
1450				soong_namespace {}
1451
1452				output_file_provider {
1453					name: "b",
1454					outs: ["gen/b"],
1455				}
1456
1457				output_file_provider {
1458					name: "c",
1459					outs: ["gen/c"],
1460				}
1461			`),
1462			src:  "foo/:/other:b",
1463			rel:  ":/other:b",
1464			srcs: []string{"foo/:/other:c"},
1465			rels: []string{":/other:c"},
1466		},
1467		{
1468			name: "output file provider, missing fully qualified name",
1469			bp: `
1470			test {
1471				name: "foo",
1472				src: "//other:b",
1473				srcs: ["//other:c"],
1474			}`,
1475			errorHandler: FixtureExpectsAllErrorsToMatchAPattern([]string{
1476				`"foo" depends on undefined module "//other:b"`,
1477				`"foo" depends on undefined module "//other:c"`,
1478			}),
1479		},
1480		{
1481			name: "output file provider, fully qualified name",
1482			bp: `
1483			test {
1484				name: "foo",
1485				src: "//other:b",
1486				srcs: ["//other:c"],
1487			}`,
1488			src:  "out/soong/.intermediates/other/b/gen/b",
1489			rel:  "gen/b",
1490			srcs: []string{"out/soong/.intermediates/other/c/gen/c"},
1491			rels: []string{"gen/c"},
1492			preparer: FixtureAddTextFile("other/Android.bp", `
1493				soong_namespace {}
1494
1495				output_file_provider {
1496					name: "b",
1497					outs: ["gen/b"],
1498				}
1499
1500				output_file_provider {
1501					name: "c",
1502					outs: ["gen/c"],
1503				}
1504			`),
1505		},
1506	}
1507
1508	testPathForModuleSrc(t, tests)
1509}
1510
1511func TestPathsForModuleSrc_AllowMissingDependencies(t *testing.T) {
1512	bp := `
1513		test {
1514			name: "foo",
1515			srcs: [":a"],
1516			exclude_srcs: [":b"],
1517			src: ":c",
1518		}
1519
1520		test {
1521			name: "bar",
1522			srcs: [":d"],
1523			exclude_srcs: [":e"],
1524			module_handles_missing_deps: true,
1525		}
1526	`
1527
1528	result := GroupFixturePreparers(
1529		PrepareForTestWithAllowMissingDependencies,
1530		FixtureRegisterWithContext(func(ctx RegistrationContext) {
1531			ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
1532		}),
1533		FixtureWithRootAndroidBp(bp),
1534	).RunTest(t)
1535
1536	foo := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
1537
1538	AssertArrayString(t, "foo missing deps", []string{"a", "b", "c"}, foo.missingDeps)
1539	AssertArrayString(t, "foo srcs", []string{}, foo.srcs)
1540	AssertStringEquals(t, "foo src", "", foo.src)
1541
1542	bar := result.ModuleForTests("bar", "").Module().(*pathForModuleSrcTestModule)
1543
1544	AssertArrayString(t, "bar missing deps", []string{"d", "e"}, bar.missingDeps)
1545	AssertArrayString(t, "bar srcs", []string{}, bar.srcs)
1546}
1547
1548func TestPathRelativeToTop(t *testing.T) {
1549	testConfig := pathTestConfig("/tmp/build/top")
1550	deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
1551
1552	ctx := &testModuleInstallPathContext{
1553		baseModuleContext: baseModuleContext{
1554			archModuleContext: archModuleContext{
1555				os:     deviceTarget.Os,
1556				target: deviceTarget,
1557			},
1558		},
1559	}
1560	ctx.baseModuleContext.config = testConfig
1561
1562	t.Run("install for soong", func(t *testing.T) {
1563		p := PathForModuleInstall(ctx, "install/path")
1564		AssertPathRelativeToTopEquals(t, "install path for soong", "out/soong/target/product/test_device/system/install/path", p)
1565	})
1566	t.Run("install for make", func(t *testing.T) {
1567		p := PathForModuleInstall(ctx, "install/path")
1568		p.makePath = true
1569		AssertPathRelativeToTopEquals(t, "install path for make", "out/target/product/test_device/system/install/path", p)
1570	})
1571	t.Run("output", func(t *testing.T) {
1572		p := PathForOutput(ctx, "output/path")
1573		AssertPathRelativeToTopEquals(t, "output path", "out/soong/output/path", p)
1574	})
1575	t.Run("source", func(t *testing.T) {
1576		p := PathForSource(ctx, "source/path")
1577		AssertPathRelativeToTopEquals(t, "source path", "source/path", p)
1578	})
1579	t.Run("mixture", func(t *testing.T) {
1580		paths := Paths{
1581			PathForModuleInstall(ctx, "install/path"),
1582			PathForOutput(ctx, "output/path"),
1583			PathForSource(ctx, "source/path"),
1584		}
1585
1586		expected := []string{
1587			"out/soong/target/product/test_device/system/install/path",
1588			"out/soong/output/path",
1589			"source/path",
1590		}
1591		AssertPathsRelativeToTopEquals(t, "mixture", expected, paths)
1592	})
1593}
1594
1595func TestDirectoryPathIsIncompatibleWithPath(t *testing.T) {
1596	d := (DirectoryPath)(&directoryPath{})
1597	_, ok := d.(Path)
1598	AssertBoolEquals(t, "directoryPath shouldn't implement Path", ok, false)
1599}
1600
1601func ExampleOutputPath_ReplaceExtension() {
1602	ctx := &configErrorWrapper{
1603		config: TestConfig("out", nil, "", nil),
1604	}
1605	p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art")
1606	p2 := p.ReplaceExtension(ctx, "oat")
1607	fmt.Println(p, p2)
1608	fmt.Println(p.Rel(), p2.Rel())
1609
1610	// Output:
1611	// out/soong/system/framework/boot.art out/soong/system/framework/boot.oat
1612	// boot.art boot.oat
1613}
1614
1615func ExampleOutputPath_InSameDir() {
1616	ctx := &configErrorWrapper{
1617		config: TestConfig("out", nil, "", nil),
1618	}
1619	p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art")
1620	p2 := p.InSameDir(ctx, "oat", "arm", "boot.vdex")
1621	fmt.Println(p, p2)
1622	fmt.Println(p.Rel(), p2.Rel())
1623
1624	// Output:
1625	// out/soong/system/framework/boot.art out/soong/system/framework/oat/arm/boot.vdex
1626	// boot.art oat/arm/boot.vdex
1627}
1628
1629func BenchmarkFirstUniquePaths(b *testing.B) {
1630	implementations := []struct {
1631		name string
1632		f    func(Paths) Paths
1633	}{
1634		{
1635			name: "list",
1636			f:    firstUniquePathsList,
1637		},
1638		{
1639			name: "map",
1640			f:    firstUniquePathsMap,
1641		},
1642	}
1643	const maxSize = 1024
1644	uniquePaths := make(Paths, maxSize)
1645	for i := range uniquePaths {
1646		uniquePaths[i] = PathForTesting(strconv.Itoa(i))
1647	}
1648	samePath := make(Paths, maxSize)
1649	for i := range samePath {
1650		samePath[i] = uniquePaths[0]
1651	}
1652
1653	f := func(b *testing.B, imp func(Paths) Paths, paths Paths) {
1654		for i := 0; i < b.N; i++ {
1655			b.ReportAllocs()
1656			paths = append(Paths(nil), paths...)
1657			imp(paths)
1658		}
1659	}
1660
1661	for n := 1; n <= maxSize; n <<= 1 {
1662		b.Run(strconv.Itoa(n), func(b *testing.B) {
1663			for _, implementation := range implementations {
1664				b.Run(implementation.name, func(b *testing.B) {
1665					b.Run("same", func(b *testing.B) {
1666						f(b, implementation.f, samePath[:n])
1667					})
1668					b.Run("unique", func(b *testing.B) {
1669						f(b, implementation.f, uniquePaths[:n])
1670					})
1671				})
1672			}
1673		})
1674	}
1675}
1676