1// Copyright 2017 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	"fmt"
19	"path/filepath"
20	"regexp"
21	"sort"
22	"strings"
23	"testing"
24
25	"github.com/google/blueprint"
26)
27
28func NewTestContext() *TestContext {
29	namespaceExportFilter := func(namespace *Namespace) bool {
30		return true
31	}
32
33	nameResolver := NewNameResolver(namespaceExportFilter)
34	ctx := &TestContext{
35		Context:      &Context{blueprint.NewContext()},
36		NameResolver: nameResolver,
37	}
38
39	ctx.SetNameInterface(nameResolver)
40
41	ctx.postDeps = append(ctx.postDeps, registerPathDepsMutator)
42
43	return ctx
44}
45
46func NewTestArchContext() *TestContext {
47	ctx := NewTestContext()
48	ctx.preDeps = append(ctx.preDeps, registerArchMutator)
49	return ctx
50}
51
52type TestContext struct {
53	*Context
54	preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
55	NameResolver                          *NameResolver
56	config                                Config
57}
58
59func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
60	ctx.preArch = append(ctx.preArch, f)
61}
62
63func (ctx *TestContext) HardCodedPreArchMutators(f RegisterMutatorFunc) {
64	// Register mutator function as normal for testing.
65	ctx.PreArchMutators(f)
66}
67
68func (ctx *TestContext) PreDepsMutators(f RegisterMutatorFunc) {
69	ctx.preDeps = append(ctx.preDeps, f)
70}
71
72func (ctx *TestContext) PostDepsMutators(f RegisterMutatorFunc) {
73	ctx.postDeps = append(ctx.postDeps, f)
74}
75
76func (ctx *TestContext) FinalDepsMutators(f RegisterMutatorFunc) {
77	ctx.finalDeps = append(ctx.finalDeps, f)
78}
79
80func (ctx *TestContext) Register(config Config) {
81	ctx.SetFs(config.fs)
82	if config.mockBpList != "" {
83		ctx.SetModuleListFile(config.mockBpList)
84	}
85	registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
86
87	ctx.RegisterSingletonType("env", EnvSingleton)
88
89	ctx.config = config
90}
91
92func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
93	// This function adapts the old style ParseFileList calls that are spread throughout the tests
94	// to the new style that takes a config.
95	return ctx.Context.ParseFileList(rootDir, filePaths, ctx.config)
96}
97
98func (ctx *TestContext) ParseBlueprintsFiles(rootDir string) (deps []string, errs []error) {
99	// This function adapts the old style ParseBlueprintsFiles calls that are spread throughout the
100	// tests to the new style that takes a config.
101	return ctx.Context.ParseBlueprintsFiles(rootDir, ctx.config)
102}
103
104func (ctx *TestContext) RegisterModuleType(name string, factory ModuleFactory) {
105	ctx.Context.RegisterModuleType(name, ModuleFactoryAdaptor(factory))
106}
107
108func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) {
109	ctx.Context.RegisterSingletonType(name, SingletonFactoryAdaptor(factory))
110}
111
112func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule {
113	var module Module
114	ctx.VisitAllModules(func(m blueprint.Module) {
115		if ctx.ModuleName(m) == name && ctx.ModuleSubDir(m) == variant {
116			module = m.(Module)
117		}
118	})
119
120	if module == nil {
121		// find all the modules that do exist
122		allModuleNames := []string{}
123		ctx.VisitAllModules(func(m blueprint.Module) {
124			allModuleNames = append(allModuleNames, m.(Module).Name()+"("+ctx.ModuleSubDir(m)+")")
125		})
126		sort.Strings(allModuleNames)
127
128		panic(fmt.Errorf("failed to find module %q variant %q. All modules:\n  %s",
129			name, variant, strings.Join(allModuleNames, "\n  ")))
130	}
131
132	return TestingModule{module}
133}
134
135func (ctx *TestContext) ModuleVariantsForTests(name string) []string {
136	var variants []string
137	ctx.VisitAllModules(func(m blueprint.Module) {
138		if ctx.ModuleName(m) == name {
139			variants = append(variants, ctx.ModuleSubDir(m))
140		}
141	})
142	return variants
143}
144
145// SingletonForTests returns a TestingSingleton for the singleton registered with the given name.
146func (ctx *TestContext) SingletonForTests(name string) TestingSingleton {
147	allSingletonNames := []string{}
148	for _, s := range ctx.Singletons() {
149		n := ctx.SingletonName(s)
150		if n == name {
151			return TestingSingleton{
152				singleton: s.(*singletonAdaptor).Singleton,
153				provider:  s.(testBuildProvider),
154			}
155		}
156		allSingletonNames = append(allSingletonNames, n)
157	}
158
159	panic(fmt.Errorf("failed to find singleton %q."+
160		"\nall singletons: %v", name, allSingletonNames))
161}
162
163type testBuildProvider interface {
164	BuildParamsForTests() []BuildParams
165	RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
166}
167
168type TestingBuildParams struct {
169	BuildParams
170	RuleParams blueprint.RuleParams
171}
172
173func newTestingBuildParams(provider testBuildProvider, bparams BuildParams) TestingBuildParams {
174	return TestingBuildParams{
175		BuildParams: bparams,
176		RuleParams:  provider.RuleParamsForTests()[bparams.Rule],
177	}
178}
179
180func maybeBuildParamsFromRule(provider testBuildProvider, rule string) TestingBuildParams {
181	for _, p := range provider.BuildParamsForTests() {
182		if strings.Contains(p.Rule.String(), rule) {
183			return newTestingBuildParams(provider, p)
184		}
185	}
186	return TestingBuildParams{}
187}
188
189func buildParamsFromRule(provider testBuildProvider, rule string) TestingBuildParams {
190	p := maybeBuildParamsFromRule(provider, rule)
191	if p.Rule == nil {
192		panic(fmt.Errorf("couldn't find rule %q", rule))
193	}
194	return p
195}
196
197func maybeBuildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
198	for _, p := range provider.BuildParamsForTests() {
199		if strings.Contains(p.Description, desc) {
200			return newTestingBuildParams(provider, p)
201		}
202	}
203	return TestingBuildParams{}
204}
205
206func buildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
207	p := maybeBuildParamsFromDescription(provider, desc)
208	if p.Rule == nil {
209		panic(fmt.Errorf("couldn't find description %q", desc))
210	}
211	return p
212}
213
214func maybeBuildParamsFromOutput(provider testBuildProvider, file string) (TestingBuildParams, []string) {
215	var searchedOutputs []string
216	for _, p := range provider.BuildParamsForTests() {
217		outputs := append(WritablePaths(nil), p.Outputs...)
218		outputs = append(outputs, p.ImplicitOutputs...)
219		if p.Output != nil {
220			outputs = append(outputs, p.Output)
221		}
222		for _, f := range outputs {
223			if f.String() == file || f.Rel() == file {
224				return newTestingBuildParams(provider, p), nil
225			}
226			searchedOutputs = append(searchedOutputs, f.Rel())
227		}
228	}
229	return TestingBuildParams{}, searchedOutputs
230}
231
232func buildParamsFromOutput(provider testBuildProvider, file string) TestingBuildParams {
233	p, searchedOutputs := maybeBuildParamsFromOutput(provider, file)
234	if p.Rule == nil {
235		panic(fmt.Errorf("couldn't find output %q.\nall outputs: %v",
236			file, searchedOutputs))
237	}
238	return p
239}
240
241func allOutputs(provider testBuildProvider) []string {
242	var outputFullPaths []string
243	for _, p := range provider.BuildParamsForTests() {
244		outputs := append(WritablePaths(nil), p.Outputs...)
245		outputs = append(outputs, p.ImplicitOutputs...)
246		if p.Output != nil {
247			outputs = append(outputs, p.Output)
248		}
249		outputFullPaths = append(outputFullPaths, outputs.Strings()...)
250	}
251	return outputFullPaths
252}
253
254// TestingModule is wrapper around an android.Module that provides methods to find information about individual
255// ctx.Build parameters for verification in tests.
256type TestingModule struct {
257	module Module
258}
259
260// Module returns the Module wrapped by the TestingModule.
261func (m TestingModule) Module() Module {
262	return m.module
263}
264
265// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Returns an empty
266// BuildParams if no rule is found.
267func (m TestingModule) MaybeRule(rule string) TestingBuildParams {
268	return maybeBuildParamsFromRule(m.module, rule)
269}
270
271// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Panics if no rule is found.
272func (m TestingModule) Rule(rule string) TestingBuildParams {
273	return buildParamsFromRule(m.module, rule)
274}
275
276// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string.  Returns an empty
277// BuildParams if no rule is found.
278func (m TestingModule) MaybeDescription(desc string) TestingBuildParams {
279	return maybeBuildParamsFromDescription(m.module, desc)
280}
281
282// Description finds a call to ctx.Build with BuildParams.Description set to a the given string.  Panics if no rule is
283// found.
284func (m TestingModule) Description(desc string) TestingBuildParams {
285	return buildParamsFromDescription(m.module, desc)
286}
287
288// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
289// value matches the provided string.  Returns an empty BuildParams if no rule is found.
290func (m TestingModule) MaybeOutput(file string) TestingBuildParams {
291	p, _ := maybeBuildParamsFromOutput(m.module, file)
292	return p
293}
294
295// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
296// value matches the provided string.  Panics if no rule is found.
297func (m TestingModule) Output(file string) TestingBuildParams {
298	return buildParamsFromOutput(m.module, file)
299}
300
301// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
302func (m TestingModule) AllOutputs() []string {
303	return allOutputs(m.module)
304}
305
306// TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual
307// ctx.Build parameters for verification in tests.
308type TestingSingleton struct {
309	singleton Singleton
310	provider  testBuildProvider
311}
312
313// Singleton returns the Singleton wrapped by the TestingSingleton.
314func (s TestingSingleton) Singleton() Singleton {
315	return s.singleton
316}
317
318// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Returns an empty
319// BuildParams if no rule is found.
320func (s TestingSingleton) MaybeRule(rule string) TestingBuildParams {
321	return maybeBuildParamsFromRule(s.provider, rule)
322}
323
324// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Panics if no rule is found.
325func (s TestingSingleton) Rule(rule string) TestingBuildParams {
326	return buildParamsFromRule(s.provider, rule)
327}
328
329// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string.  Returns an empty
330// BuildParams if no rule is found.
331func (s TestingSingleton) MaybeDescription(desc string) TestingBuildParams {
332	return maybeBuildParamsFromDescription(s.provider, desc)
333}
334
335// Description finds a call to ctx.Build with BuildParams.Description set to a the given string.  Panics if no rule is
336// found.
337func (s TestingSingleton) Description(desc string) TestingBuildParams {
338	return buildParamsFromDescription(s.provider, desc)
339}
340
341// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
342// value matches the provided string.  Returns an empty BuildParams if no rule is found.
343func (s TestingSingleton) MaybeOutput(file string) TestingBuildParams {
344	p, _ := maybeBuildParamsFromOutput(s.provider, file)
345	return p
346}
347
348// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
349// value matches the provided string.  Panics if no rule is found.
350func (s TestingSingleton) Output(file string) TestingBuildParams {
351	return buildParamsFromOutput(s.provider, file)
352}
353
354// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
355func (s TestingSingleton) AllOutputs() []string {
356	return allOutputs(s.provider)
357}
358
359func FailIfErrored(t *testing.T, errs []error) {
360	t.Helper()
361	if len(errs) > 0 {
362		for _, err := range errs {
363			t.Error(err)
364		}
365		t.FailNow()
366	}
367}
368
369func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) {
370	t.Helper()
371
372	matcher, err := regexp.Compile(pattern)
373	if err != nil {
374		t.Errorf("failed to compile regular expression %q because %s", pattern, err)
375	}
376
377	found := false
378	for _, err := range errs {
379		if matcher.FindStringIndex(err.Error()) != nil {
380			found = true
381			break
382		}
383	}
384	if !found {
385		t.Errorf("missing the expected error %q (checked %d error(s))", pattern, len(errs))
386		for i, err := range errs {
387			t.Errorf("errs[%d] = %s", i, err)
388		}
389	}
390}
391
392func CheckErrorsAgainstExpectations(t *testing.T, errs []error, expectedErrorPatterns []string) {
393	t.Helper()
394
395	if expectedErrorPatterns == nil {
396		FailIfErrored(t, errs)
397	} else {
398		for _, expectedError := range expectedErrorPatterns {
399			FailIfNoMatchingErrors(t, expectedError, errs)
400		}
401		if len(errs) > len(expectedErrorPatterns) {
402			t.Errorf("additional errors found, expected %d, found %d",
403				len(expectedErrorPatterns), len(errs))
404			for i, expectedError := range expectedErrorPatterns {
405				t.Errorf("expectedErrors[%d] = %s", i, expectedError)
406			}
407			for i, err := range errs {
408				t.Errorf("errs[%d] = %s", i, err)
409			}
410		}
411	}
412
413}
414
415func SetInMakeForTests(config Config) {
416	config.inMake = true
417}
418
419func AndroidMkEntriesForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) []AndroidMkEntries {
420	var p AndroidMkEntriesProvider
421	var ok bool
422	if p, ok = mod.(AndroidMkEntriesProvider); !ok {
423		t.Errorf("module does not implement AndroidMkEntriesProvider: " + mod.Name())
424	}
425
426	entriesList := p.AndroidMkEntries()
427	for i, _ := range entriesList {
428		entriesList[i].fillInEntries(config, bpPath, mod)
429	}
430	return entriesList
431}
432
433func AndroidMkDataForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) AndroidMkData {
434	var p AndroidMkDataProvider
435	var ok bool
436	if p, ok = mod.(AndroidMkDataProvider); !ok {
437		t.Errorf("module does not implement AndroidMkDataProvider: " + mod.Name())
438	}
439	data := p.AndroidMk()
440	data.fillInData(config, bpPath, mod)
441	return data
442}
443
444// Normalize the path for testing.
445//
446// If the path is relative to the build directory then return the relative path
447// to avoid tests having to deal with the dynamically generated build directory.
448//
449// Otherwise, return the supplied path as it is almost certainly a source path
450// that is relative to the root of the source tree.
451//
452// The build and source paths should be distinguishable based on their contents.
453func NormalizePathForTesting(path Path) string {
454	p := path.String()
455	if w, ok := path.(WritablePath); ok {
456		rel, err := filepath.Rel(w.buildDir(), p)
457		if err != nil {
458			panic(err)
459		}
460		return rel
461	}
462	return p
463}
464
465func NormalizePathsForTesting(paths Paths) []string {
466	var result []string
467	for _, path := range paths {
468		relative := NormalizePathForTesting(path)
469		result = append(result, relative)
470	}
471	return result
472}
473