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 "bytes" 19 "fmt" 20 "path/filepath" 21 "regexp" 22 "sort" 23 "strings" 24 "sync" 25 "testing" 26 27 mkparser "android/soong/androidmk/parser" 28 29 "github.com/google/blueprint" 30 "github.com/google/blueprint/proptools" 31) 32 33func newTestContextForFixture(config Config) *TestContext { 34 ctx := &TestContext{ 35 Context: &Context{blueprint.NewContext(), config}, 36 } 37 38 ctx.postDeps = append(ctx.postDeps, registerPathDepsMutator) 39 40 ctx.SetFs(ctx.config.fs) 41 if ctx.config.mockBpList != "" { 42 ctx.SetModuleListFile(ctx.config.mockBpList) 43 } 44 45 return ctx 46} 47 48func NewTestContext(config Config) *TestContext { 49 ctx := newTestContextForFixture(config) 50 51 nameResolver := NewNameResolver(config) 52 ctx.NameResolver = nameResolver 53 ctx.SetNameInterface(nameResolver) 54 55 return ctx 56} 57 58var PrepareForTestWithArchMutator = GroupFixturePreparers( 59 // Configure architecture targets in the fixture config. 60 FixtureModifyConfig(modifyTestConfigToSupportArchMutator), 61 62 // Add the arch mutator to the context. 63 FixtureRegisterWithContext(func(ctx RegistrationContext) { 64 ctx.PreDepsMutators(registerArchMutator) 65 }), 66) 67 68var PrepareForTestWithDefaults = FixtureRegisterWithContext(func(ctx RegistrationContext) { 69 ctx.PreArchMutators(RegisterDefaultsPreArchMutators) 70}) 71 72var PrepareForTestWithComponentsMutator = FixtureRegisterWithContext(func(ctx RegistrationContext) { 73 ctx.PreArchMutators(RegisterComponentsMutator) 74}) 75 76var PrepareForTestWithPrebuilts = FixtureRegisterWithContext(RegisterPrebuiltMutators) 77 78var PrepareForTestWithOverrides = FixtureRegisterWithContext(func(ctx RegistrationContext) { 79 ctx.PostDepsMutators(RegisterOverridePostDepsMutators) 80}) 81 82var PrepareForTestWithLicenses = GroupFixturePreparers( 83 FixtureRegisterWithContext(RegisterLicenseKindBuildComponents), 84 FixtureRegisterWithContext(RegisterLicenseBuildComponents), 85 FixtureRegisterWithContext(registerLicenseMutators), 86) 87 88var PrepareForTestWithGenNotice = FixtureRegisterWithContext(RegisterGenNoticeBuildComponents) 89 90func registerLicenseMutators(ctx RegistrationContext) { 91 ctx.PreArchMutators(RegisterLicensesPackageMapper) 92 ctx.PreArchMutators(RegisterLicensesPropertyGatherer) 93 ctx.PostDepsMutators(RegisterLicensesDependencyChecker) 94} 95 96var PrepareForTestWithLicenseDefaultModules = GroupFixturePreparers( 97 FixtureAddTextFile("build/soong/licenses/Android.bp", ` 98 license { 99 name: "Android-Apache-2.0", 100 package_name: "Android", 101 license_kinds: ["SPDX-license-identifier-Apache-2.0"], 102 copyright_notice: "Copyright (C) The Android Open Source Project", 103 license_text: ["LICENSE"], 104 } 105 106 license_kind { 107 name: "SPDX-license-identifier-Apache-2.0", 108 conditions: ["notice"], 109 url: "https://spdx.org/licenses/Apache-2.0.html", 110 } 111 112 license_kind { 113 name: "legacy_unencumbered", 114 conditions: ["unencumbered"], 115 } 116 `), 117 FixtureAddFile("build/soong/licenses/LICENSE", nil), 118) 119 120var PrepareForTestWithNamespace = FixtureRegisterWithContext(func(ctx RegistrationContext) { 121 registerNamespaceBuildComponents(ctx) 122 ctx.PreArchMutators(RegisterNamespaceMutator) 123}) 124 125var PrepareForTestWithMakevars = FixtureRegisterWithContext(func(ctx RegistrationContext) { 126 ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc) 127}) 128 129// Test fixture preparer that will register most java build components. 130// 131// Singletons and mutators should only be added here if they are needed for a majority of java 132// module types, otherwise they should be added under a separate preparer to allow them to be 133// selected only when needed to reduce test execution time. 134// 135// Module types do not have much of an overhead unless they are used so this should include as many 136// module types as possible. The exceptions are those module types that require mutators and/or 137// singletons in order to function in which case they should be kept together in a separate 138// preparer. 139// 140// The mutators in this group were chosen because they are needed by the vast majority of tests. 141var PrepareForTestWithAndroidBuildComponents = GroupFixturePreparers( 142 // Sorted alphabetically as the actual order does not matter as tests automatically enforce the 143 // correct order. 144 PrepareForTestWithArchMutator, 145 PrepareForTestWithComponentsMutator, 146 PrepareForTestWithDefaults, 147 PrepareForTestWithFilegroup, 148 PrepareForTestWithOverrides, 149 PrepareForTestWithPackageModule, 150 PrepareForTestWithPrebuilts, 151 PrepareForTestWithVisibility, 152) 153 154// Prepares an integration test with all build components from the android package. 155// 156// This should only be used by tests that want to run with as much of the build enabled as possible. 157var PrepareForIntegrationTestWithAndroid = GroupFixturePreparers( 158 PrepareForTestWithAndroidBuildComponents, 159) 160 161// Prepares a test that may be missing dependencies by setting allow_missing_dependencies to 162// true. 163var PrepareForTestWithAllowMissingDependencies = GroupFixturePreparers( 164 FixtureModifyProductVariables(func(variables FixtureProductVariables) { 165 variables.Allow_missing_dependencies = proptools.BoolPtr(true) 166 }), 167 FixtureModifyContext(func(ctx *TestContext) { 168 ctx.SetAllowMissingDependencies(true) 169 }), 170) 171 172// Prepares a test that disallows non-existent paths. 173var PrepareForTestDisallowNonExistentPaths = FixtureModifyConfig(func(config Config) { 174 config.TestAllowNonExistentPaths = false 175}) 176 177func NewTestArchContext(config Config) *TestContext { 178 ctx := NewTestContext(config) 179 ctx.preDeps = append(ctx.preDeps, registerArchMutator) 180 return ctx 181} 182 183type TestContext struct { 184 *Context 185 preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc 186 NameResolver *NameResolver 187 188 // The list of singletons registered for the test. 189 singletons sortableComponents 190 191 // The order in which the mutators and singletons will be run in this test 192 // context; for debugging. 193 mutatorOrder, singletonOrder []string 194} 195 196func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) { 197 ctx.preArch = append(ctx.preArch, f) 198} 199 200func (ctx *TestContext) HardCodedPreArchMutators(f RegisterMutatorFunc) { 201 // Register mutator function as normal for testing. 202 ctx.PreArchMutators(f) 203} 204 205func (ctx *TestContext) moduleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) { 206 return ctx.Context.ModuleProvider(m, p) 207} 208 209func (ctx *TestContext) PreDepsMutators(f RegisterMutatorFunc) { 210 ctx.preDeps = append(ctx.preDeps, f) 211} 212 213func (ctx *TestContext) PostDepsMutators(f RegisterMutatorFunc) { 214 ctx.postDeps = append(ctx.postDeps, f) 215} 216 217func (ctx *TestContext) FinalDepsMutators(f RegisterMutatorFunc) { 218 ctx.finalDeps = append(ctx.finalDeps, f) 219} 220 221func (ctx *TestContext) OtherModuleProviderAdaptor() OtherModuleProviderContext { 222 return NewOtherModuleProviderAdaptor(func(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) { 223 return ctx.moduleProvider(module, provider) 224 }) 225} 226 227// registeredComponentOrder defines the order in which a sortableComponent type is registered at 228// runtime and provides support for reordering the components registered for a test in the same 229// way. 230type registeredComponentOrder struct { 231 // The name of the component type, used for error messages. 232 componentType string 233 234 // The names of the registered components in the order in which they were registered. 235 namesInOrder []string 236 237 // Maps from the component name to its position in the runtime ordering. 238 namesToIndex map[string]int 239 240 // A function that defines the order between two named components that can be used to sort a slice 241 // of component names into the same order as they appear in namesInOrder. 242 less func(string, string) bool 243} 244 245// registeredComponentOrderFromExistingOrder takes an existing slice of sortableComponents and 246// creates a registeredComponentOrder that contains a less function that can be used to sort a 247// subset of that list of names so it is in the same order as the original sortableComponents. 248func registeredComponentOrderFromExistingOrder(componentType string, existingOrder sortableComponents) registeredComponentOrder { 249 // Only the names from the existing order are needed for this so create a list of component names 250 // in the correct order. 251 namesInOrder := componentsToNames(existingOrder) 252 253 // Populate the map from name to position in the list. 254 nameToIndex := make(map[string]int) 255 for i, n := range namesInOrder { 256 nameToIndex[n] = i 257 } 258 259 // A function to use to map from a name to an index in the original order. 260 indexOf := func(name string) int { 261 index, ok := nameToIndex[name] 262 if !ok { 263 // Should never happen as tests that use components that are not known at runtime do not sort 264 // so should never use this function. 265 panic(fmt.Errorf("internal error: unknown %s %q should be one of %s", componentType, name, strings.Join(namesInOrder, ", "))) 266 } 267 return index 268 } 269 270 // The less function. 271 less := func(n1, n2 string) bool { 272 i1 := indexOf(n1) 273 i2 := indexOf(n2) 274 return i1 < i2 275 } 276 277 return registeredComponentOrder{ 278 componentType: componentType, 279 namesInOrder: namesInOrder, 280 namesToIndex: nameToIndex, 281 less: less, 282 } 283} 284 285// componentsToNames maps from the slice of components to a slice of their names. 286func componentsToNames(components sortableComponents) []string { 287 names := make([]string, len(components)) 288 for i, c := range components { 289 names[i] = c.componentName() 290 } 291 return names 292} 293 294// enforceOrdering enforces the supplied components are in the same order as is defined in this 295// object. 296// 297// If the supplied components contains any components that are not registered at runtime, i.e. test 298// specific components, then it is impossible to sort them into an order that both matches the 299// runtime and also preserves the implicit ordering defined in the test. In that case it will not 300// sort the components, instead it will just check that the components are in the correct order. 301// 302// Otherwise, this will sort the supplied components in place. 303func (o *registeredComponentOrder) enforceOrdering(components sortableComponents) { 304 // Check to see if the list of components contains any components that are 305 // not registered at runtime. 306 var unknownComponents []string 307 testOrder := componentsToNames(components) 308 for _, name := range testOrder { 309 if _, ok := o.namesToIndex[name]; !ok { 310 unknownComponents = append(unknownComponents, name) 311 break 312 } 313 } 314 315 // If the slice contains some unknown components then it is not possible to 316 // sort them into an order that matches the runtime while also preserving the 317 // order expected from the test, so in that case don't sort just check that 318 // the order of the known mutators does match. 319 if len(unknownComponents) > 0 { 320 // Check order. 321 o.checkTestOrder(testOrder, unknownComponents) 322 } else { 323 // Sort the components. 324 sort.Slice(components, func(i, j int) bool { 325 n1 := components[i].componentName() 326 n2 := components[j].componentName() 327 return o.less(n1, n2) 328 }) 329 } 330} 331 332// checkTestOrder checks that the supplied testOrder matches the one defined by this object, 333// panicking if it does not. 334func (o *registeredComponentOrder) checkTestOrder(testOrder []string, unknownComponents []string) { 335 lastMatchingTest := -1 336 matchCount := 0 337 // Take a copy of the runtime order as it is modified during the comparison. 338 runtimeOrder := append([]string(nil), o.namesInOrder...) 339 componentType := o.componentType 340 for i, j := 0, 0; i < len(testOrder) && j < len(runtimeOrder); { 341 test := testOrder[i] 342 runtime := runtimeOrder[j] 343 344 if test == runtime { 345 testOrder[i] = test + fmt.Sprintf(" <-- matched with runtime %s %d", componentType, j) 346 runtimeOrder[j] = runtime + fmt.Sprintf(" <-- matched with test %s %d", componentType, i) 347 lastMatchingTest = i 348 i += 1 349 j += 1 350 matchCount += 1 351 } else if _, ok := o.namesToIndex[test]; !ok { 352 // The test component is not registered globally so assume it is the correct place, treat it 353 // as having matched and skip it. 354 i += 1 355 matchCount += 1 356 } else { 357 // Assume that the test list is in the same order as the runtime list but the runtime list 358 // contains some components that are not present in the tests. So, skip the runtime component 359 // to try and find the next one that matches the current test component. 360 j += 1 361 } 362 } 363 364 // If every item in the test order was either test specific or matched one in the runtime then 365 // it is in the correct order. Otherwise, it was not so fail. 366 if matchCount != len(testOrder) { 367 // The test component names were not all matched with a runtime component name so there must 368 // either be a component present in the test that is not present in the runtime or they must be 369 // in the wrong order. 370 testOrder[lastMatchingTest+1] = testOrder[lastMatchingTest+1] + " <--- unmatched" 371 panic(fmt.Errorf("the tests uses test specific components %q and so cannot be automatically sorted."+ 372 " Unfortunately it uses %s components in the wrong order.\n"+ 373 "test order:\n %s\n"+ 374 "runtime order\n %s\n", 375 SortedUniqueStrings(unknownComponents), 376 componentType, 377 strings.Join(testOrder, "\n "), 378 strings.Join(runtimeOrder, "\n "))) 379 } 380} 381 382// registrationSorter encapsulates the information needed to ensure that the test mutators are 383// registered, and thereby executed, in the same order as they are at runtime. 384// 385// It MUST be populated lazily AFTER all package initialization has been done otherwise it will 386// only define the order for a subset of all the registered build components that are available for 387// the packages being tested. 388// 389// e.g if this is initialized during say the cc package initialization then any tests run in the 390// java package will not sort build components registered by the java package's init() functions. 391type registrationSorter struct { 392 // Used to ensure that this is only created once. 393 once sync.Once 394 395 // The order of mutators 396 mutatorOrder registeredComponentOrder 397 398 // The order of singletons 399 singletonOrder registeredComponentOrder 400} 401 402// populate initializes this structure from globally registered build components. 403// 404// Only the first call has any effect. 405func (s *registrationSorter) populate() { 406 s.once.Do(func() { 407 // Created an ordering from the globally registered mutators. 408 globallyRegisteredMutators := collateGloballyRegisteredMutators() 409 s.mutatorOrder = registeredComponentOrderFromExistingOrder("mutator", globallyRegisteredMutators) 410 411 // Create an ordering from the globally registered singletons. 412 globallyRegisteredSingletons := collateGloballyRegisteredSingletons() 413 s.singletonOrder = registeredComponentOrderFromExistingOrder("singleton", globallyRegisteredSingletons) 414 }) 415} 416 417// Provides support for enforcing the same order in which build components are registered globally 418// to the order in which they are registered during tests. 419// 420// MUST only be accessed via the globallyRegisteredComponentsOrder func. 421var globalRegistrationSorter registrationSorter 422 423// globallyRegisteredComponentsOrder returns the globalRegistrationSorter after ensuring it is 424// correctly populated. 425func globallyRegisteredComponentsOrder() *registrationSorter { 426 globalRegistrationSorter.populate() 427 return &globalRegistrationSorter 428} 429 430func (ctx *TestContext) Register() { 431 globalOrder := globallyRegisteredComponentsOrder() 432 433 mutators := collateRegisteredMutators(ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps) 434 // Ensure that the mutators used in the test are in the same order as they are used at runtime. 435 globalOrder.mutatorOrder.enforceOrdering(mutators) 436 mutators.registerAll(ctx.Context) 437 438 // Ensure that the singletons used in the test are in the same order as they are used at runtime. 439 globalOrder.singletonOrder.enforceOrdering(ctx.singletons) 440 ctx.singletons.registerAll(ctx.Context) 441 442 // Save the sorted components order away to make them easy to access while debugging. 443 ctx.mutatorOrder = componentsToNames(mutators) 444 ctx.singletonOrder = componentsToNames(singletons) 445} 446 447func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) { 448 // This function adapts the old style ParseFileList calls that are spread throughout the tests 449 // to the new style that takes a config. 450 return ctx.Context.ParseFileList(rootDir, filePaths, ctx.config) 451} 452 453func (ctx *TestContext) ParseBlueprintsFiles(rootDir string) (deps []string, errs []error) { 454 // This function adapts the old style ParseBlueprintsFiles calls that are spread throughout the 455 // tests to the new style that takes a config. 456 return ctx.Context.ParseBlueprintsFiles(rootDir, ctx.config) 457} 458 459func (ctx *TestContext) RegisterModuleType(name string, factory ModuleFactory) { 460 ctx.Context.RegisterModuleType(name, ModuleFactoryAdaptor(factory)) 461} 462 463func (ctx *TestContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) { 464 s, m := SingletonModuleFactoryAdaptor(name, factory) 465 ctx.RegisterSingletonType(name, s) 466 ctx.RegisterModuleType(name, m) 467} 468 469func (ctx *TestContext) RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory) { 470 s, m := SingletonModuleFactoryAdaptor(name, factory) 471 ctx.RegisterParallelSingletonType(name, s) 472 ctx.RegisterModuleType(name, m) 473} 474 475func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) { 476 ctx.singletons = append(ctx.singletons, newSingleton(name, factory, false)) 477} 478 479func (ctx *TestContext) RegisterParallelSingletonType(name string, factory SingletonFactory) { 480 ctx.singletons = append(ctx.singletons, newSingleton(name, factory, true)) 481} 482 483// ModuleVariantForTests selects a specific variant of the module with the given 484// name by matching the variations map against the variations of each module 485// variant. A module variant matches the map if every variation that exists in 486// both have the same value. Both the module and the map are allowed to have 487// extra variations that the other doesn't have. Panics if not exactly one 488// module variant matches. 489func (ctx *TestContext) ModuleVariantForTests(name string, matchVariations map[string]string) TestingModule { 490 modules := []Module{} 491 ctx.VisitAllModules(func(m blueprint.Module) { 492 if ctx.ModuleName(m) == name { 493 am := m.(Module) 494 amMut := am.base().commonProperties.DebugMutators 495 amVar := am.base().commonProperties.DebugVariations 496 matched := true 497 for i, mut := range amMut { 498 if wantedVar, found := matchVariations[mut]; found && amVar[i] != wantedVar { 499 matched = false 500 break 501 } 502 } 503 if matched { 504 modules = append(modules, am) 505 } 506 } 507 }) 508 509 if len(modules) == 0 { 510 // Show all the modules or module variants that do exist. 511 var allModuleNames []string 512 var allVariants []string 513 ctx.VisitAllModules(func(m blueprint.Module) { 514 allModuleNames = append(allModuleNames, ctx.ModuleName(m)) 515 if ctx.ModuleName(m) == name { 516 allVariants = append(allVariants, m.(Module).String()) 517 } 518 }) 519 520 if len(allVariants) == 0 { 521 panic(fmt.Errorf("failed to find module %q. All modules:\n %s", 522 name, strings.Join(SortedUniqueStrings(allModuleNames), "\n "))) 523 } else { 524 sort.Strings(allVariants) 525 panic(fmt.Errorf("failed to find module %q matching %v. All variants:\n %s", 526 name, matchVariations, strings.Join(allVariants, "\n "))) 527 } 528 } 529 530 if len(modules) > 1 { 531 moduleStrings := []string{} 532 for _, m := range modules { 533 moduleStrings = append(moduleStrings, m.String()) 534 } 535 sort.Strings(moduleStrings) 536 panic(fmt.Errorf("module %q has more than one variant that match %v:\n %s", 537 name, matchVariations, strings.Join(moduleStrings, "\n "))) 538 } 539 540 return newTestingModule(ctx.config, modules[0]) 541} 542 543func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule { 544 var module Module 545 ctx.VisitAllModules(func(m blueprint.Module) { 546 if ctx.ModuleName(m) == name && ctx.ModuleSubDir(m) == variant { 547 module = m.(Module) 548 } 549 }) 550 551 if module == nil { 552 // find all the modules that do exist 553 var allModuleNames []string 554 var allVariants []string 555 ctx.VisitAllModules(func(m blueprint.Module) { 556 allModuleNames = append(allModuleNames, ctx.ModuleName(m)) 557 if ctx.ModuleName(m) == name { 558 allVariants = append(allVariants, ctx.ModuleSubDir(m)) 559 } 560 }) 561 sort.Strings(allVariants) 562 563 if len(allVariants) == 0 { 564 panic(fmt.Errorf("failed to find module %q. All modules:\n %s", 565 name, strings.Join(SortedUniqueStrings(allModuleNames), "\n "))) 566 } else { 567 panic(fmt.Errorf("failed to find module %q variant %q. All variants:\n %s", 568 name, variant, strings.Join(allVariants, "\n "))) 569 } 570 } 571 572 return newTestingModule(ctx.config, module) 573} 574 575func (ctx *TestContext) ModuleVariantsForTests(name string) []string { 576 var variants []string 577 ctx.VisitAllModules(func(m blueprint.Module) { 578 if ctx.ModuleName(m) == name { 579 variants = append(variants, ctx.ModuleSubDir(m)) 580 } 581 }) 582 return variants 583} 584 585// SingletonForTests returns a TestingSingleton for the singleton registered with the given name. 586func (ctx *TestContext) SingletonForTests(name string) TestingSingleton { 587 allSingletonNames := []string{} 588 for _, s := range ctx.Singletons() { 589 n := ctx.SingletonName(s) 590 if n == name { 591 return TestingSingleton{ 592 baseTestingComponent: newBaseTestingComponent(ctx.config, s.(testBuildProvider)), 593 singleton: s.(*singletonAdaptor).Singleton, 594 } 595 } 596 allSingletonNames = append(allSingletonNames, n) 597 } 598 599 panic(fmt.Errorf("failed to find singleton %q."+ 600 "\nall singletons: %v", name, allSingletonNames)) 601} 602 603type InstallMakeRule struct { 604 Target string 605 Deps []string 606 OrderOnlyDeps []string 607} 608 609func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []InstallMakeRule { 610 var rules []InstallMakeRule 611 for _, node := range nodes { 612 if mkParserRule, ok := node.(*mkparser.Rule); ok { 613 var rule InstallMakeRule 614 615 if targets := mkParserRule.Target.Words(); len(targets) == 0 { 616 t.Fatalf("no targets for rule %s", mkParserRule.Dump()) 617 } else if len(targets) > 1 { 618 t.Fatalf("unsupported multiple targets for rule %s", mkParserRule.Dump()) 619 } else if !targets[0].Const() { 620 t.Fatalf("unsupported non-const target for rule %s", mkParserRule.Dump()) 621 } else { 622 rule.Target = normalizeStringRelativeToTop(config, targets[0].Value(nil)) 623 } 624 625 prereqList := &rule.Deps 626 for _, prereq := range mkParserRule.Prerequisites.Words() { 627 if !prereq.Const() { 628 t.Fatalf("unsupported non-const prerequisite for rule %s", mkParserRule.Dump()) 629 } 630 631 if prereq.Value(nil) == "|" { 632 prereqList = &rule.OrderOnlyDeps 633 continue 634 } 635 636 *prereqList = append(*prereqList, normalizeStringRelativeToTop(config, prereq.Value(nil))) 637 } 638 639 rules = append(rules, rule) 640 } 641 } 642 643 return rules 644} 645 646func (ctx *TestContext) InstallMakeRulesForTesting(t *testing.T) []InstallMakeRule { 647 installs := ctx.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).installsForTesting 648 buf := bytes.NewBuffer(append([]byte(nil), installs...)) 649 parser := mkparser.NewParser("makevars", buf) 650 651 nodes, errs := parser.Parse() 652 if len(errs) > 0 { 653 t.Fatalf("error parsing install rules: %s", errs[0]) 654 } 655 656 return parseMkRules(t, ctx.config, nodes) 657} 658 659// MakeVarVariable provides access to make vars that will be written by the makeVarsSingleton 660type MakeVarVariable interface { 661 // Name is the name of the variable. 662 Name() string 663 664 // Value is the value of the variable. 665 Value() string 666} 667 668func (v makeVarsVariable) Name() string { 669 return v.name 670} 671 672func (v makeVarsVariable) Value() string { 673 return v.value 674} 675 676// PrepareForTestAccessingMakeVars sets up the test so that MakeVarsForTesting will work. 677var PrepareForTestAccessingMakeVars = GroupFixturePreparers( 678 PrepareForTestWithAndroidMk, 679 PrepareForTestWithMakevars, 680) 681 682// MakeVarsForTesting returns a filtered list of MakeVarVariable objects that represent the 683// variables that will be written out. 684// 685// It is necessary to use PrepareForTestAccessingMakeVars in tests that want to call this function. 686// Along with any other preparers needed to add the make vars. 687func (ctx *TestContext) MakeVarsForTesting(filter func(variable MakeVarVariable) bool) []MakeVarVariable { 688 vars := ctx.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).varsForTesting 689 result := make([]MakeVarVariable, 0, len(vars)) 690 for _, v := range vars { 691 if filter(v) { 692 result = append(result, v) 693 } 694 } 695 696 return result 697} 698 699func (ctx *TestContext) Config() Config { 700 return ctx.config 701} 702 703type testBuildProvider interface { 704 BuildParamsForTests() []BuildParams 705 RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams 706} 707 708type TestingBuildParams struct { 709 BuildParams 710 RuleParams blueprint.RuleParams 711 712 config Config 713} 714 715// RelativeToTop creates a new instance of this which has had any usages of the current test's 716// temporary and test specific build directory replaced with a path relative to the notional top. 717// 718// The parts of this structure which are changed are: 719// * BuildParams 720// - Args 721// - All Path, Paths, WritablePath and WritablePaths fields. 722// 723// * RuleParams 724// - Command 725// - Depfile 726// - Rspfile 727// - RspfileContent 728// - CommandDeps 729// - CommandOrderOnly 730// 731// See PathRelativeToTop for more details. 732// 733// deprecated: this is no longer needed as TestingBuildParams are created in this form. 734func (p TestingBuildParams) RelativeToTop() TestingBuildParams { 735 // If this is not a valid params then just return it back. That will make it easy to use with the 736 // Maybe...() methods. 737 if p.Rule == nil { 738 return p 739 } 740 if p.config.config == nil { 741 return p 742 } 743 // Take a copy of the build params and replace any args that contains test specific temporary 744 // paths with paths relative to the top. 745 bparams := p.BuildParams 746 bparams.Depfile = normalizeWritablePathRelativeToTop(bparams.Depfile) 747 bparams.Output = normalizeWritablePathRelativeToTop(bparams.Output) 748 bparams.Outputs = bparams.Outputs.RelativeToTop() 749 bparams.ImplicitOutput = normalizeWritablePathRelativeToTop(bparams.ImplicitOutput) 750 bparams.ImplicitOutputs = bparams.ImplicitOutputs.RelativeToTop() 751 bparams.Input = normalizePathRelativeToTop(bparams.Input) 752 bparams.Inputs = bparams.Inputs.RelativeToTop() 753 bparams.Implicit = normalizePathRelativeToTop(bparams.Implicit) 754 bparams.Implicits = bparams.Implicits.RelativeToTop() 755 bparams.OrderOnly = bparams.OrderOnly.RelativeToTop() 756 bparams.Validation = normalizePathRelativeToTop(bparams.Validation) 757 bparams.Validations = bparams.Validations.RelativeToTop() 758 bparams.Args = normalizeStringMapRelativeToTop(p.config, bparams.Args) 759 760 // Ditto for any fields in the RuleParams. 761 rparams := p.RuleParams 762 rparams.Command = normalizeStringRelativeToTop(p.config, rparams.Command) 763 rparams.Depfile = normalizeStringRelativeToTop(p.config, rparams.Depfile) 764 rparams.Rspfile = normalizeStringRelativeToTop(p.config, rparams.Rspfile) 765 rparams.RspfileContent = normalizeStringRelativeToTop(p.config, rparams.RspfileContent) 766 rparams.CommandDeps = normalizeStringArrayRelativeToTop(p.config, rparams.CommandDeps) 767 rparams.CommandOrderOnly = normalizeStringArrayRelativeToTop(p.config, rparams.CommandOrderOnly) 768 769 return TestingBuildParams{ 770 BuildParams: bparams, 771 RuleParams: rparams, 772 } 773} 774 775func normalizeWritablePathRelativeToTop(path WritablePath) WritablePath { 776 if path == nil { 777 return nil 778 } 779 return path.RelativeToTop().(WritablePath) 780} 781 782func normalizePathRelativeToTop(path Path) Path { 783 if path == nil { 784 return nil 785 } 786 return path.RelativeToTop() 787} 788 789func allOutputs(p BuildParams) []string { 790 outputs := append(WritablePaths(nil), p.Outputs...) 791 outputs = append(outputs, p.ImplicitOutputs...) 792 if p.Output != nil { 793 outputs = append(outputs, p.Output) 794 } 795 return outputs.Strings() 796} 797 798// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms. 799func (p TestingBuildParams) AllOutputs() []string { 800 return allOutputs(p.BuildParams) 801} 802 803// baseTestingComponent provides functionality common to both TestingModule and TestingSingleton. 804type baseTestingComponent struct { 805 config Config 806 provider testBuildProvider 807} 808 809func newBaseTestingComponent(config Config, provider testBuildProvider) baseTestingComponent { 810 return baseTestingComponent{config, provider} 811} 812 813// A function that will normalize a string containing paths, e.g. ninja command, by replacing 814// any references to the test specific temporary build directory that changes with each run to a 815// fixed path relative to a notional top directory. 816// 817// This is similar to StringPathRelativeToTop except that assumes the string is a single path 818// containing at most one instance of the temporary build directory at the start of the path while 819// this assumes that there can be any number at any position. 820func normalizeStringRelativeToTop(config Config, s string) string { 821 // The soongOutDir usually looks something like: /tmp/testFoo2345/001 822 // 823 // Replace any usage of the soongOutDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with 824 // "out/soong". 825 outSoongDir := filepath.Clean(config.soongOutDir) 826 re := regexp.MustCompile(`\Q` + outSoongDir + `\E\b`) 827 s = re.ReplaceAllString(s, "out/soong") 828 829 // Replace any usage of the soongOutDir/.. with out, e.g. replace "/tmp/testFoo2345" with 830 // "out". This must come after the previous replacement otherwise this would replace 831 // "/tmp/testFoo2345/001" with "out/001" instead of "out/soong". 832 outDir := filepath.Dir(outSoongDir) 833 re = regexp.MustCompile(`\Q` + outDir + `\E\b`) 834 s = re.ReplaceAllString(s, "out") 835 836 return s 837} 838 839// normalizeStringArrayRelativeToTop creates a new slice constructed by applying 840// normalizeStringRelativeToTop to each item in the slice. 841func normalizeStringArrayRelativeToTop(config Config, slice []string) []string { 842 newSlice := make([]string, len(slice)) 843 for i, s := range slice { 844 newSlice[i] = normalizeStringRelativeToTop(config, s) 845 } 846 return newSlice 847} 848 849// normalizeStringMapRelativeToTop creates a new map constructed by applying 850// normalizeStringRelativeToTop to each value in the map. 851func normalizeStringMapRelativeToTop(config Config, m map[string]string) map[string]string { 852 newMap := map[string]string{} 853 for k, v := range m { 854 newMap[k] = normalizeStringRelativeToTop(config, v) 855 } 856 return newMap 857} 858 859func (b baseTestingComponent) newTestingBuildParams(bparams BuildParams) TestingBuildParams { 860 return TestingBuildParams{ 861 config: b.config, 862 BuildParams: bparams, 863 RuleParams: b.provider.RuleParamsForTests()[bparams.Rule], 864 }.RelativeToTop() 865} 866 867func (b baseTestingComponent) maybeBuildParamsFromRule(rule string) (TestingBuildParams, []string) { 868 var searchedRules []string 869 buildParams := b.provider.BuildParamsForTests() 870 for _, p := range buildParams { 871 ruleAsString := p.Rule.String() 872 searchedRules = append(searchedRules, ruleAsString) 873 if strings.Contains(ruleAsString, rule) { 874 return b.newTestingBuildParams(p), searchedRules 875 } 876 } 877 return TestingBuildParams{}, searchedRules 878} 879 880func (b baseTestingComponent) buildParamsFromRule(rule string) TestingBuildParams { 881 p, searchRules := b.maybeBuildParamsFromRule(rule) 882 if p.Rule == nil { 883 panic(fmt.Errorf("couldn't find rule %q.\nall rules:\n%s", rule, strings.Join(searchRules, "\n"))) 884 } 885 return p 886} 887 888func (b baseTestingComponent) maybeBuildParamsFromDescription(desc string) (TestingBuildParams, []string) { 889 var searchedDescriptions []string 890 for _, p := range b.provider.BuildParamsForTests() { 891 searchedDescriptions = append(searchedDescriptions, p.Description) 892 if strings.Contains(p.Description, desc) { 893 return b.newTestingBuildParams(p), searchedDescriptions 894 } 895 } 896 return TestingBuildParams{}, searchedDescriptions 897} 898 899func (b baseTestingComponent) buildParamsFromDescription(desc string) TestingBuildParams { 900 p, searchedDescriptions := b.maybeBuildParamsFromDescription(desc) 901 if p.Rule == nil { 902 panic(fmt.Errorf("couldn't find description %q\nall descriptions:\n%s", desc, strings.Join(searchedDescriptions, "\n"))) 903 } 904 return p 905} 906 907func (b baseTestingComponent) maybeBuildParamsFromOutput(file string) (TestingBuildParams, []string) { 908 searchedOutputs := WritablePaths(nil) 909 for _, p := range b.provider.BuildParamsForTests() { 910 outputs := append(WritablePaths(nil), p.Outputs...) 911 outputs = append(outputs, p.ImplicitOutputs...) 912 if p.Output != nil { 913 outputs = append(outputs, p.Output) 914 } 915 for _, f := range outputs { 916 if f.String() == file || f.Rel() == file || PathRelativeToTop(f) == file { 917 return b.newTestingBuildParams(p), nil 918 } 919 searchedOutputs = append(searchedOutputs, f) 920 } 921 } 922 923 formattedOutputs := []string{} 924 for _, f := range searchedOutputs { 925 formattedOutputs = append(formattedOutputs, 926 fmt.Sprintf("%s (rel=%s)", PathRelativeToTop(f), f.Rel())) 927 } 928 929 return TestingBuildParams{}, formattedOutputs 930} 931 932func (b baseTestingComponent) buildParamsFromOutput(file string) TestingBuildParams { 933 p, searchedOutputs := b.maybeBuildParamsFromOutput(file) 934 if p.Rule == nil { 935 panic(fmt.Errorf("couldn't find output %q.\nall outputs:\n %s\n", 936 file, strings.Join(searchedOutputs, "\n "))) 937 } 938 return p 939} 940 941func (b baseTestingComponent) allOutputs() []string { 942 var outputFullPaths []string 943 for _, p := range b.provider.BuildParamsForTests() { 944 outputFullPaths = append(outputFullPaths, allOutputs(p)...) 945 } 946 return outputFullPaths 947} 948 949// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Returns an empty 950// BuildParams if no rule is found. 951func (b baseTestingComponent) MaybeRule(rule string) TestingBuildParams { 952 r, _ := b.maybeBuildParamsFromRule(rule) 953 return r 954} 955 956// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Panics if no rule is found. 957func (b baseTestingComponent) Rule(rule string) TestingBuildParams { 958 return b.buildParamsFromRule(rule) 959} 960 961// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty 962// BuildParams if no rule is found. 963func (b baseTestingComponent) MaybeDescription(desc string) TestingBuildParams { 964 p, _ := b.maybeBuildParamsFromDescription(desc) 965 return p 966} 967 968// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is 969// found. 970func (b baseTestingComponent) Description(desc string) TestingBuildParams { 971 return b.buildParamsFromDescription(desc) 972} 973 974// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel() 975// value matches the provided string. Returns an empty BuildParams if no rule is found. 976func (b baseTestingComponent) MaybeOutput(file string) TestingBuildParams { 977 p, _ := b.maybeBuildParamsFromOutput(file) 978 return p 979} 980 981// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel() 982// value matches the provided string. Panics if no rule is found. 983func (b baseTestingComponent) Output(file string) TestingBuildParams { 984 return b.buildParamsFromOutput(file) 985} 986 987// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms. 988func (b baseTestingComponent) AllOutputs() []string { 989 return b.allOutputs() 990} 991 992// TestingModule is wrapper around an android.Module that provides methods to find information about individual 993// ctx.Build parameters for verification in tests. 994type TestingModule struct { 995 baseTestingComponent 996 module Module 997} 998 999func newTestingModule(config Config, module Module) TestingModule { 1000 return TestingModule{ 1001 newBaseTestingComponent(config, module), 1002 module, 1003 } 1004} 1005 1006// Module returns the Module wrapped by the TestingModule. 1007func (m TestingModule) Module() Module { 1008 return m.module 1009} 1010 1011// VariablesForTestsRelativeToTop returns a copy of the Module.VariablesForTests() with every value 1012// having any temporary build dir usages replaced with paths relative to a notional top. 1013func (m TestingModule) VariablesForTestsRelativeToTop() map[string]string { 1014 return normalizeStringMapRelativeToTop(m.config, m.module.VariablesForTests()) 1015} 1016 1017// OutputFiles first checks if module base outputFiles property has any output 1018// files can be used to return. 1019// If not, it calls OutputFileProducer.OutputFiles on the 1020// encapsulated module, exits the test immediately if there is an error and 1021// otherwise returns the result of calling Paths.RelativeToTop 1022// on the returned Paths. 1023func (m TestingModule) OutputFiles(t *testing.T, tag string) Paths { 1024 // TODO: add non-empty-string tag case and remove OutputFileProducer part 1025 if tag == "" && m.module.base().outputFiles.DefaultOutputFiles != nil { 1026 return m.module.base().outputFiles.DefaultOutputFiles.RelativeToTop() 1027 } 1028 1029 producer, ok := m.module.(OutputFileProducer) 1030 if !ok { 1031 t.Fatalf("%q must implement OutputFileProducer\n", m.module.Name()) 1032 } 1033 paths, err := producer.OutputFiles(tag) 1034 if err != nil { 1035 t.Fatal(err) 1036 } 1037 1038 return paths.RelativeToTop() 1039} 1040 1041// TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual 1042// ctx.Build parameters for verification in tests. 1043type TestingSingleton struct { 1044 baseTestingComponent 1045 singleton Singleton 1046} 1047 1048// Singleton returns the Singleton wrapped by the TestingSingleton. 1049func (s TestingSingleton) Singleton() Singleton { 1050 return s.singleton 1051} 1052 1053func FailIfErrored(t *testing.T, errs []error) { 1054 t.Helper() 1055 if len(errs) > 0 { 1056 for _, err := range errs { 1057 t.Error(err) 1058 } 1059 t.FailNow() 1060 } 1061} 1062 1063// Fail if no errors that matched the regular expression were found. 1064// 1065// Returns true if a matching error was found, false otherwise. 1066func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) bool { 1067 t.Helper() 1068 1069 matcher, err := regexp.Compile(pattern) 1070 if err != nil { 1071 t.Fatalf("failed to compile regular expression %q because %s", pattern, err) 1072 } 1073 1074 found := false 1075 for _, err := range errs { 1076 if matcher.FindStringIndex(err.Error()) != nil { 1077 found = true 1078 break 1079 } 1080 } 1081 if !found { 1082 t.Errorf("could not match the expected error regex %q (checked %d error(s))", pattern, len(errs)) 1083 for i, err := range errs { 1084 t.Errorf("errs[%d] = %q", i, err) 1085 } 1086 } 1087 1088 return found 1089} 1090 1091func CheckErrorsAgainstExpectations(t *testing.T, errs []error, expectedErrorPatterns []string) { 1092 t.Helper() 1093 1094 if expectedErrorPatterns == nil { 1095 FailIfErrored(t, errs) 1096 } else { 1097 for _, expectedError := range expectedErrorPatterns { 1098 FailIfNoMatchingErrors(t, expectedError, errs) 1099 } 1100 if len(errs) > len(expectedErrorPatterns) { 1101 t.Errorf("additional errors found, expected %d, found %d", 1102 len(expectedErrorPatterns), len(errs)) 1103 for i, expectedError := range expectedErrorPatterns { 1104 t.Errorf("expectedErrors[%d] = %s", i, expectedError) 1105 } 1106 for i, err := range errs { 1107 t.Errorf("errs[%d] = %s", i, err) 1108 } 1109 t.FailNow() 1110 } 1111 } 1112} 1113 1114func SetKatiEnabledForTests(config Config) { 1115 config.katiEnabled = true 1116} 1117 1118func SetTrimmedApexEnabledForTests(config Config) { 1119 config.productVariables.TrimmedApex = new(bool) 1120 *config.productVariables.TrimmedApex = true 1121} 1122 1123func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) []AndroidMkEntries { 1124 t.Helper() 1125 var p AndroidMkEntriesProvider 1126 var ok bool 1127 if p, ok = mod.(AndroidMkEntriesProvider); !ok { 1128 t.Errorf("module does not implement AndroidMkEntriesProvider: " + mod.Name()) 1129 } 1130 1131 entriesList := p.AndroidMkEntries() 1132 aconfigUpdateAndroidMkEntries(ctx, mod.(Module), &entriesList) 1133 for i := range entriesList { 1134 entriesList[i].fillInEntries(ctx, mod) 1135 } 1136 return entriesList 1137} 1138 1139func AndroidMkDataForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) AndroidMkData { 1140 t.Helper() 1141 var p AndroidMkDataProvider 1142 var ok bool 1143 if p, ok = mod.(AndroidMkDataProvider); !ok { 1144 t.Fatalf("module does not implement AndroidMkDataProvider: " + mod.Name()) 1145 } 1146 data := p.AndroidMk() 1147 data.fillInData(ctx, mod) 1148 aconfigUpdateAndroidMkData(ctx, mod.(Module), &data) 1149 return data 1150} 1151 1152// Normalize the path for testing. 1153// 1154// If the path is relative to the build directory then return the relative path 1155// to avoid tests having to deal with the dynamically generated build directory. 1156// 1157// Otherwise, return the supplied path as it is almost certainly a source path 1158// that is relative to the root of the source tree. 1159// 1160// The build and source paths should be distinguishable based on their contents. 1161// 1162// deprecated: use PathRelativeToTop instead as it handles make install paths and differentiates 1163// between output and source properly. 1164func NormalizePathForTesting(path Path) string { 1165 if path == nil { 1166 return "<nil path>" 1167 } 1168 p := path.String() 1169 if w, ok := path.(WritablePath); ok { 1170 rel, err := filepath.Rel(w.getSoongOutDir(), p) 1171 if err != nil { 1172 panic(err) 1173 } 1174 return rel 1175 } 1176 return p 1177} 1178 1179// NormalizePathsForTesting creates a slice of strings where each string is the result of applying 1180// NormalizePathForTesting to the corresponding Path in the input slice. 1181// 1182// deprecated: use PathsRelativeToTop instead as it handles make install paths and differentiates 1183// between output and source properly. 1184func NormalizePathsForTesting(paths Paths) []string { 1185 var result []string 1186 for _, path := range paths { 1187 relative := NormalizePathForTesting(path) 1188 result = append(result, relative) 1189 } 1190 return result 1191} 1192 1193// PathRelativeToTop returns a string representation of the path relative to a notional top 1194// directory. 1195// 1196// It return "<nil path>" if the supplied path is nil, otherwise it returns the result of calling 1197// Path.RelativeToTop to obtain a relative Path and then calling Path.String on that to get the 1198// string representation. 1199func PathRelativeToTop(path Path) string { 1200 if path == nil { 1201 return "<nil path>" 1202 } 1203 return path.RelativeToTop().String() 1204} 1205 1206// PathsRelativeToTop creates a slice of strings where each string is the result of applying 1207// PathRelativeToTop to the corresponding Path in the input slice. 1208func PathsRelativeToTop(paths Paths) []string { 1209 var result []string 1210 for _, path := range paths { 1211 relative := PathRelativeToTop(path) 1212 result = append(result, relative) 1213 } 1214 return result 1215} 1216 1217// StringPathRelativeToTop returns a string representation of the path relative to a notional top 1218// directory. 1219// 1220// See Path.RelativeToTop for more details as to what `relative to top` means. 1221// 1222// This is provided for processing paths that have already been converted into a string, e.g. paths 1223// in AndroidMkEntries structures. As a result it needs to be supplied the soong output dir against 1224// which it can try and relativize paths. PathRelativeToTop must be used for process Path objects. 1225func StringPathRelativeToTop(soongOutDir string, path string) string { 1226 ensureTestOnly() 1227 1228 // A relative path must be a source path so leave it as it is. 1229 if !filepath.IsAbs(path) { 1230 return path 1231 } 1232 1233 // Check to see if the path is relative to the soong out dir. 1234 rel, isRel, err := maybeRelErr(soongOutDir, path) 1235 if err != nil { 1236 panic(err) 1237 } 1238 1239 if isRel { 1240 // The path is in the soong out dir so indicate that in the relative path. 1241 return filepath.Join("out/soong", rel) 1242 } 1243 1244 // Check to see if the path is relative to the top level out dir. 1245 outDir := filepath.Dir(soongOutDir) 1246 rel, isRel, err = maybeRelErr(outDir, path) 1247 if err != nil { 1248 panic(err) 1249 } 1250 1251 if isRel { 1252 // The path is in the out dir so indicate that in the relative path. 1253 return filepath.Join("out", rel) 1254 } 1255 1256 // This should never happen. 1257 panic(fmt.Errorf("internal error: absolute path %s is not relative to the out dir %s", path, outDir)) 1258} 1259 1260// StringPathsRelativeToTop creates a slice of strings where each string is the result of applying 1261// StringPathRelativeToTop to the corresponding string path in the input slice. 1262// 1263// This is provided for processing paths that have already been converted into a string, e.g. paths 1264// in AndroidMkEntries structures. As a result it needs to be supplied the soong output dir against 1265// which it can try and relativize paths. PathsRelativeToTop must be used for process Paths objects. 1266func StringPathsRelativeToTop(soongOutDir string, paths []string) []string { 1267 var result []string 1268 for _, path := range paths { 1269 relative := StringPathRelativeToTop(soongOutDir, path) 1270 result = append(result, relative) 1271 } 1272 return result 1273} 1274 1275// StringRelativeToTop will normalize a string containing paths, e.g. ninja command, by replacing 1276// any references to the test specific temporary build directory that changes with each run to a 1277// fixed path relative to a notional top directory. 1278// 1279// This is similar to StringPathRelativeToTop except that assumes the string is a single path 1280// containing at most one instance of the temporary build directory at the start of the path while 1281// this assumes that there can be any number at any position. 1282func StringRelativeToTop(config Config, command string) string { 1283 return normalizeStringRelativeToTop(config, command) 1284} 1285 1286// StringsRelativeToTop will return a new slice such that each item in the new slice is the result 1287// of calling StringRelativeToTop on the corresponding item in the input slice. 1288func StringsRelativeToTop(config Config, command []string) []string { 1289 return normalizeStringArrayRelativeToTop(config, command) 1290} 1291 1292func EnsureListContainsSuffix(t *testing.T, result []string, expected string) { 1293 t.Helper() 1294 if !SuffixInList(result, expected) { 1295 t.Errorf("%q is not found in %v", expected, result) 1296 } 1297} 1298 1299type panickingConfigAndErrorContext struct { 1300 ctx *TestContext 1301} 1302 1303func (ctx *panickingConfigAndErrorContext) OtherModulePropertyErrorf(module Module, property, fmt string, args ...interface{}) { 1304 panic(ctx.ctx.PropertyErrorf(module, property, fmt, args...).Error()) 1305} 1306 1307func (ctx *panickingConfigAndErrorContext) Config() Config { 1308 return ctx.ctx.Config() 1309} 1310 1311func PanickingConfigAndErrorContext(ctx *TestContext) ConfigAndErrorContext { 1312 return &panickingConfigAndErrorContext{ 1313 ctx: ctx, 1314 } 1315} 1316