1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
// Copyright 2024 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 tradefed_modules
import (
"encoding/json"
"path"
"path/filepath"
"android/soong/android"
"android/soong/tradefed"
"github.com/google/blueprint"
)
const testSuiteModuleType = "test_suite"
type testSuiteTag struct {
blueprint.BaseDependencyTag
}
type testSuiteManifest struct {
Name string `json:"name"`
Files []string `json:"files"`
}
func init() {
RegisterTestSuiteBuildComponents(android.InitRegistrationContext)
}
func RegisterTestSuiteBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType(testSuiteModuleType, TestSuiteFactory)
}
var PrepareForTestWithTestSuiteBuildComponents = android.GroupFixturePreparers(
android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
)
type testSuiteProperties struct {
Description string
Tests []string `android:"path,arch_variant"`
}
type testSuiteModule struct {
android.ModuleBase
android.DefaultableModuleBase
testSuiteProperties
}
func (t *testSuiteModule) DepsMutator(ctx android.BottomUpMutatorContext) {
for _, test := range t.Tests {
if ctx.OtherModuleDependencyVariantExists(ctx.Config().BuildOSCommonTarget.Variations(), test) {
// Host tests.
ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), testSuiteTag{}, test)
} else {
// Target tests.
ctx.AddDependency(ctx.Module(), testSuiteTag{}, test)
}
}
}
func (t *testSuiteModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
suiteName := ctx.ModuleName()
modulesByName := make(map[string]android.Module)
ctx.WalkDeps(func(child, parent android.Module) bool {
// Recurse into test_suite dependencies.
if ctx.OtherModuleType(child) == testSuiteModuleType {
ctx.Phony(suiteName, android.PathForPhony(ctx, child.Name()))
return true
}
// Only write out top level test suite dependencies here.
if _, ok := ctx.OtherModuleDependencyTag(child).(testSuiteTag); !ok {
return false
}
if !child.InstallInTestcases() {
ctx.ModuleErrorf("test_suite only supports modules installed in testcases. %q is not installed in testcases.", child.Name())
return false
}
modulesByName[child.Name()] = child
return false
})
var files []string
for name, module := range modulesByName {
// Get the test provider data from the child.
tp, ok := android.OtherModuleProvider(ctx, module, tradefed.BaseTestProviderKey)
if !ok {
// TODO: Consider printing out a list of all module types.
ctx.ModuleErrorf("%q is not a test module.", name)
continue
}
files = append(files, packageModuleFiles(ctx, suiteName, module, tp)...)
ctx.Phony(suiteName, android.PathForPhony(ctx, name))
}
manifestPath := android.PathForSuiteInstall(ctx, suiteName, suiteName+".json")
b, err := json.Marshal(testSuiteManifest{Name: suiteName, Files: android.SortedUniqueStrings(files)})
if err != nil {
ctx.ModuleErrorf("Failed to marshal manifest: %v", err)
return
}
android.WriteFileRule(ctx, manifestPath, string(b))
ctx.Phony(suiteName, manifestPath)
}
func TestSuiteFactory() android.Module {
module := &testSuiteModule{}
module.AddProperties(&module.testSuiteProperties)
android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
return module
}
func packageModuleFiles(ctx android.ModuleContext, suiteName string, module android.Module, tp tradefed.BaseTestProviderData) []string {
hostOrTarget := "target"
if tp.IsHost {
hostOrTarget = "host"
}
// suiteRoot at out/soong/packaging/<suiteName>.
suiteRoot := android.PathForSuiteInstall(ctx, suiteName)
var installed android.InstallPaths
// Install links to installed files from the module.
if installFilesInfo, ok := android.OtherModuleProvider(ctx, module, android.InstallFilesProvider); ok {
for _, f := range installFilesInfo.InstallFiles {
// rel is anything under .../<partition>, normally under .../testcases.
rel := android.Rel(ctx, f.PartitionDir(), f.String())
// Install the file under <suiteRoot>/<host|target>/<partition>.
installDir := suiteRoot.Join(ctx, hostOrTarget, f.Partition(), path.Dir(rel))
linkTo, err := filepath.Rel(installDir.String(), f.String())
if err != nil {
ctx.ModuleErrorf("Failed to get relative path from %s to %s: %v", installDir.String(), f.String(), err)
continue
}
installed = append(installed, ctx.InstallAbsoluteSymlink(installDir, path.Base(rel), linkTo))
}
}
// Install config file.
if tp.TestConfig != nil {
moduleRoot := suiteRoot.Join(ctx, hostOrTarget, "testcases", module.Name())
installed = append(installed, ctx.InstallFile(moduleRoot, module.Name()+".config", tp.TestConfig))
}
// Add to phony and manifest, manifestpaths are relative to suiteRoot.
var manifestEntries []string
for _, f := range installed {
manifestEntries = append(manifestEntries, android.Rel(ctx, suiteRoot.String(), f.String()))
ctx.Phony(suiteName, f)
}
return manifestEntries
}
|