platform_build_soong/android/namespace_test.go
Colin Cross ae8600b507 Pass Config to NewTestContext instead of ctx.Register
Prepare for using Config when adding singletons by passing
Config to NewTestContext and NewContext instead of to ctx.Register.
This will enable a followup change to store SingletonMakeVarsProviders
registered on the Context in the Config, which is necessary to run
multiple tests in parallel without data races.

Test: all soong tests
Change-Id: Id229629a4e42ff4487d317241673837726c075fc
2020-11-12 10:07:49 -08:00

762 lines
15 KiB
Go

// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package android
import (
"errors"
"path/filepath"
"reflect"
"testing"
"github.com/google/blueprint"
)
func TestDependingOnModuleInSameNamespace(t *testing.T) {
ctx := setupTest(t,
map[string]string{
"dir1": `
soong_namespace {
}
test_module {
name: "a",
}
test_module {
name: "b",
deps: ["a"],
}
`,
},
)
a := getModule(ctx, "a")
b := getModule(ctx, "b")
if !dependsOn(ctx, b, a) {
t.Errorf("module b does not depend on module a in the same namespace")
}
}
func TestDependingOnModuleInRootNamespace(t *testing.T) {
ctx := setupTest(t,
map[string]string{
".": `
test_module {
name: "b",
deps: ["a"],
}
test_module {
name: "a",
}
`,
},
)
a := getModule(ctx, "a")
b := getModule(ctx, "b")
if !dependsOn(ctx, b, a) {
t.Errorf("module b in root namespace does not depend on module a in the root namespace")
}
}
func TestImplicitlyImportRootNamespace(t *testing.T) {
_ = setupTest(t,
map[string]string{
".": `
test_module {
name: "a",
}
`,
"dir1": `
soong_namespace {
}
test_module {
name: "b",
deps: ["a"],
}
`,
},
)
// setupTest will report any errors
}
func TestDependingOnBlueprintModuleInRootNamespace(t *testing.T) {
_ = setupTest(t,
map[string]string{
".": `
blueprint_test_module {
name: "a",
}
`,
"dir1": `
soong_namespace {
}
blueprint_test_module {
name: "b",
deps: ["a"],
}
`,
},
)
// setupTest will report any errors
}
func TestDependingOnModuleInImportedNamespace(t *testing.T) {
ctx := setupTest(t,
map[string]string{
"dir1": `
soong_namespace {
}
test_module {
name: "a",
}
`,
"dir2": `
soong_namespace {
imports: ["dir1"],
}
test_module {
name: "b",
deps: ["a"],
}
`,
},
)
a := getModule(ctx, "a")
b := getModule(ctx, "b")
if !dependsOn(ctx, b, a) {
t.Errorf("module b does not depend on module a in the same namespace")
}
}
func TestDependingOnModuleInNonImportedNamespace(t *testing.T) {
_, errs := setupTestExpectErrs(
map[string]string{
"dir1": `
soong_namespace {
}
test_module {
name: "a",
}
`,
"dir2": `
soong_namespace {
}
test_module {
name: "a",
}
`,
"dir3": `
soong_namespace {
}
test_module {
name: "b",
deps: ["a"],
}
`,
},
)
expectedErrors := []error{
errors.New(
`dir3/Android.bp:4:4: "b" depends on undefined module "a"
Module "b" is defined in namespace "dir3" which can read these 2 namespaces: ["dir3" "."]
Module "a" can be found in these namespaces: ["dir1" "dir2"]`),
}
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
}
}
func TestDependingOnModuleByFullyQualifiedReference(t *testing.T) {
ctx := setupTest(t,
map[string]string{
"dir1": `
soong_namespace {
}
test_module {
name: "a",
}
`,
"dir2": `
soong_namespace {
}
test_module {
name: "b",
deps: ["//dir1:a"],
}
`,
},
)
a := getModule(ctx, "a")
b := getModule(ctx, "b")
if !dependsOn(ctx, b, a) {
t.Errorf("module b does not depend on module a")
}
}
func TestSameNameInTwoNamespaces(t *testing.T) {
ctx := setupTest(t,
map[string]string{
"dir1": `
soong_namespace {
}
test_module {
name: "a",
id: "1",
}
test_module {
name: "b",
deps: ["a"],
id: "2",
}
`,
"dir2": `
soong_namespace {
}
test_module {
name: "a",
id:"3",
}
test_module {
name: "b",
deps: ["a"],
id:"4",
}
`,
},
)
one := findModuleById(ctx, "1")
two := findModuleById(ctx, "2")
three := findModuleById(ctx, "3")
four := findModuleById(ctx, "4")
if !dependsOn(ctx, two, one) {
t.Fatalf("Module 2 does not depend on module 1 in its namespace")
}
if dependsOn(ctx, two, three) {
t.Fatalf("Module 2 depends on module 3 in another namespace")
}
if !dependsOn(ctx, four, three) {
t.Fatalf("Module 4 does not depend on module 3 in its namespace")
}
if dependsOn(ctx, four, one) {
t.Fatalf("Module 4 depends on module 1 in another namespace")
}
}
func TestSearchOrder(t *testing.T) {
ctx := setupTest(t,
map[string]string{
"dir1": `
soong_namespace {
}
test_module {
name: "a",
id: "1",
}
`,
"dir2": `
soong_namespace {
}
test_module {
name: "a",
id:"2",
}
test_module {
name: "b",
id:"3",
}
`,
"dir3": `
soong_namespace {
}
test_module {
name: "a",
id:"4",
}
test_module {
name: "b",
id:"5",
}
test_module {
name: "c",
id:"6",
}
`,
".": `
test_module {
name: "a",
id: "7",
}
test_module {
name: "b",
id: "8",
}
test_module {
name: "c",
id: "9",
}
test_module {
name: "d",
id: "10",
}
`,
"dir4": `
soong_namespace {
imports: ["dir1", "dir2", "dir3"]
}
test_module {
name: "test_me",
id:"0",
deps: ["a", "b", "c", "d"],
}
`,
},
)
testMe := findModuleById(ctx, "0")
if !dependsOn(ctx, testMe, findModuleById(ctx, "1")) {
t.Errorf("test_me doesn't depend on id 1")
}
if !dependsOn(ctx, testMe, findModuleById(ctx, "3")) {
t.Errorf("test_me doesn't depend on id 3")
}
if !dependsOn(ctx, testMe, findModuleById(ctx, "6")) {
t.Errorf("test_me doesn't depend on id 6")
}
if !dependsOn(ctx, testMe, findModuleById(ctx, "10")) {
t.Errorf("test_me doesn't depend on id 10")
}
if numDeps(ctx, testMe) != 4 {
t.Errorf("num dependencies of test_me = %v, not 4\n", numDeps(ctx, testMe))
}
}
func TestTwoNamespacesCanImportEachOther(t *testing.T) {
_ = setupTest(t,
map[string]string{
"dir1": `
soong_namespace {
imports: ["dir2"]
}
test_module {
name: "a",
}
test_module {
name: "c",
deps: ["b"],
}
`,
"dir2": `
soong_namespace {
imports: ["dir1"],
}
test_module {
name: "b",
deps: ["a"],
}
`,
},
)
// setupTest will report any errors
}
func TestImportingNonexistentNamespace(t *testing.T) {
_, errs := setupTestExpectErrs(
map[string]string{
"dir1": `
soong_namespace {
imports: ["a_nonexistent_namespace"]
}
test_module {
name: "a",
deps: ["a_nonexistent_module"]
}
`,
},
)
// should complain about the missing namespace and not complain about the unresolvable dependency
expectedErrors := []error{
errors.New(`dir1/Android.bp:2:4: module "soong_namespace": namespace a_nonexistent_namespace does not exist`),
}
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
}
}
func TestNamespacesDontInheritParentNamespaces(t *testing.T) {
_, errs := setupTestExpectErrs(
map[string]string{
"dir1": `
soong_namespace {
}
test_module {
name: "a",
}
`,
"dir1/subdir1": `
soong_namespace {
}
test_module {
name: "b",
deps: ["a"],
}
`,
},
)
expectedErrors := []error{
errors.New(`dir1/subdir1/Android.bp:4:4: "b" depends on undefined module "a"
Module "b" is defined in namespace "dir1/subdir1" which can read these 2 namespaces: ["dir1/subdir1" "."]
Module "a" can be found in these namespaces: ["dir1"]`),
}
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
}
}
func TestModulesDoReceiveParentNamespace(t *testing.T) {
_ = setupTest(t,
map[string]string{
"dir1": `
soong_namespace {
}
test_module {
name: "a",
}
`,
"dir1/subdir": `
test_module {
name: "b",
deps: ["a"],
}
`,
},
)
// setupTest will report any errors
}
func TestNamespaceImportsNotTransitive(t *testing.T) {
_, errs := setupTestExpectErrs(
map[string]string{
"dir1": `
soong_namespace {
}
test_module {
name: "a",
}
`,
"dir2": `
soong_namespace {
imports: ["dir1"],
}
test_module {
name: "b",
deps: ["a"],
}
`,
"dir3": `
soong_namespace {
imports: ["dir2"],
}
test_module {
name: "c",
deps: ["a"],
}
`,
},
)
expectedErrors := []error{
errors.New(`dir3/Android.bp:5:4: "c" depends on undefined module "a"
Module "c" is defined in namespace "dir3" which can read these 3 namespaces: ["dir3" "dir2" "."]
Module "a" can be found in these namespaces: ["dir1"]`),
}
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
}
}
func TestTwoNamepacesInSameDir(t *testing.T) {
_, errs := setupTestExpectErrs(
map[string]string{
"dir1": `
soong_namespace {
}
soong_namespace {
}
`,
},
)
expectedErrors := []error{
errors.New(`dir1/Android.bp:4:4: namespace dir1 already exists`),
}
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
}
}
func TestNamespaceNotAtTopOfFile(t *testing.T) {
_, errs := setupTestExpectErrs(
map[string]string{
"dir1": `
test_module {
name: "a"
}
soong_namespace {
}
`,
},
)
expectedErrors := []error{
errors.New(`dir1/Android.bp:5:4: a namespace must be the first module in the file`),
}
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
}
}
func TestTwoModulesWithSameNameInSameNamespace(t *testing.T) {
_, errs := setupTestExpectErrs(
map[string]string{
"dir1": `
soong_namespace {
}
test_module {
name: "a"
}
test_module {
name: "a"
}
`,
},
)
expectedErrors := []error{
errors.New(`dir1/Android.bp:7:4: module "a" already defined
dir1/Android.bp:4:4 <-- previous definition here`),
}
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
}
}
func TestDeclaringNamespaceInNonAndroidBpFile(t *testing.T) {
_, errs := setupTestFromFiles(
map[string][]byte{
"Android.bp": []byte(`
build = ["include.bp"]
`),
"include.bp": []byte(`
soong_namespace {
}
`),
},
)
expectedErrors := []error{
errors.New(`include.bp:2:5: A namespace may only be declared in a file named Android.bp`),
}
if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
}
}
// so that the generated .ninja file will have consistent names
func TestConsistentNamespaceNames(t *testing.T) {
ctx := setupTest(t,
map[string]string{
"dir1": "soong_namespace{}",
"dir2": "soong_namespace{}",
"dir3": "soong_namespace{}",
})
ns1, _ := ctx.NameResolver.namespaceAt("dir1")
ns2, _ := ctx.NameResolver.namespaceAt("dir2")
ns3, _ := ctx.NameResolver.namespaceAt("dir3")
actualIds := []string{ns1.id, ns2.id, ns3.id}
expectedIds := []string{"1", "2", "3"}
if !reflect.DeepEqual(actualIds, expectedIds) {
t.Errorf("Incorrect namespace ids.\nactual: %s\nexpected: %s\n", actualIds, expectedIds)
}
}
// so that the generated .ninja file will have consistent names
func TestRename(t *testing.T) {
_ = setupTest(t,
map[string]string{
"dir1": `
soong_namespace {
}
test_module {
name: "a",
deps: ["c"],
}
test_module {
name: "b",
rename: "c",
}
`})
// setupTest will report any errors
}
// some utils to support the tests
func mockFiles(bps map[string]string) (files map[string][]byte) {
files = make(map[string][]byte, len(bps))
files["Android.bp"] = []byte("")
for dir, text := range bps {
files[filepath.Join(dir, "Android.bp")] = []byte(text)
}
return files
}
func setupTestFromFiles(bps map[string][]byte) (ctx *TestContext, errs []error) {
config := TestConfig(buildDir, nil, "", bps)
ctx = NewTestContext(config)
ctx.RegisterModuleType("test_module", newTestModule)
ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
ctx.PreArchMutators(RegisterNamespaceMutator)
ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("rename", renameMutator)
})
ctx.Register()
_, errs = ctx.ParseBlueprintsFiles("Android.bp")
if len(errs) > 0 {
return ctx, errs
}
_, errs = ctx.PrepareBuildActions(config)
return ctx, errs
}
func setupTestExpectErrs(bps map[string]string) (ctx *TestContext, errs []error) {
files := make(map[string][]byte, len(bps))
files["Android.bp"] = []byte("")
for dir, text := range bps {
files[filepath.Join(dir, "Android.bp")] = []byte(text)
}
return setupTestFromFiles(files)
}
func setupTest(t *testing.T, bps map[string]string) (ctx *TestContext) {
t.Helper()
ctx, errs := setupTestExpectErrs(bps)
FailIfErrored(t, errs)
return ctx
}
func dependsOn(ctx *TestContext, module TestingModule, possibleDependency TestingModule) bool {
depends := false
visit := func(dependency blueprint.Module) {
if dependency == possibleDependency.module {
depends = true
}
}
ctx.VisitDirectDeps(module.module, visit)
return depends
}
func numDeps(ctx *TestContext, module TestingModule) int {
count := 0
visit := func(dependency blueprint.Module) {
count++
}
ctx.VisitDirectDeps(module.module, visit)
return count
}
func getModule(ctx *TestContext, moduleName string) TestingModule {
return ctx.ModuleForTests(moduleName, "")
}
func findModuleById(ctx *TestContext, id string) (module TestingModule) {
visit := func(candidate blueprint.Module) {
testModule, ok := candidate.(*testModule)
if ok {
if testModule.properties.Id == id {
module = TestingModule{testModule}
}
}
}
ctx.VisitAllModules(visit)
return module
}
type testModule struct {
ModuleBase
properties struct {
Rename string
Deps []string
Id string
}
}
func (m *testModule) DepsMutator(ctx BottomUpMutatorContext) {
if m.properties.Rename != "" {
ctx.Rename(m.properties.Rename)
}
for _, d := range m.properties.Deps {
ctx.AddDependency(ctx.Module(), nil, d)
}
}
func (m *testModule) GenerateAndroidBuildActions(ModuleContext) {
}
func renameMutator(ctx BottomUpMutatorContext) {
if m, ok := ctx.Module().(*testModule); ok {
if m.properties.Rename != "" {
ctx.Rename(m.properties.Rename)
}
}
}
func newTestModule() Module {
m := &testModule{}
m.AddProperties(&m.properties)
InitAndroidModule(m)
return m
}
type blueprintTestModule struct {
blueprint.SimpleName
properties struct {
Deps []string
}
}
func (b *blueprintTestModule) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
return b.properties.Deps
}
func (b *blueprintTestModule) GenerateBuildActions(blueprint.ModuleContext) {
}
func newBlueprintTestModule() (blueprint.Module, []interface{}) {
m := &blueprintTestModule{}
return m, []interface{}{&m.properties, &m.SimpleName.Properties}
}