Fix install location for vendor tests

These should be install in /data/nativetest* with the rest of the tests,
but had been moved to /vendor/nativetest* accidentally. Add some tests
so that this doesn't happen again.

Bug: 63393698
Test: m -j blueprint_tools
Test: compare out/soong/Android-aosp_arm64.mk
Test: compare out/soong/build.ninja
Change-Id: Id3b08a7e3908955df18a043a02ea576dc88086c3
diff --git a/android/config.go b/android/config.go
index 1f29d94..dbb9fa4 100644
--- a/android/config.go
+++ b/android/config.go
@@ -88,8 +88,7 @@
 }
 
 type deviceConfig struct {
-	config  *config
-	targets []Arch
+	config *config
 	OncePer
 }
 
@@ -167,9 +166,18 @@
 
 // TestConfig returns a Config object suitable for using for tests
 func TestConfig(buildDir string) Config {
-	return Config{&config{
+	config := &config{
+		ProductVariables: productVariables{
+			DeviceName: stringPtr("test_device"),
+		},
+
 		buildDir: buildDir,
-	}}
+	}
+	config.deviceConfig = &deviceConfig{
+		config: config,
+	}
+
+	return Config{config}
 }
 
 // New creates a new Config object.  The srcDir argument specifies the path to
@@ -182,16 +190,12 @@
 
 		srcDir:   srcDir,
 		buildDir: buildDir,
-
-		deviceConfig: &deviceConfig{},
 	}
 
-	deviceConfig := &deviceConfig{
+	config.deviceConfig = &deviceConfig{
 		config: config,
 	}
 
-	config.deviceConfig = deviceConfig
-
 	// Sanity check the build and source directories. This won't catch strange
 	// configurations with symlinks, but at least checks the obvious cases.
 	absBuildDir, err := filepath.Abs(buildDir)
diff --git a/android/paths.go b/android/paths.go
index aa06127..b5b4730 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -39,6 +39,17 @@
 var _ PathContext = blueprint.SingletonContext(nil)
 var _ PathContext = blueprint.ModuleContext(nil)
 
+type ModuleInstallPathContext interface {
+	PathContext
+
+	androidBaseContext
+
+	InstallInData() bool
+	InstallInSanitizerDir() bool
+}
+
+var _ ModuleInstallPathContext = ModuleContext(nil)
+
 // errorfContext is the interface containing the Errorf method matching the
 // Errorf method in blueprint.SingletonContext.
 type errorfContext interface {
@@ -669,14 +680,14 @@
 
 // PathForModuleInstall returns a Path representing the install path for the
 // module appended with paths...
-func PathForModuleInstall(ctx ModuleContext, pathComponents ...string) OutputPath {
+func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) OutputPath {
 	var outPaths []string
 	if ctx.Device() {
 		var partition string
-		if ctx.Vendor() {
-			partition = ctx.DeviceConfig().VendorPath()
-		} else if ctx.InstallInData() {
+		if ctx.InstallInData() {
 			partition = "data"
+		} else if ctx.Vendor() {
+			partition = ctx.DeviceConfig().VendorPath()
 		} else {
 			partition = "system"
 		}
diff --git a/android/paths_test.go b/android/paths_test.go
index 9d69473..3986b71 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -20,6 +20,8 @@
 	"reflect"
 	"strings"
 	"testing"
+
+	"github.com/google/blueprint/pathtools"
 )
 
 type strsTestCase struct {
@@ -180,3 +182,161 @@
 		return fmt.Sprintf("%#v", in)
 	}
 }
+
+type moduleInstallPathContextImpl struct {
+	androidBaseContextImpl
+
+	inData         bool
+	inSanitizerDir bool
+}
+
+func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem {
+	return pathtools.MockFs(nil)
+}
+
+func (m moduleInstallPathContextImpl) Config() interface{} {
+	return m.androidBaseContextImpl.config
+}
+
+func (moduleInstallPathContextImpl) AddNinjaFileDeps(deps ...string) {}
+
+func (m moduleInstallPathContextImpl) InstallInData() bool {
+	return m.inData
+}
+
+func (m moduleInstallPathContextImpl) InstallInSanitizerDir() bool {
+	return m.inSanitizerDir
+}
+
+func TestPathForModuleInstall(t *testing.T) {
+	testConfig := TestConfig("")
+
+	hostTarget := Target{Os: Linux}
+	deviceTarget := Target{Os: Android}
+
+	testCases := []struct {
+		name string
+		ctx  *moduleInstallPathContextImpl
+		in   []string
+		out  string
+	}{
+		{
+			name: "host binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: hostTarget,
+				},
+			},
+			in:  []string{"bin", "my_test"},
+			out: "host/linux-x86/bin/my_test",
+		},
+
+		{
+			name: "system binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+				},
+			},
+			in:  []string{"bin", "my_test"},
+			out: "target/product/test_device/system/bin/my_test",
+		},
+		{
+			name: "vendor binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					vendor: true,
+				},
+			},
+			in:  []string{"bin", "my_test"},
+			out: "target/product/test_device/vendor/bin/my_test",
+		},
+
+		{
+			name: "system native test binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+				},
+				inData: true,
+			},
+			in:  []string{"nativetest", "my_test"},
+			out: "target/product/test_device/data/nativetest/my_test",
+		},
+		{
+			name: "vendor native test binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					vendor: true,
+				},
+				inData: true,
+			},
+			in:  []string{"nativetest", "my_test"},
+			out: "target/product/test_device/data/nativetest/my_test",
+		},
+
+		{
+			name: "sanitized system binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+				},
+				inSanitizerDir: true,
+			},
+			in:  []string{"bin", "my_test"},
+			out: "target/product/test_device/data/asan/system/bin/my_test",
+		},
+		{
+			name: "sanitized vendor binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					vendor: true,
+				},
+				inSanitizerDir: true,
+			},
+			in:  []string{"bin", "my_test"},
+			out: "target/product/test_device/data/asan/vendor/bin/my_test",
+		},
+
+		{
+			name: "sanitized system native test binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+				},
+				inData:         true,
+				inSanitizerDir: true,
+			},
+			in:  []string{"nativetest", "my_test"},
+			out: "target/product/test_device/data/asan/data/nativetest/my_test",
+		},
+		{
+			name: "sanitized vendor native test binary",
+			ctx: &moduleInstallPathContextImpl{
+				androidBaseContextImpl: androidBaseContextImpl{
+					target: deviceTarget,
+					vendor: true,
+				},
+				inData:         true,
+				inSanitizerDir: true,
+			},
+			in:  []string{"nativetest", "my_test"},
+			out: "target/product/test_device/data/asan/data/nativetest/my_test",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			tc.ctx.androidBaseContextImpl.config = testConfig
+			output := PathForModuleInstall(tc.ctx, tc.in...)
+			if output.basePath.path != tc.out {
+				t.Errorf("unexpected path:\n got: %q\nwant: %q\n",
+					output.basePath.path,
+					tc.out)
+			}
+		})
+	}
+}