Handle `test_per_src` test variations as dependencies of APEX modules.

If a test module with a `test_per_src` property set to `true` is
included in an APEX module, add all the variants for mutator
`test_per_src` as dependencies of the APEX module (not just the
first one).

This is done by adding variation "" of mutator `test_per_src` when
adding a test dependency to an APEX module, which creates an indirect
dependency of the APEX module on all the `test_per_src` variants of
the test module. When generating outputs for the APEX bundle, fetch
and include the set of test outputs from the "" variant.

Test: m (`apex/apex_test.go` amended)
Bug: 129534335
Change-Id: I1c99855971a8a9b2fc5b964a420e882b6791d4e6
diff --git a/apex/apex.go b/apex/apex.go
index 284af5c..4e6c0b1 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -449,6 +449,7 @@
 	ctx.AddFarVariationDependencies([]blueprint.Variation{
 		{Mutator: "arch", Variation: arch},
 		{Mutator: "image", Variation: imageVariation},
+		{Mutator: "test_per_src", Variation: ""}, // "" is the all-tests variant
 	}, testTag, tests...)
 }
 
@@ -492,6 +493,7 @@
 		ctx.AddFarVariationDependencies([]blueprint.Variation{
 			{Mutator: "arch", Variation: target.String()},
 			{Mutator: "image", Variation: a.getImageVariation(config)},
+			{Mutator: "test_per_src", Variation: ""}, // "" is the all-tests variant
 		}, testTag, a.properties.Tests...)
 
 		// Add native modules targetting both ABIs
@@ -692,6 +694,12 @@
 	return
 }
 
+func getCopyManifestForTestPerSrcExecutables(cc *cc.Module) (filesToCopy []android.Path, dirInApex string) {
+	dirInApex = filepath.Join("bin", cc.RelativeInstallPath())
+	filesToCopy = cc.TestPerSrcOutputFiles()
+	return
+}
+
 func getCopyManifestForPyBinary(py *python.Module) (fileToCopy android.Path, dirInApex string) {
 	dirInApex = "bin"
 	fileToCopy = py.HostToolPath().Path()
@@ -827,8 +835,22 @@
 				}
 			case testTag:
 				if cc, ok := child.(*cc.Module); ok {
-					fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeTest, cc, nil})
+					if cc.TestPerSrcOutputFiles() != nil {
+						// Multiple-output test module (using `test_per_src`).
+						filesToCopy, dirInApex := getCopyManifestForTestPerSrcExecutables(cc)
+						for _, fileToCopy := range filesToCopy {
+							// Handle modules created as `test_per_src` variations of a single test module:
+							// replace the name of the original test module (`depName`, shared by all
+							// `test_per_src` variants of that module) with the name of the generated test
+							// binary.
+							moduleName := filepath.Base(fileToCopy.String())
+							filesInfo = append(filesInfo, apexFile{fileToCopy, moduleName, dirInApex, nativeTest, cc, nil})
+						}
+					} else {
+						// Single-output test module (not using `test_per_src`).
+						fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
+						filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeTest, cc, nil})
+					}
 					return true
 				} else {
 					ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 5fc0738..bb0c4c5 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -98,6 +98,7 @@
 		ctx.BottomUp("image", cc.ImageMutator).Parallel()
 		ctx.BottomUp("link", cc.LinkageMutator).Parallel()
 		ctx.BottomUp("vndk", cc.VndkMutator).Parallel()
+		ctx.BottomUp("test_per_src", cc.TestPerSrcMutator).Parallel()
 		ctx.BottomUp("version", cc.VersionMutator).Parallel()
 		ctx.BottomUp("begin", cc.BeginMutator).Parallel()
 	})
@@ -197,8 +198,11 @@
 		"system/sepolicy/apex/otherapex-file_contexts":      nil,
 		"system/sepolicy/apex/commonapex-file_contexts":     nil,
 		"mylib.cpp":                            nil,
-		"mytest.cpp":                           nil,
 		"mylib_common.cpp":                     nil,
+		"mytest.cpp":                           nil,
+		"mytest1.cpp":                          nil,
+		"mytest2.cpp":                          nil,
+		"mytest3.cpp":                          nil,
 		"myprebuilt":                           nil,
 		"my_include":                           nil,
 		"vendor/foo/devkeys/test.x509.pem":     nil,
@@ -1367,6 +1371,7 @@
 			key: "myapex.key",
 			tests: [
 				"mytest",
+				"mytests",
 			],
 		}
 
@@ -1385,6 +1390,21 @@
 			static_executable: true,
 			stl: "none",
 		}
+
+		cc_test {
+			name: "mytests",
+			gtest: false,
+			srcs: [
+				"mytest1.cpp",
+				"mytest2.cpp",
+				"mytest3.cpp",
+			],
+			test_per_src: true,
+			relative_install_path: "test",
+			system_shared_libs: [],
+			static_executable: true,
+			stl: "none",
+		}
 	`)
 
 	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
@@ -1392,6 +1412,11 @@
 
 	// Ensure that test dep is copied into apex.
 	ensureContains(t, copyCmds, "image.apex/bin/test/mytest")
+
+	// Ensure that test deps built with `test_per_src` are copied into apex.
+	ensureContains(t, copyCmds, "image.apex/bin/test/mytest1")
+	ensureContains(t, copyCmds, "image.apex/bin/test/mytest2")
+	ensureContains(t, copyCmds, "image.apex/bin/test/mytest3")
 }
 
 func TestApexUsesOtherApex(t *testing.T) {