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