Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  testing.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package android

import (
 "bytes"
 "fmt"
 "iter"
 "maps"
 "path/filepath"
 "regexp"
 "runtime"
 "slices"
 "sort"
 "strings"
 "sync"
 "testing"

 mkparser "android/soong/androidmk/parser"

 "github.com/google/blueprint"
 "github.com/google/blueprint/proptools"
)

func newTestContextForFixture(config Config) *TestContext {
 ctx := &TestContext{
  Context: &Context{blueprint.NewContext(), config},
 }

 ctx.postDeps = append(ctx.postDeps, registerPathDepsMutator)

 ctx.SetFs(ctx.config.fs)
 if ctx.config.mockBpList != "" {
  ctx.SetModuleListFile(ctx.config.mockBpList)
 }

 return ctx
}

func NewTestContext(config Config) *TestContext {
 ctx := newTestContextForFixture(config)

 nameResolver := NewNameResolver(config)
 ctx.NameResolver = nameResolver
 ctx.SetNameInterface(nameResolver)

 return ctx
}

var PrepareForTestWithArchMutator = GroupFixturePreparers(
 // Configure architecture targets in the fixture config.
 FixtureModifyConfig(modifyTestConfigToSupportArchMutator),

 // Add the arch mutator to the context.
 FixtureRegisterWithContext(func(ctx RegistrationContext) {
  ctx.PreDepsMutators(registerArchMutator)
 }),
 PrepareForTestWithBuildFlag("RELEASE_SOONG_IMAGE_VARIANT_ON_DEMAND", "true"),
)

var PrepareForTestWithDefaults = FixtureRegisterWithContext(func(ctx RegistrationContext) {
 ctx.PrePartialMutators(RegisterDefaultsPreArchMutators)
})

var PrepareForTestWithComponentsMutator = FixtureRegisterWithContext(func(ctx RegistrationContext) {
 ctx.PrePartialMutators(RegisterComponentsMutator)
})

var PrepareForTestWithPrebuilts = FixtureRegisterWithContext(RegisterPrebuiltMutators)

var PrepareForTestWithOverrides = FixtureRegisterWithContext(func(ctx RegistrationContext) {
 ctx.PostDepsMutators(RegisterOverrideDepsPostDepsMutators)
 ctx.PostDepsMutators(RegisterReplaceDepsPostDepsMutators)
})

var PrepareForTestWithLicenses = GroupFixturePreparers(
 FixtureRegisterWithContext(RegisterLicenseKindBuildComponents),
 FixtureRegisterWithContext(RegisterLicenseBuildComponents),
 FixtureRegisterWithContext(registerLicenseMutators),
)

var PrepareForTestWithGenNotice = FixtureRegisterWithContext(RegisterGenNoticeBuildComponents)

func registerLicenseMutators(ctx RegistrationContext) {
 ctx.PreArchMutators(RegisterLicensesPackageMapper)
 ctx.PreArchMutators(RegisterLicensesPropertyGatherer)
}

var PrepareForTestWithLicenseDefaultModules = GroupFixturePreparers(
 FixtureAddTextFile("build/soong/licenses/Android.bp", `
  license {
    name: "Android-Apache-2.0",
    package_name: "Android",
    license_kinds: ["SPDX-license-identifier-Apache-2.0"],
    copyright_notice: "Copyright (C) The Android Open Source Project",
    license_text: ["LICENSE"],
  }

  license_kind {
    name: "SPDX-license-identifier-Apache-2.0",
    conditions: ["notice"],
    url: "https://spdx.org/licenses/Apache-2.0.html",
  }

  license_kind {
    name: "legacy_unencumbered",
    conditions: ["unencumbered"],
  }
 `),
 FixtureAddFile("build/soong/licenses/LICENSE", nil),
)

var PrepareForTestWithNamespace = FixtureRegisterWithContext(func(ctx RegistrationContext) {
 registerNamespaceBuildComponents(ctx)
 ctx.PrePartialMutators(RegisterNamespaceMutator)
})

var PrepareForTestWithMakevars = FixtureRegisterWithContext(func(ctx RegistrationContext) {
 ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
})

var PrepareForTestVintfFragmentModules = FixtureRegisterWithContext(func(ctx RegistrationContext) {
 registerVintfFragmentComponents(ctx)
})

// Test fixture preparer that will register most java build components.
//
// Singletons and mutators should only be added here if they are needed for a majority of java
// module types, otherwise they should be added under a separate preparer to allow them to be
// selected only when needed to reduce test execution time.
//
// Module types do not have much of an overhead unless they are used so this should include as many
// module types as possible. The exceptions are those module types that require mutators and/or
// singletons in order to function in which case they should be kept together in a separate
// preparer.
//
// The mutators in this group were chosen because they are needed by the vast majority of tests.
var PrepareForTestWithAndroidBuildComponents = GroupFixturePreparers(
 // Sorted alphabetically as the actual order does not matter as tests automatically enforce the
 // correct order.
 PrepareForTestWithArchMutator,
 PrepareForTestWithComponentsMutator,
 PrepareForTestWithDefaults,
 PrepareForTestWithFilegroup,
 PrepareForTestWithOverrides,
 PrepareForTestWithPackageModule,
 PrepareForTestWithPrebuilts,
 PrepareForTestWithVisibility,
 PrepareForTestVintfFragmentModules,
)

// Prepares an integration test with all build components from the android package.
//
// This should only be used by tests that want to run with as much of the build enabled as possible.
var PrepareForIntegrationTestWithAndroid = GroupFixturePreparers(
 PrepareForTestWithAndroidBuildComponents,
)

// Prepares a test that may be missing dependencies by setting allow_missing_dependencies to
// true.
var PrepareForTestWithAllowMissingDependencies = GroupFixturePreparers(
 FixtureModifyProductVariables(func(variables FixtureProductVariables) {
  variables.Allow_missing_dependencies = proptools.BoolPtr(true)
 }),
 FixtureModifyContext(func(ctx *TestContext) {
  ctx.SetAllowMissingDependencies(true)
  // TODO(b/477627661): on-demand variants is not supported with allow missing deps.
  ctx.SetSplitAllVariants(true)
 }),
)

// Prepares a test that disallows non-existent paths.
var PrepareForTestDisallowNonExistentPaths = FixtureModifyConfig(func(config Config) {
 config.TestAllowNonExistentPaths = false
})

// PrepareForTestWithBuildFlag returns a FixturePreparer that sets the given flag to the given value.
func PrepareForTestWithBuildFlag(flag, value string) FixturePreparer {
 return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
  if variables.BuildFlags == nil {
   variables.BuildFlags = make(map[string]string)
  }
  variables.BuildFlags[flag] = value
 })
}

// PrepareForNativeBridgeEnabled sets configuration with targets including:
// - X86_64 (primary)
// - X86 (secondary)
// - Arm64 on X86_64 (native bridge)
// - Arm on X86 (native bridge)
var PrepareForNativeBridgeEnabled = FixtureModifyConfig(
 func(config Config) {
  config.Targets[Android] = []Target{
   {Os: Android, Arch: Arch{ArchType: X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}},
    NativeBridge: NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
   {Os: Android, Arch: Arch{ArchType: X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}},
    NativeBridge: NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
   {Os: Android, Arch: Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}},
    NativeBridge: NativeBridgeEnabled, NativeBridgeHostArchName: "x86_64", NativeBridgeRelativePath: "arm64"},
   {Os: Android, Arch: Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}},
    NativeBridge: NativeBridgeEnabled, NativeBridgeHostArchName: "x86", NativeBridgeRelativePath: "arm"},
  }
 },
)

func NewTestArchContext(config Config) *TestContext {
 ctx := NewTestContext(config)
 ctx.preDeps = append(ctx.preDeps, registerArchMutator)
 return ctx
}

type TestContext struct {
 *Context
 prePartial, preArch, preDeps, postDeps, postApex, finalDeps []RegisterMutatorFunc
 NameResolver                                                *NameResolver

 // The list of singletons registered for the test.
 singletons sortableComponents

 // The order in which the mutators and singletons will be run in this test
 // context; for debugging.
 mutatorOrder, singletonOrder []string
}

func (ctx *TestContext) PrePartialMutators(f RegisterMutatorFunc) {
 ctx.prePartial = append(ctx.prePartial, f)
}

func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
 ctx.preArch = append(ctx.preArch, f)
}

func (ctx *TestContext) HardCodedPreArchMutators(f RegisterMutatorFunc) {
 // Register mutator function as normal for testing.
 ctx.PreArchMutators(f)
}

func (ctx *TestContext) otherModuleProvider(m ModuleOrProxy, p blueprint.AnyProviderKey) (any, bool) {
 return ctx.Context.ModuleProvider(m, p)
}

func (ctx *TestContext) PreDepsMutators(f RegisterMutatorFunc) {
 ctx.preDeps = append(ctx.preDeps, f)
}

func (ctx *TestContext) PostDepsMutators(f RegisterMutatorFunc) {
 ctx.postDeps = append(ctx.postDeps, f)
}

func (ctx *TestContext) PostApexMutators(f RegisterMutatorFunc) {
 ctx.postApex = append(ctx.postApex, f)
}

func (ctx *TestContext) FinalDepsMutators(f RegisterMutatorFunc) {
 ctx.finalDeps = append(ctx.finalDeps, f)
}

func (ctx *TestContext) OtherModuleProviderAdaptor() OtherModuleProviderContext {
 return NewOtherModuleProviderAdaptor(func(module ModuleOrProxy, provider blueprint.AnyProviderKey) (any, bool) {
  return ctx.otherModuleProvider(module, provider)
 })
}

func (ctx *TestContext) ModuleDir(module ModuleOrProxy) string {
 return ctx.Context.ModuleDir(module)
}

func (ctx *TestContext) ModuleSubDir(module ModuleOrProxy) string {
 return ctx.Context.ModuleSubDir(module)
}

func (ctx *TestContext) ModuleType(module ModuleOrProxy) string {
 return ctx.Context.ModuleType(module)
}

func (ctx *TestContext) ModuleErrorf(module ModuleOrProxy, fmt string, args ...any) error {
 return ctx.Context.ModuleErrorf(module, fmt, args...)
}

func (ctx *TestContext) OtherModulePropertyErrorf(module ModuleOrProxy, property string, fmt_ string, args ...interface{}) {
 panic(fmt.Sprintf(fmt_, args...))
}

// VisitAllModules iterates over all modules tracked through the test config.
func (ctx *TestContext) VisitAllModules(visit func(Module)) {
 for module := range ctx.config.modulesForTests.Iter() {
  visit(module)
 }
}

// VisitAllModulesProxies wraps blueprint.Context.VisitAllModulesProxies, converting blueprint.ModuleProxy to
// android.ModuleProxy.
func (ctx *TestContext) VisitAllModulesProxies(visit func(ModuleProxy)) {
 ctx.Context.VisitAllModulesProxies(func(module blueprint.ModuleProxy) {
  visit(ModuleProxy{module})
 })
}

// VisitDirectDeps wraps blueprint.Context.VisitDirectDepsProxies, converting blueprint.ModuleProxy to android.Module
// using the list of modules in the test config.
func (ctx *TestContext) VisitDirectDeps(module Module, visit func(Module)) {
 allModules := slices.Collect(ctx.config.modulesForTests.Iter())
 ctx.VisitDirectDepsProxies(module, func(dep ModuleProxy) {
  i := slices.IndexFunc(allModules, func(m Module) bool { return EqualModules(m, dep) })
  if i < 0 {
   panic(fmt.Errorf("failed to find Module for ModuleProxy %s", dep))
  } else {
   visit(allModules[i])
  }
 })
}

// VisitDirectDepsProxies wraps blueprint.Context.VisitDirectDepsProxies, converting blueprint.ModuleProxy to
// android.ModuleProxy.
func (ctx *TestContext) VisitDirectDepsProxies(module ModuleOrProxy, visit func(ModuleProxy)) {
 ctx.Context.VisitDirectDepsProxies(module, func(dep blueprint.ModuleProxy) {
  visit(ModuleProxy{dep})
 })
}

func (ctx *TestContext) ModuleToProxy(module ModuleOrProxy) ModuleProxy {
 return ModuleProxy{ctx.Context.ModuleToProxy(module)}
}

// registeredComponentOrder defines the order in which a sortableComponent type is registered at
// runtime and provides support for reordering the components registered for a test in the same
// way.
type registeredComponentOrder struct {
 // The name of the component type, used for error messages.
 componentType string

 // The names of the registered components in the order in which they were registered.
 namesInOrder []string

 // Maps from the component name to its position in the runtime ordering.
 namesToIndex map[string]int

 // A function that defines the order between two named components that can be used to sort a slice
 // of component names into the same order as they appear in namesInOrder.
 less func(string, string) bool
}

// registeredComponentOrderFromExistingOrder takes an existing slice of sortableComponents and
// creates a registeredComponentOrder that contains a less function that can be used to sort a
// subset of that list of names so it is in the same order as the original sortableComponents.
func registeredComponentOrderFromExistingOrder(componentType string, existingOrder sortableComponents) registeredComponentOrder {
 // Only the names from the existing order are needed for this so create a list of component names
 // in the correct order.
 namesInOrder := componentsToNames(existingOrder)

 // Populate the map from name to position in the list.
 nameToIndex := make(map[string]int)
 for i, n := range namesInOrder {
  nameToIndex[n] = i
 }

 // A function to use to map from a name to an index in the original order.
 indexOf := func(name string) int {
  index, ok := nameToIndex[name]
  if !ok {
   // Should never happen as tests that use components that are not known at runtime do not sort
   // so should never use this function.
   panic(fmt.Errorf("internal error: unknown %s %q should be one of %s", componentType, name, strings.Join(namesInOrder, ", ")))
  }
  return index
 }

 // The less function.
 less := func(n1, n2 string) bool {
  i1 := indexOf(n1)
  i2 := indexOf(n2)
  return i1 < i2
 }

 return registeredComponentOrder{
  componentType: componentType,
  namesInOrder:  namesInOrder,
  namesToIndex:  nameToIndex,
  less:          less,
 }
}

// componentsToNames maps from the slice of components to a slice of their names.
func componentsToNames(components sortableComponents) []string {
 names := make([]string, len(components))
 for i, c := range components {
  names[i] = c.componentName()
 }
 return names
}

// enforceOrdering enforces the supplied components are in the same order as is defined in this
// object.
//
// If the supplied components contains any components that are not registered at runtime, i.e. test
// specific components, then it is impossible to sort them into an order that both matches the
// runtime and also preserves the implicit ordering defined in the test. In that case it will not
// sort the components, instead it will just check that the components are in the correct order.
//
// Otherwise, this will sort the supplied components in place.
func (o *registeredComponentOrder) enforceOrdering(components sortableComponents) {
 // Check to see if the list of components contains any components that are
 // not registered at runtime.
 var unknownComponents []string
 testOrder := componentsToNames(components)
 for _, name := range testOrder {
  if _, ok := o.namesToIndex[name]; !ok {
   unknownComponents = append(unknownComponents, name)
   break
  }
 }

 // If the slice contains some unknown components then it is not possible to
 // sort them into an order that matches the runtime while also preserving the
 // order expected from the test, so in that case don't sort just check that
 // the order of the known mutators does match.
 if len(unknownComponents) > 0 {
  // Check order.
  o.checkTestOrder(testOrder, unknownComponents)
 } else {
  // Sort the components.
  sort.Slice(components, func(i, j int) bool {
   n1 := components[i].componentName()
   n2 := components[j].componentName()
   return o.less(n1, n2)
  })
 }
}

// checkTestOrder checks that the supplied testOrder matches the one defined by this object,
// panicking if it does not.
func (o *registeredComponentOrder) checkTestOrder(testOrder []string, unknownComponents []string) {
 lastMatchingTest := -1
 matchCount := 0
 // Take a copy of the runtime order as it is modified during the comparison.
 runtimeOrder := append([]string(nil), o.namesInOrder...)
 componentType := o.componentType
 for i, j := 00; i < len(testOrder) && j < len(runtimeOrder); {
  test := testOrder[i]
  runtime := runtimeOrder[j]

  if test == runtime {
   testOrder[i] = test + fmt.Sprintf(" <-- matched with runtime %s %d", componentType, j)
   runtimeOrder[j] = runtime + fmt.Sprintf(" <-- matched with test %s %d", componentType, i)
   lastMatchingTest = i
   i += 1
   j += 1
   matchCount += 1
  } else if _, ok := o.namesToIndex[test]; !ok {
   // The test component is not registered globally so assume it is the correct place, treat it
   // as having matched and skip it.
   i += 1
   matchCount += 1
  } else {
   // Assume that the test list is in the same order as the runtime list but the runtime list
   // contains some components that are not present in the tests. So, skip the runtime component
   // to try and find the next one that matches the current test component.
   j += 1
  }
 }

 // If every item in the test order was either test specific or matched one in the runtime then
 // it is in the correct order. Otherwise, it was not so fail.
 if matchCount != len(testOrder) {
  // The test component names were not all matched with a runtime component name so there must
  // either be a component present in the test that is not present in the runtime or they must be
  // in the wrong order.
  testOrder[lastMatchingTest+1] = testOrder[lastMatchingTest+1] + " <--- unmatched"
  panic(fmt.Errorf("the tests uses test specific components %q and so cannot be automatically sorted."+
   " Unfortunately it uses %s components in the wrong order.\n"+
   "test order:\n    %s\n"+
   "runtime order\n    %s\n",
   SortedUniqueStrings(unknownComponents),
   componentType,
   strings.Join(testOrder, "\n    "),
   strings.Join(runtimeOrder, "\n    ")))
 }
}

// registrationSorter encapsulates the information needed to ensure that the test mutators are
// registered, and thereby executed, in the same order as they are at runtime.
//
// It MUST be populated lazily AFTER all package initialization has been done otherwise it will
// only define the order for a subset of all the registered build components that are available for
// the packages being tested.
//
// e.g if this is initialized during say the cc package initialization then any tests run in the
// java package will not sort build components registered by the java package's init() functions.
type registrationSorter struct {
 // Used to ensure that this is only created once.
 once sync.Once

 // The order of mutators
 mutatorOrder registeredComponentOrder

 // The order of singletons
 singletonOrder registeredComponentOrder
}

// populate initializes this structure from globally registered build components.
//
// Only the first call has any effect.
func (s *registrationSorter) populate() {
 s.once.Do(func() {
  // Created an ordering from the globally registered mutators.
  globallyRegisteredMutators := collateGloballyRegisteredMutators()
  s.mutatorOrder = registeredComponentOrderFromExistingOrder("mutator", globallyRegisteredMutators)

  // Create an ordering from the globally registered singletons.
  globallyRegisteredSingletons := collateGloballyRegisteredSingletons()
  s.singletonOrder = registeredComponentOrderFromExistingOrder("singleton", globallyRegisteredSingletons)
 })
}

// Provides support for enforcing the same order in which build components are registered globally
// to the order in which they are registered during tests.
//
// MUST only be accessed via the globallyRegisteredComponentsOrder func.
var globalRegistrationSorter registrationSorter

// globallyRegisteredComponentsOrder returns the globalRegistrationSorter after ensuring it is
// correctly populated.
func globallyRegisteredComponentsOrder() *registrationSorter {
 globalRegistrationSorter.populate()
 return &globalRegistrationSorter
}

func (ctx *TestContext) Register() {
 globalOrder := globallyRegisteredComponentsOrder()

 mutators := collateRegisteredMutators(ctx.prePartial, ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.postApex, ctx.finalDeps)
 // Ensure that the mutators used in the test are in the same order as they are used at runtime.
 globalOrder.mutatorOrder.enforceOrdering(mutators)
 mutators.registerAll(ctx.Context)

 // Ensure that the singletons used in the test are in the same order as they are used at runtime.
 globalOrder.singletonOrder.enforceOrdering(ctx.singletons)
 ctx.singletons.registerAll(ctx.Context)

 // Save the sorted components order away to make them easy to access while debugging.
 ctx.mutatorOrder = componentsToNames(mutators)
 ctx.singletonOrder = componentsToNames(singletons)
}

func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
 // This function adapts the old style ParseFileList calls that are spread throughout the tests
 // to the new style that takes a config.
 return ctx.Context.ParseFileList(rootDir, filePaths, ctx.config)
}

func (ctx *TestContext) ParseBlueprintsFiles(rootDir string) (deps []string, errs []error) {
 // This function adapts the old style ParseBlueprintsFiles calls that are spread throughout the
 // tests to the new style that takes a config.
 return ctx.Context.ParseBlueprintsFiles(rootDir, ctx.config)
}

func (ctx *TestContext) RegisterModuleType(name string, factory ModuleFactory) {
 ctx.Context.RegisterModuleType(name, ModuleFactoryAdaptor(factory))
}

func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) {
 ctx.singletons = append(ctx.singletons, newSingleton(name, factory, false))
}

func (ctx *TestContext) RegisterParallelSingletonType(name string, factory SingletonFactory) {
 ctx.singletons = append(ctx.singletons, newSingleton(name, factory, true))
}

// ModuleVariantForTests selects a specific variant of the module with the given
// name by matching the variations map against the variations of each module
// variant. A module variant matches the map if every variation that exists in
// both have the same value. Both the module and the map are allowed to have
// extra variations that the other doesn't have. Panics if not exactly one
// module variant matches.
func (ctx *TestContext) ModuleVariantForTests(t *testing.T, name string, matchVariations map[string]string) TestingModule {
 t.Helper()
 modules := []Module{}
 ctx.VisitAllModules(func(m Module) {
  if ctx.ModuleName(m) == name {
   am := m.(Module)
   amMut := am.base().commonProperties.DebugMutators
   amVar := am.base().commonProperties.DebugVariations
   matched := true
   for i, mut := range amMut {
    if wantedVar, found := matchVariations[mut]; found && amVar[i] != wantedVar {
     matched = false
     break
    }
   }
   if matched {
    modules = append(modules, am)
   }
  }
 })

 if len(modules) == 0 {
  // Show all the modules or module variants that do exist.
  var allModuleNames []string
  var allVariants []string
  ctx.VisitAllModules(func(m Module) {
   allModuleNames = append(allModuleNames, ctx.ModuleName(m))
   if ctx.ModuleName(m) == name {
    allVariants = append(allVariants, m.(Module).String())
   }
  })

  if len(allVariants) == 0 {
   t.Fatalf("failed to find module %q. All modules:\n  %s",
    name, strings.Join(SortedUniqueStrings(allModuleNames), "\n  "))
  } else {
   sort.Strings(allVariants)
   t.Fatalf("failed to find module %q matching %v. All variants:\n  %s",
    name, matchVariations, strings.Join(allVariants, "\n  "))
  }
 }

 if len(modules) > 1 {
  moduleStrings := []string{}
  for _, m := range modules {
   moduleStrings = append(moduleStrings, m.String())
  }
  sort.Strings(moduleStrings)
  t.Fatalf("module %q has more than one variant that match %v:\n  %s",
   name, matchVariations, strings.Join(moduleStrings, "\n  "))
 }

 return newTestingModule(t, ctx.config, modules[0], ctx.ModuleToProxy(modules[0]))
}

func (ctx *TestContext) ModuleForTests(t *testing.T, name, variant string) TestingModule {
 t.Helper()
 var module Module
 ctx.VisitAllModules(func(m Module) {
  if ctx.ModuleName(m) == name && ctx.ModuleSubDir(m) == variant {
   module = m
  }
 })

 if module == nil {
  // find all the modules that do exist
  var allModuleNames []string
  var allVariants []string
  ctx.VisitAllModules(func(m Module) {
   allModuleNames = append(allModuleNames, ctx.ModuleName(m))
   if ctx.ModuleName(m) == name {
    allVariants = append(allVariants, ctx.ModuleSubDir(m))
   }
  })
  sort.Strings(allVariants)

  if len(allVariants) == 0 {
   t.Fatalf("failed to find module %q. All modules:\n  %s",
    name, strings.Join(SortedUniqueStrings(allModuleNames), "\n  "))
  } else {
   t.Fatalf("failed to find module %q variant %q. All variants:\n  %s",
    name, variant, strings.Join(allVariants, "\n  "))
  }
 }

 return newTestingModule(t, ctx.config, module, ctx.ModuleToProxy(module))
}

func (ctx *TestContext) ModuleVariantsForTests(name string) []string {
 var variants []string
 ctx.VisitAllModules(func(m Module) {
  if ctx.ModuleName(m) == name {
   variants = append(variants, ctx.ModuleSubDir(m))
  }
 })
 return variants
}

// SingletonForTests returns a TestingSingleton for the singleton registered with the given name.
func (ctx *TestContext) SingletonForTests(t *testing.T, name string) TestingSingleton {
 t.Helper()
 allSingletonNames := []string{}
 for _, s := range ctx.Singletons() {
  n := ctx.SingletonName(s)
  if n == name {
   return TestingSingleton{
    baseTestingComponent: newBaseTestingComponent(t, ctx.config, s.(testBuildProvider)),
    singleton:            s.(*singletonAdaptor).Singleton,
   }
  }
  allSingletonNames = append(allSingletonNames, n)
 }

 t.Fatalf("failed to find singleton %q."+
  "\nall singletons: %v", name, allSingletonNames)

 return TestingSingleton{}
}

type InstallMakeRule struct {
 Target        string
 Deps          []string
 OrderOnlyDeps []string
}

func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []InstallMakeRule {
 t.Helper()
 var rules []InstallMakeRule
 for _, node := range nodes {
  if mkParserRule, ok := node.(*mkparser.Rule); ok {
   var rule InstallMakeRule

   if targets := mkParserRule.Target.Words(); len(targets) == 0 {
    t.Fatalf("no targets for rule %s", mkParserRule.Dump())
   } else if len(targets) > 1 {
    t.Fatalf("unsupported multiple targets for rule %s", mkParserRule.Dump())
   } else if !targets[0].Const() {
    t.Fatalf("unsupported non-const target for rule %s", mkParserRule.Dump())
   } else {
    rule.Target = normalizeStringRelativeToTop(config, targets[0].Value(nil))
   }

   prereqList := &rule.Deps
   for _, prereq := range mkParserRule.Prerequisites.Words() {
    if !prereq.Const() {
     t.Fatalf("unsupported non-const prerequisite for rule %s", mkParserRule.Dump())
    }

    if prereq.Value(nil) == "|" {
     prereqList = &rule.OrderOnlyDeps
     continue
    }

    *prereqList = append(*prereqList, normalizeStringRelativeToTop(config, prereq.Value(nil)))
   }

   rules = append(rules, rule)
  }
 }

 return rules
}

func (ctx *TestContext) InstallMakeRulesForTesting(t *testing.T) []InstallMakeRule {
 t.Helper()
 installs := ctx.SingletonForTests(t, "makevars").Singleton().(*makeVarsSingleton).installsForTesting
 buf := bytes.NewBuffer(append([]byte(nil), installs...))
 parser := mkparser.NewParser("makevars", buf)

 nodes, errs := parser.Parse()
 if len(errs) > 0 {
  t.Fatalf("error parsing install rules: %s", errs[0])
 }

 return parseMkRules(t, ctx.config, nodes)
}

// MakeVarVariable provides access to make vars that will be written by the makeVarsSingleton
type MakeVarVariable interface {
 // Name is the name of the variable.
 Name() string

 // Value is the value of the variable.
 Value() string
}

func (v makeVarsVariable) Name() string {
 return v.name
}

func (v makeVarsVariable) Value() string {
 return v.value
}

// PrepareForTestAccessingMakeVars sets up the test so that MakeVarsForTesting will work.
var PrepareForTestAccessingMakeVars = GroupFixturePreparers(
 PrepareForTestWithAndroidMk,
 PrepareForTestWithMakevars,
)

// MakeVarsForTesting returns a filtered list of MakeVarVariable objects that represent the
// variables that will be written out.
//
// It is necessary to use PrepareForTestAccessingMakeVars in tests that want to call this function.
// Along with any other preparers needed to add the make vars.
func (ctx *TestContext) MakeVarsForTesting(t *testing.T, filter func(variable MakeVarVariable) bool) []MakeVarVariable {
 t.Helper()
 vars := ctx.SingletonForTests(t, "makevars").Singleton().(*makeVarsSingleton).varsForTesting
 result := make([]MakeVarVariable, 0, len(vars))
 for _, v := range vars {
  if filter(v) {
   result = append(result, v)
  }
 }

 return result
}

func (ctx *TestContext) Config() Config {
 return ctx.config
}

type testBuildProvider interface {
 BuildParamsForTests() []BuildParams
 RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
}

type TestingBuildParams struct {
 BuildParams
 RuleParams blueprint.RuleParams

 config Config
}

// RelativeToTop creates a new instance of this which has had any usages of the current test's
// temporary and test specific build directory replaced with a path relative to the notional top.
//
// The parts of this structure which are changed are:
// * BuildParams
//   - Args
//   - All Path, Paths, WritablePath and WritablePaths fields.
//
// * RuleParams
//   - Command
//   - Depfile
//   - Rspfile
//   - RspfileContent
//   - CommandDeps
//   - CommandOrderOnly
//
// See PathRelativeToTop for more details.
//
// deprecated: this is no longer needed as TestingBuildParams are created in this form.
func (p TestingBuildParams) RelativeToTop() TestingBuildParams {
 // If this is not a valid params then just return it back. That will make it easy to use with the
 // Maybe...() methods.
 if p.Rule == nil {
  return p
 }
 if p.config.config == nil {
  return p
 }
 // Take a copy of the build params and replace any args that contains test specific temporary
 // paths with paths relative to the top.
 bparams := p.BuildParams
 bparams.Depfile = normalizeWritablePathRelativeToTop(bparams.Depfile)
 bparams.Output = normalizeWritablePathRelativeToTop(bparams.Output)
 bparams.Outputs = bparams.Outputs.RelativeToTop()
 bparams.ImplicitOutput = normalizeWritablePathRelativeToTop(bparams.ImplicitOutput)
 bparams.ImplicitOutputs = bparams.ImplicitOutputs.RelativeToTop()
 bparams.Input = normalizePathRelativeToTop(bparams.Input)
 bparams.Inputs = bparams.Inputs.RelativeToTop()
 bparams.Implicit = normalizePathRelativeToTop(bparams.Implicit)
 bparams.Implicits = bparams.Implicits.RelativeToTop()
 bparams.OrderOnly = bparams.OrderOnly.RelativeToTop()
 bparams.Validation = normalizePathRelativeToTop(bparams.Validation)
 bparams.Validations = bparams.Validations.RelativeToTop()
 bparams.Args = normalizeStringMapRelativeToTop(p.config, bparams.Args)

 // Ditto for any fields in the RuleParams.
 rparams := p.RuleParams
 rparams.Command = normalizeStringRelativeToTop(p.config, rparams.Command)
 rparams.Depfile = normalizeStringRelativeToTop(p.config, rparams.Depfile)
 rparams.Rspfile = normalizeStringRelativeToTop(p.config, rparams.Rspfile)
 rparams.RspfileContent = normalizeStringRelativeToTop(p.config, rparams.RspfileContent)
 rparams.CommandDeps = normalizeStringArrayRelativeToTop(p.config, rparams.CommandDeps)
 rparams.CommandOrderOnly = normalizeStringArrayRelativeToTop(p.config, rparams.CommandOrderOnly)

 return TestingBuildParams{
  BuildParams: bparams,
  RuleParams:  rparams,
 }
}

func normalizeWritablePathRelativeToTop(path WritablePath) WritablePath {
 if path == nil {
  return nil
 }
 return path.RelativeToTop().(WritablePath)
}

func normalizePathRelativeToTop(path Path) Path {
 if path == nil {
  return nil
 }
 return path.RelativeToTop()
}

func allOutputs(p BuildParams) []string {
 outputs := append(WritablePaths(nil), p.Outputs...)
 outputs = append(outputs, p.ImplicitOutputs...)
 if p.Output != nil {
  outputs = append(outputs, p.Output)
 }
 return outputs.Strings()
}

// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
func (p TestingBuildParams) AllOutputs() []string {
 return allOutputs(p.BuildParams)
}

// baseTestingComponent provides functionality common to both TestingModule and TestingSingleton.
type baseTestingComponent struct {
 t        *testing.T
 config   Config
 provider testBuildProvider
}

func newBaseTestingComponent(t *testing.T, config Config, provider testBuildProvider) baseTestingComponent {
 return baseTestingComponent{t, config, provider}
}

// A function that will normalize a string containing paths, e.g. ninja command, by replacing
// any references to the test specific temporary build directory that changes with each run to a
// fixed path relative to a notional top directory.
//
// This is similar to StringPathRelativeToTop except that assumes the string is a single path
// containing at most one instance of the temporary build directory at the start of the path while
// this assumes that there can be any number at any position.
func normalizeStringRelativeToTop(config Config, s string) string {
 // The outDir usually looks something like: /tmp/testFoo2345/001
 //
 // Replace any usage of the outDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
 // "out/soong".
 outSoongDir := filepath.Clean(config.soongOutDir)
 re := regexp.MustCompile(`\Q` + outSoongDir + `\E\b`)
 s = re.ReplaceAllString(s, "out/soong")

 // Replace any usage of the outDir/.. with out, e.g. replace "/tmp/testFoo2345" with
 // "out". This must come after the previous replacement otherwise this would replace
 // "/tmp/testFoo2345/001" with "out/001" instead of "out/soong".
 outDir := filepath.Dir(outSoongDir)
 re = regexp.MustCompile(`\Q` + outDir + `\E\b`)
 s = re.ReplaceAllString(s, "out")

 return s
}

// normalizeStringArrayRelativeToTop creates a new slice constructed by applying
// normalizeStringRelativeToTop to each item in the slice.
func normalizeStringArrayRelativeToTop(config Config, slice []string) []string {
 newSlice := make([]string, len(slice))
 for i, s := range slice {
  newSlice[i] = normalizeStringRelativeToTop(config, s)
 }
 return newSlice
}

// normalizeStringMapRelativeToTop creates a new map constructed by applying
// normalizeStringRelativeToTop to each value in the map.
func normalizeStringMapRelativeToTop(config Config, m map[string]string) map[string]string {
 newMap := map[string]string{}
 for k, v := range m {
  newMap[k] = normalizeStringRelativeToTop(config, v)
 }
 return newMap
}

func (b baseTestingComponent) newTestingBuildParams(bparams BuildParams) TestingBuildParams {
 return TestingBuildParams{
  config:      b.config,
  BuildParams: bparams,
  RuleParams:  b.provider.RuleParamsForTests()[bparams.Rule],
 }.RelativeToTop()
}

func (b baseTestingComponent) maybeBuildParamsFromRule(rule string) (TestingBuildParams, []string) {
 var searchedRules []string
 buildParams := b.provider.BuildParamsForTests()
 for _, p := range buildParams {
  ruleAsString := p.Rule.String()
  searchedRules = append(searchedRules, ruleAsString)
  if strings.Contains(ruleAsString, rule) {
   return b.newTestingBuildParams(p), searchedRules
  }
 }
 return TestingBuildParams{}, searchedRules
}

func (b baseTestingComponent) buildParamsFromRule(rule string) TestingBuildParams {
 b.t.Helper()
 p, searchRules := b.maybeBuildParamsFromRule(rule)
 if p.Rule == nil {
  b.t.Fatalf("couldn't find rule %q.\nall rules:\n%s", rule, strings.Join(searchRules, "\n"))
 }
 return p
}

func (b baseTestingComponent) maybeBuildParamsFromDescription(desc string) (TestingBuildParams, []string) {
 var searchedDescriptions []string
 for _, p := range b.provider.BuildParamsForTests() {
  searchedDescriptions = append(searchedDescriptions, p.Description)
  if strings.Contains(p.Description, desc) {
   return b.newTestingBuildParams(p), searchedDescriptions
  }
 }
 return TestingBuildParams{}, searchedDescriptions
}

func (b baseTestingComponent) buildParamsFromDescription(desc string) TestingBuildParams {
 b.t.Helper()
 p, searchedDescriptions := b.maybeBuildParamsFromDescription(desc)
 if p.Rule == nil {
  b.t.Fatalf("couldn't find description %q\nall descriptions:\n%s", desc, strings.Join(searchedDescriptions, "\n"))
 }
 return p
}

func (b baseTestingComponent) maybeBuildParamsFromOutput(file string) (TestingBuildParams, []string) {
 searchedOutputs := WritablePaths(nil)
 for _, p := range b.provider.BuildParamsForTests() {
  outputs := append(WritablePaths(nil), p.Outputs...)
  outputs = append(outputs, p.ImplicitOutputs...)
  if p.Output != nil {
   outputs = append(outputs, p.Output)
  }
  for _, f := range outputs {
   if f.String() == file || f.Rel() == file || PathRelativeToTop(f) == file {
    return b.newTestingBuildParams(p), nil
   }
   searchedOutputs = append(searchedOutputs, f)
  }
 }

 formattedOutputs := []string{}
 for _, f := range searchedOutputs {
  formattedOutputs = append(formattedOutputs,
   fmt.Sprintf("%s (rel=%s)", PathRelativeToTop(f), f.Rel()))
 }

 return TestingBuildParams{}, formattedOutputs
}

func (b baseTestingComponent) buildParamsFromOutput(file string) TestingBuildParams {
 b.t.Helper()
 p, searchedOutputs := b.maybeBuildParamsFromOutput(file)
 if p.Rule == nil {
  b.t.Fatalf("couldn't find output %q.\nall outputs:\n    %s\n",
   file, strings.Join(searchedOutputs, "\n    "))
 }
 return p
}

func (b baseTestingComponent) allOutputs() []string {
 var outputFullPaths []string
 for _, p := range b.provider.BuildParamsForTests() {
  outputFullPaths = append(outputFullPaths, allOutputs(p)...)
 }
 return outputFullPaths
}

// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Returns an empty
// BuildParams if no rule is found.
func (b baseTestingComponent) MaybeRule(rule string) TestingBuildParams {
 r, _ := b.maybeBuildParamsFromRule(rule)
 return r
}

// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Panics if no rule is found.
func (b baseTestingComponent) Rule(rule string) TestingBuildParams {
 b.t.Helper()
 return b.buildParamsFromRule(rule)
}

// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string.  Returns an empty
// BuildParams if no rule is found.
func (b baseTestingComponent) MaybeDescription(desc string) TestingBuildParams {
 p, _ := b.maybeBuildParamsFromDescription(desc)
 return p
}

// Description finds a call to ctx.Build with BuildParams.Description set to a the given string.  Panics if no rule is
// found.
func (b baseTestingComponent) Description(desc string) TestingBuildParams {
 b.t.Helper()
 return b.buildParamsFromDescription(desc)
}

// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
// value matches the provided string.  Returns an empty BuildParams if no rule is found.
func (b baseTestingComponent) MaybeOutput(file string) TestingBuildParams {
 p, _ := b.maybeBuildParamsFromOutput(file)
 return p
}

// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
// value matches the provided string.  Panics if no rule is found.
func (b baseTestingComponent) Output(file string) TestingBuildParams {
 b.t.Helper()
 return b.buildParamsFromOutput(file)
}

// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
func (b baseTestingComponent) AllOutputs() []string {
 return b.allOutputs()
}

// TestingModule is wrapper around an android.Module that provides methods to find information about individual
// ctx.Build parameters for verification in tests.
type TestingModule struct {
 baseTestingComponent
 module Module
 proxy  ModuleProxy
}

func newTestingModule(t *testing.T, config Config, module Module, proxy ModuleProxy) TestingModule {
 return TestingModule{
  newBaseTestingComponent(t, config, module),
  module,
  proxy,
 }
}

// Module returns the Module wrapped by the TestingModule.
func (m TestingModule) Module() Module {
 return m.module
}

// ModuleProxy returns the ModuleProxy wrapped by the TestingModule.
func (m TestingModule) ModuleProxy() ModuleProxy {
 return m.proxy
}

// VariablesForTestsRelativeToTop returns a copy of the Module.VariablesForTests() with every value
// having any temporary build dir usages replaced with paths relative to a notional top.
func (m TestingModule) VariablesForTestsRelativeToTop() map[string]string {
 return normalizeStringMapRelativeToTop(m.config, m.module.VariablesForTests())
}

// OutputFiles checks if module base outputFiles property has any output
// files can be used to return.
// Exits the test immediately if there is an error and
// otherwise returns the result of calling Paths.RelativeToTop
// on the returned Paths.
func (m TestingModule) OutputFiles(ctx *TestContext, t *testing.T, tag string) Paths {
 if outputFiles := GetOutputFiles(ctx.OtherModuleProviderAdaptor(), m.Module()); outputFiles != nil {
  if tag == "" && outputFiles.DefaultOutputFiles != nil {
   return outputFiles.DefaultOutputFiles.RelativeToTop()
  } else if taggedOutputFiles, hasTag := outputFiles.TaggedOutputFiles[tag]; hasTag {
   return taggedOutputFiles.RelativeToTop()
  }
 }

 t.Fatal(fmt.Errorf("No test output file has been set for tag %q", tag))
 return nil
}

// TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual
// ctx.Build parameters for verification in tests.
type TestingSingleton struct {
 baseTestingComponent
 singleton Singleton
}

// Singleton returns the Singleton wrapped by the TestingSingleton.
func (s TestingSingleton) Singleton() Singleton {
 return s.singleton
}

func FailIfErrored(t *testing.T, errs []error) {
 t.Helper()
 if len(errs) > 0 {
  for _, err := range errs {
   t.Error(err)
  }
  t.FailNow()
 }
}

// Fail if no errors that matched the regular expression were found.
//
// Returns true if a matching error was found, false otherwise.
func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) bool {
 t.Helper()

 matcher, err := regexp.Compile(pattern)
 if err != nil {
  t.Fatalf("failed to compile regular expression %q because %s", pattern, err)
 }

 found := false
 for _, err := range errs {
  if matcher.FindStringIndex(err.Error()) != nil {
   found = true
   break
  }
 }
 if !found {
  t.Errorf("could not match the expected error regex %q (checked %d error(s))", pattern, len(errs))
  for i, err := range errs {
   t.Errorf("errs[%d] = %q", i, err)
  }
 }

 return found
}

func CheckErrorsAgainstExpectations(t *testing.T, errs []error, expectedErrorPatterns []string) {
 t.Helper()

 if expectedErrorPatterns == nil {
  FailIfErrored(t, errs)
 } else {
  for _, expectedError := range expectedErrorPatterns {
   FailIfNoMatchingErrors(t, expectedError, errs)
  }
  if len(errs) > len(expectedErrorPatterns) {
   t.Errorf("additional errors found, expected %d, found %d",
    len(expectedErrorPatterns), len(errs))
   for i, expectedError := range expectedErrorPatterns {
    t.Errorf("expectedErrors[%d] = %s", i, expectedError)
   }
   for i, err := range errs {
    t.Errorf("errs[%d] = %s", i, err)
   }
   t.FailNow()
  }
 }
}

func SetKatiEnabledForTests(config Config) {
 config.katiEnabled = true
}

func SetBuildDateFileEnvVarForTests() FixturePreparer {
 return FixtureModifyConfig(func(config Config) {
  config.env["BUILD_DATETIME_FILE"] = filepath.Join(config.outDir, "build_date.txt")
 })
}

func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod Module) []AndroidMkEntries {
 t.Helper()
 var p AndroidMkEntriesProvider
 var ok bool
 if p, ok = mod.(AndroidMkEntriesProvider); !ok {
  t.Error("module does not implement AndroidMkEntriesProvider: " + mod.Name())
 }

 entriesList := p.AndroidMkEntries()
 aconfigUpdateAndroidMkEntries(ctx, mod, &entriesList)
 for i := range entriesList {
  entriesList[i].fillInEntries(ctx, mod)
 }
 return entriesList
}

func AndroidMkInfoForTest(t *testing.T, ctx *TestContext, mod Module) *AndroidMkProviderInfo {
 if runtime.GOOS == "darwin" && mod.base().Os() != Darwin {
  // The AndroidMkInfo provider is not set in this case.
  t.Skip("AndroidMkInfo provider is not set on darwin")
 }

 t.Helper()
 var ok bool
 if _, ok = mod.(AndroidMkProviderInfoProducer); !ok {
  t.Error("module does not implement AndroidMkProviderInfoProducer: " + mod.Name())
 }

 info, ok := OtherModuleProvider(ctx, mod, AndroidMkInfoProvider)
 if !ok {
  t.Fatalf("module %q is missing AndroidMkInfoProvider", mod.Name())
 }
 aconfigUpdateAndroidMkInfos(ctx, mod, info)
 commonInfo := OtherModulePointerProviderOrDefault(ctx, mod, CommonModuleInfoProvider)
 info.PrimaryInfo.fillInEntries(ctx, mod, commonInfo)
 if len(info.ExtraInfo) > 0 {
  for i := range info.ExtraInfo {
   info.ExtraInfo[i].fillInEntries(ctx, mod, commonInfo)
  }
 }

 return info
}

func AndroidMkDataForTest(t *testing.T, ctx *TestContext, mod Module) AndroidMkData {
 t.Helper()
 var p AndroidMkDataProvider
 var ok bool
 if p, ok = mod.(AndroidMkDataProvider); !ok {
  t.Fatal("module does not implement AndroidMkDataProvider: " + mod.Name())
 }
 data := p.AndroidMk()
 data.fillInData(ctx, mod)
 aconfigUpdateAndroidMkData(ctx, mod, &data)
 return data
}

// Normalize the path for testing.
//
// If the path is relative to the build directory then return the relative path
// to avoid tests having to deal with the dynamically generated build directory.
//
// Otherwise, return the supplied path as it is almost certainly a source path
// that is relative to the root of the source tree.
//
// The build and source paths should be distinguishable based on their contents.
//
// deprecated: use PathRelativeToTop instead as it handles make install paths and differentiates
// between output and source properly.
func NormalizePathForTesting(path Path) string {
 if path == nil {
  return "<nil path>"
 }
 p := path.String()
 if w, ok := path.(WritablePath); ok {
  rel, err := filepath.Rel(w.getSoongOutDir(), p)
  if err != nil {
   panic(err)
  }
  return rel
 }
 return p
}

// NormalizePathsForTesting creates a slice of strings where each string is the result of applying
// NormalizePathForTesting to the corresponding Path in the input slice.
//
// deprecated: use PathsRelativeToTop instead as it handles make install paths and differentiates
// between output and source properly.
func NormalizePathsForTesting(paths Paths) []string {
 var result []string
 for _, path := range paths {
  relative := NormalizePathForTesting(path)
  result = append(result, relative)
 }
 return result
}

// PathRelativeToTop returns a string representation of the path relative to a notional top
// directory.
//
// It return "<nil path>" if the supplied path is nil, otherwise it returns the result of calling
// Path.RelativeToTop to obtain a relative Path and then calling Path.String on that to get the
// string representation.
func PathRelativeToTop(path Path) string {
 if path == nil {
  return "<nil path>"
 }
 return path.RelativeToTop().String()
}

// PathsRelativeToTop creates a slice of strings where each string is the result of applying
// PathRelativeToTop to the corresponding Path in the input slice.
func PathsRelativeToTop(paths Paths) []string {
 var result []string
 for _, path := range paths {
  relative := PathRelativeToTop(path)
  result = append(result, relative)
 }
 return result
}

// StringPathRelativeToTop returns a string representation of the path relative to a notional top
// directory.
//
// See Path.RelativeToTop for more details as to what `relative to top` means.
//
// This is provided for processing paths that have already been converted into a string, e.g. paths
// in AndroidMkEntries structures. As a result it needs to be supplied the soong output dir against
// which it can try and relativize paths. PathRelativeToTop must be used for process Path objects.
func StringPathRelativeToTop(soongOutDir string, path string) string {
 ensureTestOnly()

 // A relative path must be a source path so leave it as it is.
 if !filepath.IsAbs(path) {
  return path
 }

 // Check to see if the path is relative to the soong out dir.
 rel, isRel, err := maybeRelErr(soongOutDir, path)
 if err != nil {
  panic(err)
 }

 if isRel {
  if strings.HasSuffix(soongOutDir, testOutSoongSubDir) {
   // The path is in the soong out dir so indicate that in the relative path.
   return filepath.Join(TestOutSoongDir, rel)
  } else {
   // Handle the PathForArbitraryOutput case
   return filepath.Join(testOutDir, rel)

  }
 }

 // Check to see if the path is relative to the top level out dir.
 outDir := filepath.Dir(soongOutDir)
 rel, isRel, err = maybeRelErr(outDir, path)
 if err != nil {
  panic(err)
 }

 if isRel {
  // The path is in the out dir so indicate that in the relative path.
  return filepath.Join("out", rel)
 }

 // This should never happen.
 panic(fmt.Errorf("internal error: absolute path %s is not relative to the out dir %s", path, outDir))
}

// StringPathsRelativeToTop creates a slice of strings where each string is the result of applying
// StringPathRelativeToTop to the corresponding string path in the input slice.
//
// This is provided for processing paths that have already been converted into a string, e.g. paths
// in AndroidMkEntries structures. As a result it needs to be supplied the soong output dir against
// which it can try and relativize paths. PathsRelativeToTop must be used for process Paths objects.
func StringPathsRelativeToTop(soongOutDir string, paths []string) []string {
 var result []string
 for _, path := range paths {
  relative := StringPathRelativeToTop(soongOutDir, path)
  result = append(result, relative)
 }
 return result
}

// StringRelativeToTop will normalize a string containing paths, e.g. ninja command, by replacing
// any references to the test specific temporary build directory that changes with each run to a
// fixed path relative to a notional top directory.
//
// This is similar to StringPathRelativeToTop except that assumes the string is a single path
// containing at most one instance of the temporary build directory at the start of the path while
// this assumes that there can be any number at any position.
func StringRelativeToTop(config Config, command string) string {
 return normalizeStringRelativeToTop(config, command)
}

// StringsRelativeToTop will return a new slice such that each item in the new slice is the result
// of calling StringRelativeToTop on the corresponding item in the input slice.
func StringsRelativeToTop(config Config, command []string) []string {
 return normalizeStringArrayRelativeToTop(config, command)
}

func EnsureListContainsSuffix(t *testing.T, result []string, expected string) {
 t.Helper()
 if !SuffixInList(result, expected) {
  t.Errorf("%q is not found in %v", expected, result)
 }
}

type panickingConfigAndErrorContext struct {
 ctx *TestContext
}

func (ctx *panickingConfigAndErrorContext) OtherModulePropertyErrorf(module ModuleOrProxy, property, fmt string, args ...interface{}) {
 panic(ctx.ctx.PropertyErrorf(module, property, fmt, args...).Error())
}

func (ctx *panickingConfigAndErrorContext) Config() Config {
 return ctx.ctx.Config()
}

func (ctx *panickingConfigAndErrorContext) HasMutatorFinished(mutatorName string) bool {
 return ctx.ctx.HasMutatorFinished(mutatorName)
}

func (ctx *panickingConfigAndErrorContext) otherModuleProvider(m ModuleOrProxy, p blueprint.AnyProviderKey) (any, bool) {
 return ctx.ctx.otherModuleProvider(m, p)
}

func PanickingConfigAndErrorContext(ctx *TestContext) ConfigurableEvaluatorContext {
 return &panickingConfigAndErrorContext{
  ctx: ctx,
 }
}

// modulesForTests stores the list of modules that exist for use during tests.
type modulesForTests struct {
 moduleGroups map[string]*moduleGroupForTests
 lock         sync.Mutex
}

// moduleGroupForTests stores the list of variants that exist for a single module name.
type moduleGroupForTests struct {
 modules []Module
 // nextInsertIndex is the position in modules where the last module is inserted.
 // It is used when inserting new variants to place them after the variant they
 // were created from.
 nextInsertIndex int
}

// Insert inserts a module into modulesForTests.  If the Module matches and existing
// Module in the list (either by being the same pointer or by being a clone with
// identical name, namespace and variations) then it replaces the entry currently
// in the list.  Otherwise, it is inserted after the most recently updated entry in
// the list.
func (m *modulesForTests) Insert(name string, module Module) {
 m.lock.Lock()
 defer m.lock.Unlock()
 if existing, ok := m.moduleGroups[name]; ok {
  // A name that is already present
  if i := slices.IndexFunc(existing.modules, func(old Module) bool {
   return old == module ||
    (old.base().commonProperties.DebugName == module.base().commonProperties.DebugName &&
     old.base().commonProperties.DebugNamespace == module.base().commonProperties.DebugNamespace &&
     slices.Equal(old.base().commonProperties.DebugVariations, module.base().commonProperties.DebugVariations) &&
     slices.Equal(old.base().commonProperties.DebugMutators, module.base().commonProperties.DebugMutators))
  }); i >= 0 {
   // The module matches an existing module, either by being the same Module or by being a clone.
   // Replace the current entry, and set the insertion point to after the current entry.
   // This path is reached when TransitionMutator is creating new variants, and relies on the
   // Blueprint optimization that the existing variant is reused as the first new variant so that
   // the list doesn't collect old obsolete variants.
   existing.modules[i] = module
   existing.nextInsertIndex = i + 1
  } else {
   // The module doesn't match an existing module, insert it at the insertion point and update the
   // insertion point to point after it.
   existing.modules = slices.Concat(existing.modules[:existing.nextInsertIndex],
    []Module{module},
    existing.modules[existing.nextInsertIndex:])
   existing.nextInsertIndex += 1
  }
 } else {
  // The name is not present, add a new entry for it.
  m.moduleGroups[name] = &moduleGroupForTests{
   modules:         []Module{module},
   nextInsertIndex: 0,
  }
 }
}

// Rename updates the name of a module group.
func (m *modulesForTests) Rename(from, to string) {
 m.lock.Lock()
 defer m.lock.Unlock()
 m.moduleGroups[to] = m.moduleGroups[from]
 delete(m.moduleGroups, from)
}

// Iter returns a Seq of all the modules in a deterministic order (alphabetically by module name,
// and then in variant order).
func (m *modulesForTests) Iter() iter.Seq[Module] {
 return func(yield func(Module) bool) {
  groups := slices.Collect(maps.Keys(m.moduleGroups))
  slices.Sort(groups)
  for _, group := range groups {
   for _, module := range m.moduleGroups[group].modules {
    if !yield(module) {
     return
    }
   }
  }
 }
}

type visitDirectDepsInterface interface {
 VisitDirectDepsProxies(ModuleOrProxy, func(dep ModuleProxy))
}

// HasDirectDep returns true if wantDep is a direct dependency of m.
func HasDirectDep(ctx visitDirectDepsInterface, m Module, wantDep ModuleOrProxy) bool {
 var found bool
 ctx.VisitDirectDepsProxies(m, func(dep ModuleProxy) {
  if EqualModules(dep, wantDep) {
   found = true
  }
 })
 return found
}

// AssertHasDirectDep asserts that wantDep is a direct dependency of m.
func AssertHasDirectDep(t *testing.T, ctx visitDirectDepsInterface, m Module, wantDep ModuleOrProxy) {
 t.Helper()
 found := false
 ctx.VisitDirectDepsProxies(m, func(dep ModuleProxy) {
  if EqualModules(dep, wantDep) {
   found = true
  }
 })
 if !found {
  t.Errorf("Could not find a dependency from %v to %v\n", m, wantDep)
 }
}

func newHostMockModule() Module {
 m := &hostMockModule{}
 InitAndroidArchModule(m, HostSupported, MultilibFirst)
 return m
}

type hostMockModule struct {
 ModuleBase
}

func (m *hostMockModule) GenerateAndroidBuildActions(ctx ModuleContext) {
}

var PrepareForTestWithHostMockModule = FixtureRegisterWithContext(func(ctx RegistrationContext) {
 ctx.RegisterModuleType("host_mock_module", newHostMockModule)
})

// PrepareForTestWithHostTools adds Android.bp files with placeholder host modules with
// the given names. The modules don't do anything. This is intended to resolve issues with
// module types that depend on host tools for their ninja rule generation.
func PrepareForTestWithHostTools(hostTools ...string) FixturePreparer {
 fs := make(MockFS)

 for _, hostTool := range hostTools {
  if _, ok := commonToyboxSymlinks[hostTool]; ok {
   prebuiltOS := prebuiltOS()
   fs["prebuilts/build-tools/"+prebuiltOS+"/bin/toybox"] = []byte{}
   fs["prebuilts/build-tools/path/"+prebuiltOS+"/"+hostTool] = []byte{}
  } else {
   fs[fmt.Sprintf("host_tools/%s/Android.bp", hostTool)] = fmt.Appendf(nil, `
   host_mock_module {
    name: "%s"
   }
   `, hostTool)
  }
 }

 return GroupFixturePreparers(
  PrepareForTestWithHostMockModule,
  fs.AddToFixture(),
 )
}

[Dauer der Verarbeitung: 0.38 Sekunden, vorverarbeitet 2026-06-28]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik