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 []RegisterMutatorFunc 54 NameResolver *NameResolver 55} 56 57func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) { 58 ctx.preArch = append(ctx.preArch, f) 59} 60 61func (ctx *TestContext) PreDepsMutators(f RegisterMutatorFunc) { 62 ctx.preDeps = append(ctx.preDeps, f) 63} 64 65func (ctx *TestContext) PostDepsMutators(f RegisterMutatorFunc) { 66 ctx.postDeps = append(ctx.postDeps, f) 67} 68 69func (ctx *TestContext) Register() { 70 registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps) 71 72 ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(EnvSingleton)) 73} 74 75func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule { 76 var module Module 77 ctx.VisitAllModules(func(m blueprint.Module) { 78 if ctx.ModuleName(m) == name && ctx.ModuleSubDir(m) == variant { 79 module = m.(Module) 80 } 81 }) 82 83 if module == nil { 84 // find all the modules that do exist 85 allModuleNames := []string{} 86 ctx.VisitAllModules(func(m blueprint.Module) { 87 allModuleNames = append(allModuleNames, m.(Module).Name()+"("+ctx.ModuleSubDir(m)+")") 88 }) 89 90 panic(fmt.Errorf("failed to find module %q variant %q."+ 91 "\nall modules: %v", name, variant, allModuleNames)) 92 } 93 94 return TestingModule{module} 95} 96 97func (ctx *TestContext) ModuleVariantsForTests(name string) []string { 98 var variants []string 99 ctx.VisitAllModules(func(m blueprint.Module) { 100 if ctx.ModuleName(m) == name { 101 variants = append(variants, ctx.ModuleSubDir(m)) 102 } 103 }) 104 return variants 105} 106 107// SingletonForTests returns a TestingSingleton for the singleton registered with the given name. 108func (ctx *TestContext) SingletonForTests(name string) TestingSingleton { 109 allSingletonNames := []string{} 110 for _, s := range ctx.Singletons() { 111 n := ctx.SingletonName(s) 112 if n == name { 113 return TestingSingleton{ 114 singleton: s.(*singletonAdaptor).Singleton, 115 provider: s.(testBuildProvider), 116 } 117 } 118 allSingletonNames = append(allSingletonNames, n) 119 } 120 121 panic(fmt.Errorf("failed to find singleton %q."+ 122 "\nall singletons: %v", name, allSingletonNames)) 123} 124 125// MockFileSystem causes the Context to replace all reads with accesses to the provided map of 126// filenames to contents stored as a byte slice. 127func (ctx *TestContext) MockFileSystem(files map[string][]byte) { 128 // no module list file specified; find every file named Blueprints or Android.bp 129 pathsToParse := []string{} 130 for candidate := range files { 131 base := filepath.Base(candidate) 132 if base == "Blueprints" || base == "Android.bp" { 133 pathsToParse = append(pathsToParse, candidate) 134 } 135 } 136 if len(pathsToParse) < 1 { 137 panic(fmt.Sprintf("No Blueprint or Android.bp files found in mock filesystem: %v\n", files)) 138 } 139 files[blueprint.MockModuleListFile] = []byte(strings.Join(pathsToParse, "\n")) 140 141 ctx.Context.MockFileSystem(files) 142} 143 144type testBuildProvider interface { 145 BuildParamsForTests() []BuildParams 146 RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams 147} 148 149type TestingBuildParams struct { 150 BuildParams 151 RuleParams blueprint.RuleParams 152} 153 154func newTestingBuildParams(provider testBuildProvider, bparams BuildParams) TestingBuildParams { 155 return TestingBuildParams{ 156 BuildParams: bparams, 157 RuleParams: provider.RuleParamsForTests()[bparams.Rule], 158 } 159} 160 161func maybeBuildParamsFromRule(provider testBuildProvider, rule string) TestingBuildParams { 162 for _, p := range provider.BuildParamsForTests() { 163 if strings.Contains(p.Rule.String(), rule) { 164 return newTestingBuildParams(provider, p) 165 } 166 } 167 return TestingBuildParams{} 168} 169 170func buildParamsFromRule(provider testBuildProvider, rule string) TestingBuildParams { 171 p := maybeBuildParamsFromRule(provider, rule) 172 if p.Rule == nil { 173 panic(fmt.Errorf("couldn't find rule %q", rule)) 174 } 175 return p 176} 177 178func maybeBuildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams { 179 for _, p := range provider.BuildParamsForTests() { 180 if p.Description == desc { 181 return newTestingBuildParams(provider, p) 182 } 183 } 184 return TestingBuildParams{} 185} 186 187func buildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams { 188 p := maybeBuildParamsFromDescription(provider, desc) 189 if p.Rule == nil { 190 panic(fmt.Errorf("couldn't find description %q", desc)) 191 } 192 return p 193} 194 195func maybeBuildParamsFromOutput(provider testBuildProvider, file string) (TestingBuildParams, []string) { 196 var searchedOutputs []string 197 for _, p := range provider.BuildParamsForTests() { 198 outputs := append(WritablePaths(nil), p.Outputs...) 199 outputs = append(outputs, p.ImplicitOutputs...) 200 if p.Output != nil { 201 outputs = append(outputs, p.Output) 202 } 203 for _, f := range outputs { 204 if f.String() == file || f.Rel() == file { 205 return newTestingBuildParams(provider, p), nil 206 } 207 searchedOutputs = append(searchedOutputs, f.Rel()) 208 } 209 } 210 return TestingBuildParams{}, searchedOutputs 211} 212 213func buildParamsFromOutput(provider testBuildProvider, file string) TestingBuildParams { 214 p, searchedOutputs := maybeBuildParamsFromOutput(provider, file) 215 if p.Rule == nil { 216 panic(fmt.Errorf("couldn't find output %q.\nall outputs: %v", 217 file, searchedOutputs)) 218 } 219 return p 220} 221 222func allOutputs(provider testBuildProvider) []string { 223 var outputFullPaths []string 224 for _, p := range provider.BuildParamsForTests() { 225 outputs := append(WritablePaths(nil), p.Outputs...) 226 outputs = append(outputs, p.ImplicitOutputs...) 227 if p.Output != nil { 228 outputs = append(outputs, p.Output) 229 } 230 outputFullPaths = append(outputFullPaths, outputs.Strings()...) 231 } 232 return outputFullPaths 233} 234 235// TestingModule is wrapper around an android.Module that provides methods to find information about individual 236// ctx.Build parameters for verification in tests. 237type TestingModule struct { 238 module Module 239} 240 241// Module returns the Module wrapped by the TestingModule. 242func (m TestingModule) Module() Module { 243 return m.module 244} 245 246// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Returns an empty 247// BuildParams if no rule is found. 248func (m TestingModule) MaybeRule(rule string) TestingBuildParams { 249 return maybeBuildParamsFromRule(m.module, rule) 250} 251 252// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Panics if no rule is found. 253func (m TestingModule) Rule(rule string) TestingBuildParams { 254 return buildParamsFromRule(m.module, rule) 255} 256 257// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty 258// BuildParams if no rule is found. 259func (m TestingModule) MaybeDescription(desc string) TestingBuildParams { 260 return maybeBuildParamsFromDescription(m.module, desc) 261} 262 263// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is 264// found. 265func (m TestingModule) Description(desc string) TestingBuildParams { 266 return buildParamsFromDescription(m.module, desc) 267} 268 269// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel() 270// value matches the provided string. Returns an empty BuildParams if no rule is found. 271func (m TestingModule) MaybeOutput(file string) TestingBuildParams { 272 p, _ := maybeBuildParamsFromOutput(m.module, file) 273 return p 274} 275 276// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel() 277// value matches the provided string. Panics if no rule is found. 278func (m TestingModule) Output(file string) TestingBuildParams { 279 return buildParamsFromOutput(m.module, file) 280} 281 282// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms. 283func (m TestingModule) AllOutputs() []string { 284 return allOutputs(m.module) 285} 286 287// TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual 288// ctx.Build parameters for verification in tests. 289type TestingSingleton struct { 290 singleton Singleton 291 provider testBuildProvider 292} 293 294// Singleton returns the Singleton wrapped by the TestingSingleton. 295func (s TestingSingleton) Singleton() Singleton { 296 return s.singleton 297} 298 299// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Returns an empty 300// BuildParams if no rule is found. 301func (s TestingSingleton) MaybeRule(rule string) TestingBuildParams { 302 return maybeBuildParamsFromRule(s.provider, rule) 303} 304 305// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Panics if no rule is found. 306func (s TestingSingleton) Rule(rule string) TestingBuildParams { 307 return buildParamsFromRule(s.provider, rule) 308} 309 310// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty 311// BuildParams if no rule is found. 312func (s TestingSingleton) MaybeDescription(desc string) TestingBuildParams { 313 return maybeBuildParamsFromDescription(s.provider, desc) 314} 315 316// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is 317// found. 318func (s TestingSingleton) Description(desc string) TestingBuildParams { 319 return buildParamsFromDescription(s.provider, desc) 320} 321 322// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel() 323// value matches the provided string. Returns an empty BuildParams if no rule is found. 324func (s TestingSingleton) MaybeOutput(file string) TestingBuildParams { 325 p, _ := maybeBuildParamsFromOutput(s.provider, file) 326 return p 327} 328 329// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel() 330// value matches the provided string. Panics if no rule is found. 331func (s TestingSingleton) Output(file string) TestingBuildParams { 332 return buildParamsFromOutput(s.provider, file) 333} 334 335// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms. 336func (s TestingSingleton) AllOutputs() []string { 337 return allOutputs(s.provider) 338} 339 340func FailIfErrored(t *testing.T, errs []error) { 341 t.Helper() 342 if len(errs) > 0 { 343 for _, err := range errs { 344 t.Error(err) 345 } 346 t.FailNow() 347 } 348} 349 350func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) { 351 t.Helper() 352 353 matcher, err := regexp.Compile(pattern) 354 if err != nil { 355 t.Errorf("failed to compile regular expression %q because %s", pattern, err) 356 } 357 358 found := false 359 for _, err := range errs { 360 if matcher.FindStringIndex(err.Error()) != nil { 361 found = true 362 break 363 } 364 } 365 if !found { 366 t.Errorf("missing the expected error %q (checked %d error(s))", pattern, len(errs)) 367 for i, err := range errs { 368 t.Errorf("errs[%d] = %s", i, err) 369 } 370 } 371} 372