Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2034187a authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes I22f90c90,I2d965212,Ib7d421f5

* changes:
  Separate the collation of singletons from registration
  Defer registration of singletons and pre-singletons in TestContext
  Ensure mutators used in tests are in the same order as at runtime
parents 6950702b 42d0b931
Loading
Loading
Loading
Loading
+19 −11
Original line number Diff line number Diff line
@@ -186,23 +186,31 @@ func (ctx *Context) Register() {
		t.register(ctx)
	}

	singletons.registerAll(ctx)

	mutators := collateGloballyRegisteredMutators()
	mutators.registerAll(ctx)

	ctx.RegisterSingletonType("bazeldeps", SingletonFactoryAdaptor(ctx, BazelSingleton))
	singletons := collateGloballyRegisteredSingletons()
	singletons.registerAll(ctx)
}

func collateGloballyRegisteredSingletons() sortableComponents {
	allSingletons := append(sortableComponents(nil), singletons...)
	allSingletons = append(allSingletons,
		singleton{false, "bazeldeps", BazelSingleton},

		// Register phony just before makevars so it can write out its phony rules as Make rules
	ctx.RegisterSingletonType("phony", SingletonFactoryAdaptor(ctx, phonySingletonFactory))
		singleton{false, "phony", phonySingletonFactory},

		// Register makevars after other singletons so they can export values through makevars
	ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(ctx, makeVarsSingletonFunc))
		singleton{false, "makevars", makeVarsSingletonFunc},

		// Register env and ninjadeps last so that they can track all used environment variables and
		// Ninja file dependencies stored in the config.
	ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(ctx, EnvSingleton))
	ctx.RegisterSingletonType("ninjadeps", SingletonFactoryAdaptor(ctx, ninjaDepsSingletonFactory))
		singleton{false, "env", EnvSingleton},
		singleton{false, "ninjadeps", ninjaDepsSingletonFactory},
	)

	return allSingletons
}

func ModuleTypeFactories() map[string]ModuleFactory {
+217 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import (
	"regexp"
	"sort"
	"strings"
	"sync"
	"testing"

	"github.com/google/blueprint"
@@ -96,6 +97,12 @@ type TestContext struct {
	preArch, preDeps, postDeps, finalDeps           []RegisterMutatorFunc
	bp2buildPreArch, bp2buildDeps, bp2buildMutators []RegisterMutatorFunc
	NameResolver                                    *NameResolver

	// The list of pre-singletons and singletons registered for the test.
	preSingletons, singletons sortableComponents

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

func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
@@ -140,11 +147,219 @@ func (ctx *TestContext) DepsBp2BuildMutators(f RegisterMutatorFunc) {
	ctx.bp2buildDeps = append(ctx.bp2buildDeps, f)
}

// 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 := 0, 0; 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
}

// 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)
	})
}

// 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()

	ctx.preSingletons.registerAll(ctx.Context)

	mutators := collateRegisteredMutators(ctx.preArch, ctx.preDeps, ctx.postDeps, 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)

	// Register the env singleton with this context before sorting.
	ctx.RegisterSingletonType("env", EnvSingleton)

	ctx.singletons.registerAll(ctx.Context)

	// Save the mutator order away to make it easy to access while debugging.
	ctx.mutatorOrder = globalOrder.mutatorOrder.namesInOrder
}

// RegisterForBazelConversion prepares a test context for bp2build conversion.
@@ -175,11 +390,11 @@ func (ctx *TestContext) RegisterSingletonModuleType(name string, factory Singlet
}

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

func (ctx *TestContext) RegisterPreSingletonType(name string, factory SingletonFactory) {
	ctx.Context.RegisterPreSingletonType(name, SingletonFactoryAdaptor(ctx.Context, factory))
	ctx.preSingletons = append(ctx.preSingletons, newPreSingleton(name, factory))
}

func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule {