Make rust_test file output more similar to cc_test.

This changes the way the output filename is calculated for rust_test
binaries to be more similar to cc_test.

This also removes the option to define multiple test binaries in a
single rust_test module via the TestPerSrc mutator. Now each rust_test
module corresponds to a single test binary.

Bug: 158500462
Test: m -j pin-utils_tests_pin_utils
Test: m -j unicode-xid_device_tests_unicode_xid
Change-Id: I6e0f79dcb4e49fa49d6ebb36abeef67a9eb180a0
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 0e2bea3..69d0df5 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -99,7 +99,6 @@
 func (test *testDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
 	test.binaryDecorator.AndroidMk(ctx, ret)
 	ret.Class = "NATIVE_TESTS"
-	ret.SubName = test.getMutatedModuleSubName(ctx.Name())
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		if len(test.Properties.Test_suites) > 0 {
 			fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
diff --git a/rust/rust.go b/rust/rust.go
index 8cf2e6d..6671dd3 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -39,7 +39,6 @@
 	android.RegisterModuleType("rust_defaults", defaultsFactory)
 	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
-		ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 	pctx.Import("android/soong/rust/config")
diff --git a/rust/test.go b/rust/test.go
index 10c2785..f616c06 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -15,14 +15,16 @@
 package rust
 
 import (
-	"path/filepath"
-	"strings"
-
 	"android/soong/android"
 	"android/soong/tradefed"
 )
 
 type TestProperties struct {
+	// Disables the creation of a test-specific directory when used with
+	// relative_install_path. Useful if several tests need to be in the same
+	// directory, but test_per_src doesn't work.
+	No_named_install_directory *bool
+
 	// the name of the test configuration (for example "AndroidTest.xml") that should be
 	// installed with the module.
 	Test_config *string `android:"path,arch_variant"`
@@ -64,7 +66,7 @@
 	}
 
 	module.compiler = test
-
+	module.AddProperties(&test.Properties)
 	return module, test
 }
 
@@ -72,36 +74,21 @@
 	return append(test.binaryDecorator.compilerProps(), &test.Properties)
 }
 
-func (test *testDecorator) getMutatedModuleSubName(moduleName string) string {
-	stem := String(test.baseCompiler.Properties.Stem)
-	if stem != "" && !strings.HasSuffix(moduleName, "_"+stem) {
-		// Avoid repeated suffix in the module name.
-		return "_" + stem
-	}
-	return ""
-}
-
 func (test *testDecorator) install(ctx ModuleContext, file android.Path) {
-	name := ctx.ModuleName()
-	path := test.baseCompiler.relativeInstallPath()
-	// on device, use mutated module name
-	name = name + test.getMutatedModuleSubName(name)
-	if !ctx.Device() { // on host, use mutated module name + arch type + stem name
-		stem := String(test.baseCompiler.Properties.Stem)
-		if stem == "" {
-			stem = name
-		}
-		name = filepath.Join(name, ctx.Arch().ArchType.String(), stem)
-	}
-	test.testConfig = tradefed.AutoGenRustTestConfig(ctx, name,
+	test.testConfig = tradefed.AutoGenRustTestConfig(ctx,
 		test.Properties.Test_config,
 		test.Properties.Test_config_template,
 		test.Properties.Test_suites,
+		nil,
 		test.Properties.Auto_gen_config)
+
 	// default relative install path is module name
-	if path == "" {
+	if !Bool(test.Properties.No_named_install_directory) {
 		test.baseCompiler.relative = ctx.ModuleName()
+	} else if String(test.baseCompiler.Properties.Relative_install_path) == "" {
+		ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
 	}
+
 	test.binaryDecorator.install(ctx, file)
 }
 
@@ -126,64 +113,3 @@
 	module, _ := NewRustTest(android.HostSupported)
 	return module.Init()
 }
-
-func (test *testDecorator) testPerSrc() bool {
-	return true
-}
-
-func (test *testDecorator) srcs() []string {
-	return test.binaryDecorator.Properties.Srcs
-}
-
-func (test *testDecorator) setSrc(name, src string) {
-	test.binaryDecorator.Properties.Srcs = []string{src}
-	test.baseCompiler.Properties.Stem = StringPtr(name)
-}
-
-func (test *testDecorator) unsetSrc() {
-	test.binaryDecorator.Properties.Srcs = nil
-	test.baseCompiler.Properties.Stem = StringPtr("")
-}
-
-type testPerSrc interface {
-	testPerSrc() bool
-	srcs() []string
-	setSrc(string, string)
-	unsetSrc()
-}
-
-var _ testPerSrc = (*testDecorator)(nil)
-
-func TestPerSrcMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok {
-		if test, ok := m.compiler.(testPerSrc); ok {
-			numTests := len(test.srcs())
-			if test.testPerSrc() && numTests > 0 {
-				if duplicate, found := android.CheckDuplicate(test.srcs()); found {
-					mctx.PropertyErrorf("srcs", "found a duplicate entry %q", duplicate)
-					return
-				}
-				// Rust compiler always compiles one source file at a time and
-				// uses the crate name as output file name.
-				// Cargo uses the test source file name as default crate name,
-				// but that can be redefined.
-				// So when there are multiple source files, the source file names will
-				// be the output file names, but when there is only one test file,
-				// use the crate name.
-				testNames := make([]string, numTests)
-				for i, src := range test.srcs() {
-					testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
-				}
-				crateName := m.compiler.crateName()
-				if numTests == 1 && crateName != "" {
-					testNames[0] = crateName
-				}
-				// TODO(chh): Add an "all tests" variation like cc/test.go?
-				tests := mctx.CreateLocalVariations(testNames...)
-				for i, src := range test.srcs() {
-					tests[i].(*Module).compiler.(testPerSrc).setSrc(testNames[i], src)
-				}
-			}
-		}
-	}
-}
diff --git a/rust/test_test.go b/rust/test_test.go
index f131c6e..2382b18 100644
--- a/rust/test_test.go
+++ b/rust/test_test.go
@@ -19,45 +19,17 @@
 	"testing"
 )
 
-// Check if rust_test_host accepts multiple source files and applies --test flag.
 func TestRustTest(t *testing.T) {
 	ctx := testRust(t, `
 		rust_test_host {
 			name: "my_test",
-			srcs: ["foo.rs", "src/bar.rs"],
-			crate_name: "new_test", // not used for multiple source files
-			relative_install_path: "rust/my-test",
-		}`)
-
-	for _, name := range []string{"foo", "bar"} {
-		testingModule := ctx.ModuleForTests("my_test", "linux_glibc_x86_64_"+name)
-		testingBuildParams := testingModule.Output(name)
-		rustcFlags := testingBuildParams.Args["rustcFlags"]
-		if !strings.Contains(rustcFlags, "--test") {
-			t.Errorf("%v missing --test flag, rustcFlags: %#v", name, rustcFlags)
-		}
-		outPath := "/my_test/linux_glibc_x86_64_" + name + "/" + name
-		if !strings.Contains(testingBuildParams.Output.String(), outPath) {
-			t.Errorf("wrong output: %v  expect: %v", testingBuildParams.Output, outPath)
-		}
-	}
-}
-
-// crate_name is output file name, when there is only one source file.
-func TestRustTestSingleFile(t *testing.T) {
-	ctx := testRust(t, `
-		rust_test_host {
-			name: "my-test",
 			srcs: ["foo.rs"],
-			crate_name: "new_test",
-			relative_install_path: "my-pkg",
 		}`)
 
-	name := "new_test"
-	testingModule := ctx.ModuleForTests("my-test", "linux_glibc_x86_64_"+name)
-	outPath := "/my-test/linux_glibc_x86_64_" + name + "/" + name
-	testingBuildParams := testingModule.Output(name)
-	if !strings.Contains(testingBuildParams.Output.String(), outPath) {
-		t.Errorf("wrong output: %v  expect: %v", testingBuildParams.Output, outPath)
+	testingModule := ctx.ModuleForTests("my_test", "linux_glibc_x86_64")
+	expectedOut := "my_test/linux_glibc_x86_64/my_test"
+	outPath := testingModule.Output("my_test").Output.String()
+	if !strings.Contains(outPath, expectedOut) {
+		t.Errorf("wrong output path: %v;  expected: %v", outPath, expectedOut)
 	}
 }
diff --git a/rust/testing.go b/rust/testing.go
index f94af71..4e186d3 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -97,7 +97,6 @@
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		// rust mutators
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
-		ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 	ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index 2829146..8f0bf39 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -220,19 +220,20 @@
 	return path
 }
 
-func AutoGenRustTestConfig(ctx android.ModuleContext, name string, testConfigProp *string,
-	testConfigTemplateProp *string, testSuites []string, autoGenConfig *bool) android.Path {
+func AutoGenRustTestConfig(ctx android.ModuleContext, testConfigProp *string,
+	testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool) android.Path {
 	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
 	if autogenPath != nil {
-		templatePathString := "${RustHostTestConfigTemplate}"
-		if ctx.Device() {
-			templatePathString = "${RustDeviceTestConfigTemplate}"
-		}
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
-			templatePathString = templatePath.String()
+			autogenTemplate(ctx, autogenPath, templatePath.String(), config)
+		} else {
+			if ctx.Device() {
+				autogenTemplate(ctx, autogenPath, "${RustDeviceTestConfigTemplate}", config)
+			} else {
+				autogenTemplate(ctx, autogenPath, "${RustHostTestConfigTemplate}", config)
+			}
 		}
-		autogenTemplateWithName(ctx, name, autogenPath, templatePathString, nil)
 		return autogenPath
 	}
 	return path