rust: refactor tests setup

Move to a builder pattern to increase flexibility when generating the
test configuration. The testRust, testRustCov and testRustError are kept
as main entry points for standard tests. Add documentation.

Test: m nothing
Change-Id: I891bec982ff2d65413f150d2395edf0fb0d68a43
This commit is contained in:
Thiébaud Weksteen 2020-10-07 14:30:03 +02:00
parent 36e4ad1f4d
commit 0a75e52460
2 changed files with 92 additions and 69 deletions

View file

@ -22,22 +22,15 @@ import (
"testing"
"android/soong/android"
"android/soong/cc"
)
// testProjectJson run the generation of rust-project.json. It returns the raw
// content of the generated file.
func testProjectJson(t *testing.T, bp string, fs map[string][]byte) []byte {
cc.GatherRequiredFilesForTest(fs)
env := map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
config := android.TestArchConfig(buildDir, env, bp, fs)
ctx := CreateTestContext()
ctx.Register(config)
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
android.FailIfErrored(t, errs)
func testProjectJson(t *testing.T, bp string) []byte {
tctx := newTestRustCtx(t, bp)
tctx.env = map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
tctx.generateConfig()
tctx.parse(t)
// The JSON file is generated via WriteFileToOutputDir. Therefore, it
// won't appear in the Output of the TestingSingleton. Manually verify
@ -87,12 +80,8 @@ func TestProjectJsonDep(t *testing.T) {
crate_name: "b",
rlibs: ["liba"],
}
` + GatherRequiredDepsForTest()
fs := map[string][]byte{
"a/src/lib.rs": nil,
"b/src/lib.rs": nil,
}
jsonContent := testProjectJson(t, bp, fs)
`
jsonContent := testProjectJson(t, bp)
validateJsonCrates(t, jsonContent)
}
@ -123,11 +112,8 @@ func TestProjectJsonBindGen(t *testing.T) {
source_stem: "bindings2",
wrapper_src: "src/any.h",
}
` + GatherRequiredDepsForTest()
fs := map[string][]byte{
"src/lib.rs": nil,
}
jsonContent := testProjectJson(t, bp, fs)
`
jsonContent := testProjectJson(t, bp)
crates := validateJsonCrates(t, jsonContent)
for _, c := range crates {
crate, ok := c.(map[string]interface{})
@ -166,13 +152,8 @@ func TestProjectJsonMultiVersion(t *testing.T) {
crate_name: "b",
rustlibs: ["liba1", "liba2"],
}
` + GatherRequiredDepsForTest()
fs := map[string][]byte{
"a1/src/lib.rs": nil,
"a2/src/lib.rs": nil,
"b/src/lib.rs": nil,
}
jsonContent := testProjectJson(t, bp, fs)
`
jsonContent := testProjectJson(t, bp)
crates := validateJsonCrates(t, jsonContent)
for _, crate := range crates {
c := crate.(map[string]interface{})

View file

@ -54,10 +54,58 @@ func TestMain(m *testing.M) {
os.Exit(run())
}
func testConfig(bp string) android.Config {
bp = bp + GatherRequiredDepsForTest()
// testRust returns a TestContext in which a basic environment has been setup.
// This environment contains a few mocked files. See testRustCtx.useMockedFs
// for the list of these files.
func testRust(t *testing.T, bp string) *android.TestContext {
tctx := newTestRustCtx(t, bp)
tctx.useMockedFs()
tctx.generateConfig()
return tctx.parse(t)
}
fs := map[string][]byte{
// testRustCov returns a TestContext in which a basic environment has been
// setup. This environment explicitly enables coverage.
func testRustCov(t *testing.T, bp string) *android.TestContext {
tctx := newTestRustCtx(t, bp)
tctx.useMockedFs()
tctx.generateConfig()
tctx.enableCoverage(t)
return tctx.parse(t)
}
// testRustError ensures that at least one error was raised and its value
// matches the pattern provided. The error can be either in the parsing of the
// Blueprint or when generating the build actions.
func testRustError(t *testing.T, pattern string, bp string) {
tctx := newTestRustCtx(t, bp)
tctx.useMockedFs()
tctx.generateConfig()
tctx.parseError(t, pattern)
}
// testRustCtx is used to build a particular test environment. Unless your
// tests requires a specific setup, prefer the wrapping functions: testRust,
// testRustCov or testRustError.
type testRustCtx struct {
bp string
fs map[string][]byte
env map[string]string
config *android.Config
}
// newTestRustCtx returns a new testRustCtx for the Blueprint definition argument.
func newTestRustCtx(t *testing.T, bp string) *testRustCtx {
// TODO (b/140435149)
if runtime.GOOS != "linux" {
t.Skip("Rust Soong tests can only be run on Linux hosts currently")
}
return &testRustCtx{bp: bp}
}
// useMockedFs setup a default mocked filesystem for the test environment.
func (tctx *testRustCtx) useMockedFs() {
tctx.fs = map[string][]byte{
"foo.rs": nil,
"foo.c": nil,
"src/bar.rs": nil,
@ -66,57 +114,51 @@ func testConfig(bp string) android.Config {
"liby.so": nil,
"libz.so": nil,
}
cc.GatherRequiredFilesForTest(fs)
return android.TestArchConfig(buildDir, nil, bp, fs)
}
func testRust(t *testing.T, bp string) *android.TestContext {
return testRustContext(t, bp, false)
// generateConfig creates the android.Config based on the bp, fs and env
// attributes of the testRustCtx.
func (tctx *testRustCtx) generateConfig() {
tctx.bp = tctx.bp + GatherRequiredDepsForTest()
cc.GatherRequiredFilesForTest(tctx.fs)
config := android.TestArchConfig(buildDir, tctx.env, tctx.bp, tctx.fs)
tctx.config = &config
}
func testRustCov(t *testing.T, bp string) *android.TestContext {
return testRustContext(t, bp, true)
}
func testRustContext(t *testing.T, bp string, coverage bool) *android.TestContext {
// TODO (b/140435149)
if runtime.GOOS != "linux" {
t.Skip("Only the Linux toolchain is supported for Rust")
// enableCoverage configures the test to enable coverage.
func (tctx *testRustCtx) enableCoverage(t *testing.T) {
if tctx.config == nil {
t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
}
tctx.config.TestProductVariables.GcovCoverage = proptools.BoolPtr(true)
tctx.config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
tctx.config.TestProductVariables.NativeCoveragePaths = []string{"*"}
}
t.Helper()
config := testConfig(bp)
if coverage {
config.TestProductVariables.GcovCoverage = proptools.BoolPtr(true)
config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
config.TestProductVariables.NativeCoveragePaths = []string{"*"}
// parse validates the configuration and parses the Blueprint file. It returns
// a TestContext which can be used to retrieve the generated modules via
// ModuleForTests.
func (tctx testRustCtx) parse(t *testing.T) *android.TestContext {
if tctx.config == nil {
t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
}
ctx := CreateTestContext()
ctx.Register(config)
ctx.Register(*tctx.config)
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
_, errs = ctx.PrepareBuildActions(*tctx.config)
android.FailIfErrored(t, errs)
return ctx
}
func testRustError(t *testing.T, pattern string, bp string) {
// TODO (b/140435149)
if runtime.GOOS != "linux" {
t.Skip("Only the Linux toolchain is supported for Rust")
// parseError parses the Blueprint file and ensure that at least one error
// matching the provided pattern is observed.
func (tctx testRustCtx) parseError(t *testing.T, pattern string) {
if tctx.config == nil {
t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
}
t.Helper()
config := testConfig(bp)
ctx := CreateTestContext()
ctx.Register(config)
ctx.Register(*tctx.config)
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
if len(errs) > 0 {
@ -124,7 +166,7 @@ func testRustError(t *testing.T, pattern string, bp string) {
return
}
_, errs = ctx.PrepareBuildActions(config)
_, errs = ctx.PrepareBuildActions(*tctx.config)
if len(errs) > 0 {
android.FailIfNoMatchingErrors(t, pattern, errs)
return