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