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