Support running bp2build tests in fixtures

Previously, the fixture test infrastructure was hard coded to
initialize itself in preparation for invoking the PrepareBuildActions()
method. That meant it could not be used for testing the bp2build and
apiBp2build modes which required different initialization and called a
different method.

This change extracts that behavior into a FixtureTestRunner and adds an
implementation that allows it to test the above two modes. It then uses
that to implement the runBp2BuildTestCaseWithSetup method.

The TestPrebuiltLibraryAdditionalAttrs was the only test which broke as
it supplied an invalid path to the MockFS (it does not support using a
trailing / to represent an empty build directory). So, it was modified
to add a couple of files inside those directories instead.

Test: m nothing
Change-Id: I6798a4f761160af8d1bfed81d46de9628bda3eb9
diff --git a/android/fixture.go b/android/fixture.go
index 3f01f5a..c2b16f6 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -213,6 +213,46 @@
 	})
 }
 
+// FixtureTestRunner determines the type of test to run.
+//
+// If no custom FixtureTestRunner is provided (using the FixtureSetTestRunner) then the default test
+// runner will run a standard Soong test that corresponds to what happens when Soong is run on the
+// command line.
+type FixtureTestRunner interface {
+	// FinalPreparer is a function that is run immediately before parsing the blueprint files. It is
+	// intended to perform the initialization needed by PostParseProcessor.
+	//
+	// It returns a CustomTestResult that is passed into PostParseProcessor and returned from
+	// FixturePreparer.RunTestWithCustomResult. If it needs to return some custom data then it must
+	// provide its own implementation of CustomTestResult and return an instance of that. Otherwise,
+	// it can just return the supplied *TestResult.
+	FinalPreparer(result *TestResult) CustomTestResult
+
+	// PostParseProcessor is called after successfully parsing the blueprint files and can do further
+	// work on the result of parsing the files.
+	//
+	// Successfully parsing simply means that no errors were encountered when parsing the blueprint
+	// files.
+	//
+	// This must collate any information useful for testing, e.g. errs, ninja deps and custom data in
+	// the supplied result.
+	PostParseProcessor(result CustomTestResult)
+}
+
+// FixtureSetTestRunner sets the FixtureTestRunner in the fixture.
+//
+// It is an error if more than one of these is applied to a single fixture. If none of these are
+// applied then the fixture will use the defaultTestRunner which will run the test as if it was
+// being run in `m <target>`.
+func FixtureSetTestRunner(testRunner FixtureTestRunner) FixturePreparer {
+	return newSimpleFixturePreparer(func(fixture *fixture) {
+		if fixture.testRunner != nil {
+			panic("fixture test runner has already been set")
+		}
+		fixture.testRunner = testRunner
+	})
+}
+
 // Modify the config
 func FixtureModifyConfig(mutator func(config Config)) FixturePreparer {
 	return newSimpleFixturePreparer(func(f *fixture) {
@@ -391,6 +431,21 @@
 	// Shorthand for Fixture(t).RunTest()
 	RunTest(t *testing.T) *TestResult
 
+	// RunTestWithCustomResult runs the test just as RunTest(t) does but instead of returning a
+	// *TestResult it returns the CustomTestResult that was returned by the custom
+	// FixtureTestRunner.PostParseProcessor method that ran the test, or the *TestResult if that
+	// method returned nil.
+	//
+	// This method must be used when needing to access custom data collected by the
+	// FixtureTestRunner.PostParseProcessor method.
+	//
+	// e.g. something like this
+	//
+	//   preparers := ...FixtureSetTestRunner(&myTestRunner)...
+	//   customResult := preparers.RunTestWithCustomResult(t).(*myCustomTestResult)
+	//   doSomething(customResult.data)
+	RunTestWithCustomResult(t *testing.T) CustomTestResult
+
 	// Run the test with the supplied Android.bp file.
 	//
 	// preparer.RunTestWithBp(t, bp) is shorthand for
@@ -619,7 +674,7 @@
 	MockFS() MockFS
 
 	// Run the test, checking any errors reported and returning a TestResult instance.
-	RunTest() *TestResult
+	RunTest() CustomTestResult
 }
 
 // Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods.
@@ -642,6 +697,39 @@
 	NinjaDeps []string
 }
 
+func (r *TestResult) testResult() *TestResult { return r }
+
+// CustomTestResult is the interface that FixtureTestRunner implementations who wish to return
+// custom data must implement. It must embed *TestResult and initialize that to the value passed
+// into the method. It is returned from the FixtureTestRunner.FinalPreparer, passed into the
+// FixtureTestRunner.PostParseProcessor and returned from FixturePreparer.RunTestWithCustomResult.
+//
+// e.g. something like this:
+//
+//		type myCustomTestResult struct {
+//		    *android.TestResult
+//		    data []string
+//		}
+//
+//		func (r *myTestRunner) FinalPreparer(result *TestResult) CustomTestResult {
+//	     ... do some final test preparation ...
+//	     return &myCustomTestResult{TestResult: result)
+//	 }
+//
+//		func (r *myTestRunner) PostParseProcessor(result CustomTestResult) {
+//		    ...
+//		    myData := []string {....}
+//		    ...
+//		    customResult := result.(*myCustomTestResult)
+//	     customResult.data = myData
+//		}
+type CustomTestResult interface {
+	// testResult returns the embedded *TestResult.
+	testResult() *TestResult
+}
+
+var _ CustomTestResult = (*TestResult)(nil)
+
 type TestPathContext struct {
 	*TestResult
 }
@@ -696,6 +784,11 @@
 
 func (b *baseFixturePreparer) RunTest(t *testing.T) *TestResult {
 	t.Helper()
+	return b.RunTestWithCustomResult(t).testResult()
+}
+
+func (b *baseFixturePreparer) RunTestWithCustomResult(t *testing.T) CustomTestResult {
+	t.Helper()
 	fixture := b.self.Fixture(t)
 	return fixture.RunTest()
 }
@@ -724,13 +817,16 @@
 		ctx.SetModuleListFile(ctx.config.mockBpList)
 	}
 
-	return fixture.RunTest()
+	return fixture.RunTest().testResult()
 }
 
 type fixture struct {
 	// The preparers used to create this fixture.
 	preparers []*simpleFixturePreparer
 
+	// The test runner used in this fixture, defaults to defaultTestRunner if not set.
+	testRunner FixtureTestRunner
+
 	// The gotest state of the go test within which this was created.
 	t *testing.T
 
@@ -762,7 +858,7 @@
 	return f.mockFS
 }
 
-func (f *fixture) RunTest() *TestResult {
+func (f *fixture) RunTest() CustomTestResult {
 	f.t.Helper()
 
 	// If in debug mode output the state of the fixture before running the test.
@@ -800,30 +896,59 @@
 	// Set the NameResolver in the TestContext.
 	ctx.NameResolver = resolver
 
-	ctx.Register()
-	var ninjaDeps []string
-	extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored")
-	if len(errs) == 0 {
-		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
-		extraNinjaDeps, errs = ctx.PrepareBuildActions(f.config)
-		if len(errs) == 0 {
-			ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
-		}
+	// If test runner has not been set then use the default runner.
+	if f.testRunner == nil {
+		f.testRunner = defaultTestRunner
 	}
 
+	// Create the result to collate result information.
 	result := &TestResult{
 		testContext: testContext{ctx},
 		fixture:     f,
 		Config:      f.config,
-		Errs:        errs,
-		NinjaDeps:   ninjaDeps,
+	}
+
+	// Do any last minute preparation before parsing the blueprint files.
+	customResult := f.testRunner.FinalPreparer(result)
+
+	// Parse the blueprint files adding the information to the result.
+	extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored")
+	result.NinjaDeps = append(result.NinjaDeps, extraNinjaDeps...)
+	result.Errs = append(result.Errs, errs...)
+
+	if len(result.Errs) == 0 {
+		// If parsing the blueprint files was successful then perform any additional processing.
+		f.testRunner.PostParseProcessor(customResult)
 	}
 
 	f.errorHandler.CheckErrors(f.t, result)
 
+	return customResult
+}
+
+// standardTestRunner is the implementation of the default test runner
+type standardTestRunner struct{}
+
+func (s *standardTestRunner) FinalPreparer(result *TestResult) CustomTestResult {
+	// Register the hard coded mutators and singletons used by the standard Soong build as well as
+	// any additional instances that have been registered with this fixture.
+	result.TestContext.Register()
 	return result
 }
 
+func (s *standardTestRunner) PostParseProcessor(customResult CustomTestResult) {
+	result := customResult.(*TestResult)
+	ctx := result.TestContext
+	cfg := result.Config
+	// Prepare the build actions, i.e. run all the mutators, singletons and then invoke the
+	// GenerateAndroidBuildActions methods on all the modules.
+	extraNinjaDeps, errs := ctx.PrepareBuildActions(cfg)
+	result.NinjaDeps = append(result.NinjaDeps, extraNinjaDeps...)
+	result.CollateErrs(errs)
+}
+
+var defaultTestRunner FixtureTestRunner = &standardTestRunner{}
+
 func (f *fixture) outputDebugState() {
 	fmt.Printf("Begin Fixture State for %s\n", f.t.Name())
 	if len(f.config.env) == 0 {
@@ -909,3 +1034,10 @@
 func (r *TestResult) Module(name string, variant string) Module {
 	return r.ModuleForTests(name, variant).Module()
 }
+
+// CollateErrs adds additional errors to the result and returns true if there is more than one
+// error in the result.
+func (r *TestResult) CollateErrs(errs []error) bool {
+	r.Errs = append(r.Errs, errs...)
+	return len(r.Errs) > 0
+}