Add tests for ctx.InstallFile

Add tests that cover Soong-only installation as well as installation with
InstallBypassMake.

Bug: 204136549
Test: TestInstall
Test: TestInstallBypassMake
Change-Id: Iac22c9fdf99994e06b419623ee5fa399ef6957fb
This commit is contained in:
Colin Cross
2021-11-09 13:17:44 -08:00
parent 7b6a55f5a2
commit 6ac9576eef
3 changed files with 313 additions and 7 deletions

View File

@@ -19,6 +19,9 @@ bootstrap_go_package {
"soong-ui-metrics_proto",
"golang-protobuf-proto",
"golang-protobuf-encoding-prototext",
// Only used for tests.
"androidmk-parser",
],
srcs: [
"androidmk.go",

View File

@@ -142,15 +142,19 @@ type SingletonMakeVarsProvider interface {
var singletonMakeVarsProvidersKey = NewOnceKey("singletonMakeVarsProvidersKey")
func getSingletonMakevarsProviders(config Config) *[]makeVarsProvider {
return config.Once(singletonMakeVarsProvidersKey, func() interface{} {
return &[]makeVarsProvider{}
}).(*[]makeVarsProvider)
}
// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to
// the list of MakeVarsProviders to run.
func registerSingletonMakeVarsProvider(config Config, singleton SingletonMakeVarsProvider) {
// Singletons are registered on the Context and may be different between different Contexts,
// for example when running multiple tests. Store the SingletonMakeVarsProviders in the
// Config so they are attached to the Context.
singletonMakeVarsProviders := config.Once(singletonMakeVarsProvidersKey, func() interface{} {
return &[]makeVarsProvider{}
}).(*[]makeVarsProvider)
singletonMakeVarsProviders := getSingletonMakevarsProviders(config)
*singletonMakeVarsProviders = append(*singletonMakeVarsProviders,
makeVarsProvider{pctx, singletonMakeVarsProviderAdapter(singleton)})
@@ -175,7 +179,9 @@ func makeVarsSingletonFunc() Singleton {
return &makeVarsSingleton{}
}
type makeVarsSingleton struct{}
type makeVarsSingleton struct {
installsForTesting []byte
}
type makeVarsProvider struct {
pctx PackageContext
@@ -238,7 +244,7 @@ func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
var katiSymlinks []katiInstall
providers := append([]makeVarsProvider(nil), makeVarsInitProviders...)
providers = append(providers, *ctx.Config().Get(singletonMakeVarsProvidersKey).(*[]makeVarsProvider)...)
providers = append(providers, *getSingletonMakevarsProviders(ctx.Config())...)
for _, provider := range providers {
mctx := &makeVarsContext{
@@ -313,6 +319,8 @@ func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
if err := pathtools.WriteFileIfChanged(installsFile, installsBytes, 0666); err != nil {
ctx.Errorf(err.Error())
}
s.installsForTesting = installsBytes
}
func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {

View File

@@ -15,7 +15,12 @@
package android
import (
"bytes"
"path/filepath"
"runtime"
"testing"
mkparser "android/soong/androidmk/parser"
)
func TestSrcIsModule(t *testing.T) {
@@ -199,17 +204,28 @@ type depsModule struct {
}
}
func (m *depsModule) InstallBypassMake() bool {
return true
}
func (m *depsModule) GenerateAndroidBuildActions(ctx ModuleContext) {
outputFile := PathForModuleOut(ctx, ctx.ModuleName())
ctx.Build(pctx, BuildParams{
Rule: Touch,
Output: outputFile,
})
installFile := ctx.InstallFile(PathForModuleInstall(ctx), ctx.ModuleName(), outputFile)
ctx.InstallSymlink(PathForModuleInstall(ctx, "symlinks"), ctx.ModuleName(), installFile)
}
func (m *depsModule) DepsMutator(ctx BottomUpMutatorContext) {
ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
}
func depsModuleFactory() Module {
m := &depsModule{}
m.AddProperties(&m.props)
InitAndroidModule(m)
InitAndroidArchModule(m, HostAndDeviceDefault, MultilibCommon)
return m
}
@@ -320,3 +336,282 @@ func TestDistErrorChecking(t *testing.T) {
ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(expectedErrs)).
RunTestWithBp(t, bp)
}
func TestInstall(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("requires linux")
}
bp := `
deps {
name: "foo",
deps: ["bar"],
}
deps {
name: "bar",
deps: ["baz", "qux"],
}
deps {
name: "baz",
deps: ["qux"],
}
deps {
name: "qux",
}
`
result := GroupFixturePreparers(
prepareForModuleTests,
PrepareForTestWithArchMutator,
).RunTestWithBp(t, bp)
module := func(name string, host bool) TestingModule {
variant := "android_common"
if host {
variant = result.Config.BuildOSCommonTarget.String()
}
return result.ModuleForTests(name, variant)
}
outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
installRule := func(name string) TestingBuildParams {
return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system", name))
}
symlinkRule := func(name string) TestingBuildParams {
return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system/symlinks", name))
}
hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
hostInstallRule := func(name string) TestingBuildParams {
return module(name, true).Output(filepath.Join("out/soong/host/linux-x86", name))
}
hostSymlinkRule := func(name string) TestingBuildParams {
return module(name, true).Output(filepath.Join("out/soong/host/linux-x86/symlinks", name))
}
assertInputs := func(params TestingBuildParams, inputs ...Path) {
t.Helper()
AssertArrayString(t, "expected inputs", Paths(inputs).Strings(),
append(PathsIfNonNil(params.Input), params.Inputs...).Strings())
}
assertImplicits := func(params TestingBuildParams, implicits ...Path) {
t.Helper()
AssertArrayString(t, "expected implicit dependencies", Paths(implicits).Strings(),
append(PathsIfNonNil(params.Implicit), params.Implicits...).Strings())
}
assertOrderOnlys := func(params TestingBuildParams, orderonlys ...Path) {
t.Helper()
AssertArrayString(t, "expected orderonly dependencies", Paths(orderonlys).Strings(),
params.OrderOnly.Strings())
}
// Check host install rule dependencies
assertInputs(hostInstallRule("foo"), hostOutputRule("foo").Output)
assertImplicits(hostInstallRule("foo"),
hostInstallRule("bar").Output,
hostSymlinkRule("bar").Output,
hostInstallRule("baz").Output,
hostSymlinkRule("baz").Output,
hostInstallRule("qux").Output,
hostSymlinkRule("qux").Output,
)
assertOrderOnlys(hostInstallRule("foo"))
// Check host symlink rule dependencies
assertInputs(hostSymlinkRule("foo"), hostInstallRule("foo").Output)
assertImplicits(hostSymlinkRule("foo"))
assertOrderOnlys(hostSymlinkRule("foo"))
// Check device install rule dependencies
assertInputs(installRule("foo"), outputRule("foo").Output)
assertImplicits(installRule("foo"))
assertOrderOnlys(installRule("foo"),
installRule("bar").Output,
symlinkRule("bar").Output,
installRule("baz").Output,
symlinkRule("baz").Output,
installRule("qux").Output,
symlinkRule("qux").Output,
)
// Check device symlink rule dependencies
assertInputs(symlinkRule("foo"), installRule("foo").Output)
assertImplicits(symlinkRule("foo"))
assertOrderOnlys(symlinkRule("foo"))
}
func TestInstallBypassMake(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("requires linux")
}
bp := `
deps {
name: "foo",
deps: ["bar"],
}
deps {
name: "bar",
deps: ["baz", "qux"],
}
deps {
name: "baz",
deps: ["qux"],
}
deps {
name: "qux",
}
`
result := GroupFixturePreparers(
prepareForModuleTests,
PrepareForTestWithArchMutator,
FixtureModifyConfig(SetKatiEnabledForTests),
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
}),
).RunTestWithBp(t, bp)
installs := result.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).installsForTesting
buf := bytes.NewBuffer(append([]byte(nil), installs...))
parser := mkparser.NewParser("makevars", buf)
nodes, errs := parser.Parse()
if len(errs) > 0 {
t.Fatalf("error parsing install rules: %s", errs[0])
}
rules := parseMkRules(t, result.Config, nodes)
module := func(name string, host bool) TestingModule {
variant := "android_common"
if host {
variant = result.Config.BuildOSCommonTarget.String()
}
return result.ModuleForTests(name, variant)
}
outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
ruleForOutput := func(output string) installMakeRule {
for _, rule := range rules {
if rule.target == output {
return rule
}
}
t.Fatalf("no make install rule for %s", output)
return installMakeRule{}
}
installRule := func(name string) installMakeRule {
return ruleForOutput(filepath.Join("out/target/product/test_device/system", name))
}
symlinkRule := func(name string) installMakeRule {
return ruleForOutput(filepath.Join("out/target/product/test_device/system/symlinks", name))
}
hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
hostInstallRule := func(name string) installMakeRule {
return ruleForOutput(filepath.Join("out/host/linux-x86", name))
}
hostSymlinkRule := func(name string) installMakeRule {
return ruleForOutput(filepath.Join("out/host/linux-x86/symlinks", name))
}
assertDeps := func(rule installMakeRule, deps ...string) {
t.Helper()
AssertArrayString(t, "expected inputs", deps, rule.deps)
}
assertOrderOnlys := func(rule installMakeRule, orderonlys ...string) {
t.Helper()
AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.orderOnlyDeps)
}
// Check host install rule dependencies
assertDeps(hostInstallRule("foo"),
hostOutputRule("foo").Output.String(),
hostInstallRule("bar").target,
hostSymlinkRule("bar").target,
hostInstallRule("baz").target,
hostSymlinkRule("baz").target,
hostInstallRule("qux").target,
hostSymlinkRule("qux").target,
)
assertOrderOnlys(hostInstallRule("foo"))
// Check host symlink rule dependencies
assertDeps(hostSymlinkRule("foo"))
assertOrderOnlys(hostSymlinkRule("foo"), hostInstallRule("foo").target)
// Check device install rule dependencies
assertDeps(installRule("foo"), outputRule("foo").Output.String())
assertOrderOnlys(installRule("foo"),
installRule("bar").target,
symlinkRule("bar").target,
installRule("baz").target,
symlinkRule("baz").target,
installRule("qux").target,
symlinkRule("qux").target,
)
// Check device symlink rule dependencies
assertDeps(symlinkRule("foo"))
assertOrderOnlys(symlinkRule("foo"), installRule("foo").target)
}
type installMakeRule struct {
target string
deps []string
orderOnlyDeps []string
}
func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []installMakeRule {
var rules []installMakeRule
for _, node := range nodes {
if mkParserRule, ok := node.(*mkparser.Rule); ok {
var rule installMakeRule
if targets := mkParserRule.Target.Words(); len(targets) == 0 {
t.Fatalf("no targets for rule %s", mkParserRule.Dump())
} else if len(targets) > 1 {
t.Fatalf("unsupported multiple targets for rule %s", mkParserRule.Dump())
} else if !targets[0].Const() {
t.Fatalf("unsupported non-const target for rule %s", mkParserRule.Dump())
} else {
rule.target = normalizeStringRelativeToTop(config, targets[0].Value(nil))
}
prereqList := &rule.deps
for _, prereq := range mkParserRule.Prerequisites.Words() {
if !prereq.Const() {
t.Fatalf("unsupported non-const prerequisite for rule %s", mkParserRule.Dump())
}
if prereq.Value(nil) == "|" {
prereqList = &rule.orderOnlyDeps
continue
}
*prereqList = append(*prereqList, normalizeStringRelativeToTop(config, prereq.Value(nil)))
}
rules = append(rules, rule)
}
}
return rules
}