Create additional test target for go modules in bp2build

For go modules with non-empty testSrcs, we will create an additional
go_test target. To build a standalone test executable, we need to
include the source files in the compilation unit. This will be done
using the `embed` attribute
1. For tests of go_library, this is straightforward. It will embed the
   go_library and "inherit" its .go files and deps
2. For tetss of go_binary, we need to create an additional go_source
   target since go_binary cannot be embedded inside a go_test

Using `b test` for these tests revealed that certain tests are not
hermitic and rely on `testdata` files checked into the tree. This CL
introduces an allowlist to skip generating go_test targets for them.

Test: bp2build unit test
Test: TH
Bug: 284483729
Bug: 288491147
Change-Id: Ic736d655babc2f6067e4da75384900b7b8bdc2ed
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 0e6596b..0ac01ea 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -353,9 +353,91 @@
 	Importpath             bazel.StringAttribute
 	Srcs                   bazel.LabelListAttribute
 	Deps                   bazel.LabelListAttribute
+	Data                   bazel.LabelListAttribute
 	Target_compatible_with bazel.LabelListAttribute
+
+	// attributes for the dynamically generated go_test target
+	Embed bazel.LabelListAttribute
 }
 
+type goTestProperties struct {
+	name           string
+	dir            string
+	testSrcs       []string
+	linuxTestSrcs  []string
+	darwinTestSrcs []string
+	testData       []string
+	// Name of the target that should be compiled together with the test
+	embedName string
+}
+
+// Creates a go_test target for bootstrap_go_package / blueprint_go_binary
+func generateBazelTargetsGoTest(ctx *android.Context, goModulesMap nameToGoLibraryModule, gp goTestProperties) (BazelTarget, error) {
+	ca := android.CommonAttributes{
+		Name: gp.name,
+	}
+	ga := goAttributes{
+		Srcs: goSrcLabels(ctx.Config(), gp.dir, gp.testSrcs, gp.linuxTestSrcs, gp.darwinTestSrcs),
+		Data: goSrcLabels(ctx.Config(), gp.dir, gp.testData, []string{}, []string{}),
+		Embed: bazel.MakeLabelListAttribute(
+			bazel.MakeLabelList(
+				[]bazel.Label{bazel.Label{Label: ":" + gp.embedName}},
+			),
+		),
+		Target_compatible_with: targetNotCompatibleWithAndroid(),
+	}
+
+	libTest := goBazelTarget{
+		targetName:            gp.name,
+		targetPackage:         gp.dir,
+		bazelRuleClass:        "go_test",
+		bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl",
+		bazelAttributes:       []interface{}{&ca, &ga},
+	}
+	return generateBazelTarget(ctx, libTest)
+}
+
+// TODO - b/288491147: testSrcs of certain bootstrap_go_package/blueprint_go_binary are not hermetic and depend on
+// testdata checked into the filesystem.
+// Denylist the generation of go_test targets for these Soong modules.
+// The go_library/go_binary will still be generated, since those are hermitic.
+var (
+	goTestsDenylist = []string{
+		"android-archive-zip",
+		"bazel_notice_gen",
+		"blueprint-bootstrap-bpdoc",
+		"blueprint-microfactory",
+		"blueprint-pathtools",
+		"bssl_ar",
+		"compliance_checkmetadata",
+		"compliance_checkshare",
+		"compliance_dumpgraph",
+		"compliance_dumpresolutions",
+		"compliance_listshare",
+		"compliance-module",
+		"compliancenotice_bom",
+		"compliancenotice_shippedlibs",
+		"compliance_rtrace",
+		"compliance_sbom",
+		"golang-protobuf-internal-fuzz-jsonfuzz",
+		"golang-protobuf-internal-fuzz-textfuzz",
+		"golang-protobuf-internal-fuzz-wirefuzz",
+		"htmlnotice",
+		"protoc-gen-go",
+		"rbcrun-module",
+		"spdx-tools-builder",
+		"spdx-tools-builder2v1",
+		"spdx-tools-builder2v2",
+		"spdx-tools-builder2v3",
+		"spdx-tools-idsearcher",
+		"spdx-tools-spdx-json",
+		"spdx-tools-utils",
+		"soong-ui-build",
+		"textnotice",
+		"xmlnotice",
+	}
+)
+
 func generateBazelTargetsGoPackage(ctx *android.Context, g *bootstrap.GoPackage, goModulesMap nameToGoLibraryModule) ([]BazelTarget, []error) {
 	ca := android.CommonAttributes{
 		Name: g.Name(),
@@ -390,12 +472,33 @@
 		bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl",
 		bazelAttributes:       []interface{}{&ca, &ga},
 	}
-	// TODO - b/284483729: Create go_test target from testSrcs
-	libTarget, err := generateBazelTarget(ctx, lib)
-	if err != nil {
-		return []BazelTarget{}, []error{err}
+	retTargets := []BazelTarget{}
+	var retErrs []error
+	if libTarget, err := generateBazelTarget(ctx, lib); err == nil {
+		retTargets = append(retTargets, libTarget)
+	} else {
+		retErrs = []error{err}
 	}
-	return []BazelTarget{libTarget}, nil
+
+	// If the library contains test srcs, create an additional go_test target
+	if !android.InList(g.Name(), goTestsDenylist) && (len(g.TestSrcs()) > 0 || len(g.LinuxTestSrcs()) > 0 || len(g.DarwinTestSrcs()) > 0) {
+		gp := goTestProperties{
+			name:           g.Name() + "-test",
+			dir:            ctx.ModuleDir(g),
+			testSrcs:       g.TestSrcs(),
+			linuxTestSrcs:  g.LinuxTestSrcs(),
+			darwinTestSrcs: g.DarwinTestSrcs(),
+			testData:       g.TestData(),
+			embedName:      g.Name(), // embed the source go_library in the test so that its .go files are included in the compilation unit
+		}
+		if libTestTarget, err := generateBazelTargetsGoTest(ctx, goModulesMap, gp); err == nil {
+			retTargets = append(retTargets, libTestTarget)
+		} else {
+			retErrs = append(retErrs, err)
+		}
+	}
+
+	return retTargets, retErrs
 }
 
 type goLibraryModule struct {
@@ -440,6 +543,9 @@
 		Name: g.Name(),
 	}
 
+	retTargets := []BazelTarget{}
+	var retErrs []error
+
 	// For this bootstrap_go_package dep chain,
 	// A --> B --> C ( ---> depends on)
 	// Soong provides the convenience of only listing B as deps of A even if a src file of A imports C
@@ -450,12 +556,70 @@
 	// bp2build does not have sufficient info on whether C is a direct dep of A or not, so for now collect all transitive deps and add them to deps
 	transitiveDeps := transitiveGoDeps(g.Deps(), goModulesMap)
 
+	goSource := ""
+	// If the library contains test srcs, create an additional go_test target
+	// The go_test target will embed a go_source containining the source .go files it tests
+	if !android.InList(g.Name(), goTestsDenylist) && (len(g.TestSrcs()) > 0 || len(g.LinuxTestSrcs()) > 0 || len(g.DarwinTestSrcs()) > 0) {
+		// Create a go_source containing the source .go files of go_library
+		// This target will be an `embed` of the go_binary and go_test
+		goSource = g.Name() + "-source"
+		ca := android.CommonAttributes{
+			Name: goSource,
+		}
+		ga := goAttributes{
+			Srcs:                   goSrcLabels(ctx.Config(), ctx.ModuleDir(g), g.Srcs(), g.LinuxSrcs(), g.DarwinSrcs()),
+			Deps:                   goDepLabels(transitiveDeps, goModulesMap),
+			Target_compatible_with: targetNotCompatibleWithAndroid(),
+		}
+		libTestSource := goBazelTarget{
+			targetName:            goSource,
+			targetPackage:         ctx.ModuleDir(g),
+			bazelRuleClass:        "go_source",
+			bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl",
+			bazelAttributes:       []interface{}{&ca, &ga},
+		}
+		if libSourceTarget, err := generateBazelTarget(ctx, libTestSource); err == nil {
+			retTargets = append(retTargets, libSourceTarget)
+		} else {
+			retErrs = append(retErrs, err)
+		}
+
+		// Create a go_test target
+		gp := goTestProperties{
+			name:           g.Name() + "-test",
+			dir:            ctx.ModuleDir(g),
+			testSrcs:       g.TestSrcs(),
+			linuxTestSrcs:  g.LinuxTestSrcs(),
+			darwinTestSrcs: g.DarwinTestSrcs(),
+			testData:       g.TestData(),
+			// embed the go_source in the test
+			embedName: g.Name() + "-source",
+		}
+		if libTestTarget, err := generateBazelTargetsGoTest(ctx, goModulesMap, gp); err == nil {
+			retTargets = append(retTargets, libTestTarget)
+		} else {
+			retErrs = append(retErrs, err)
+		}
+
+	}
+
+	// Create a go_binary target
 	ga := goAttributes{
-		Srcs:                   goSrcLabels(ctx.Config(), ctx.ModuleDir(g), g.Srcs(), g.LinuxSrcs(), g.DarwinSrcs()),
 		Deps:                   goDepLabels(transitiveDeps, goModulesMap),
 		Target_compatible_with: targetNotCompatibleWithAndroid(),
 	}
 
+	// If the binary has testSrcs, embed the common `go_source`
+	if goSource != "" {
+		ga.Embed = bazel.MakeLabelListAttribute(
+			bazel.MakeLabelList(
+				[]bazel.Label{bazel.Label{Label: ":" + goSource}},
+			),
+		)
+	} else {
+		ga.Srcs = goSrcLabels(ctx.Config(), ctx.ModuleDir(g), g.Srcs(), g.LinuxSrcs(), g.DarwinSrcs())
+	}
+
 	bin := goBazelTarget{
 		targetName:            g.Name(),
 		targetPackage:         ctx.ModuleDir(g),
@@ -463,12 +627,14 @@
 		bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl",
 		bazelAttributes:       []interface{}{&ca, &ga},
 	}
-	// TODO - b/284483729: Create go_test target from testSrcs
-	binTarget, err := generateBazelTarget(ctx, bin)
-	if err != nil {
-		return []BazelTarget{}, []error{err}
+
+	if binTarget, err := generateBazelTarget(ctx, bin); err == nil {
+		retTargets = append(retTargets, binTarget)
+	} else {
+		retErrs = []error{err}
 	}
-	return []BazelTarget{binTarget}, nil
+
+	return retTargets, retErrs
 }
 
 func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (conversionResults, []error) {
diff --git a/bp2build/go_conversion_test.go b/bp2build/go_conversion_test.go
index 507fbf0..2387641 100644
--- a/bp2build/go_conversion_test.go
+++ b/bp2build/go_conversion_test.go
@@ -45,11 +45,17 @@
 		srcs: [
 			"foo_linux.go",
 		],
+		testSrcs: [
+			"foo_linux_test.go",
+		],
 	},
 	darwin: {
 		srcs: [
 			"foo_darwin.go",
 		],
+		testSrcs: [
+			"foo_darwin_test.go",
+		],
 	},
 	testSrcs: [
 		"foo1_test.go",
@@ -84,7 +90,21 @@
     })`,
 			},
 			android.HostSupported,
-		)},
+		),
+			makeBazelTargetHostOrDevice("go_test", "foo-test",
+				AttrNameToString{
+					"embed": `[":foo"]`,
+					"srcs": `[
+        "foo1_test.go",
+        "foo2_test.go",
+    ] + select({
+        "//build/bazel/platforms/os:darwin": ["foo_darwin_test.go"],
+        "//build/bazel/platforms/os:linux_glibc": ["foo_linux_test.go"],
+        "//conditions:default": [],
+    })`,
+				},
+				android.HostSupported,
+			)},
 	})
 }
 
@@ -125,6 +145,44 @@
 	})
 }
 
+func TestConvertGoBinaryWithTestSrcs(t *testing.T) {
+	bp := `
+blueprint_go_binary {
+	name: "foo",
+	srcs: ["main.go"],
+	testSrcs: ["main_test.go"],
+}
+`
+	t.Parallel()
+	runGoTests(t, Bp2buildTestCase{
+		Description: "Convert blueprint_go_binary with testSrcs",
+		Blueprint:   bp,
+		ExpectedBazelTargets: []string{
+			makeBazelTargetHostOrDevice("go_binary", "foo",
+				AttrNameToString{
+					"deps":  `[]`,
+					"embed": `[":foo-source"]`,
+				},
+				android.HostSupported,
+			),
+			makeBazelTargetHostOrDevice("go_source", "foo-source",
+				AttrNameToString{
+					"deps": `[]`,
+					"srcs": `["main.go"]`,
+				},
+				android.HostSupported,
+			),
+			makeBazelTargetHostOrDevice("go_test", "foo-test",
+				AttrNameToString{
+					"embed": `[":foo-source"]`,
+					"srcs":  `["main_test.go"]`,
+				},
+				android.HostSupported,
+			),
+		},
+	})
+}
+
 func TestConvertGoBinaryWithSrcInDifferentPackage(t *testing.T) {
 	bp := `
 blueprint_go_binary {