blob: 00585f5f6c3bed9d3efcb3b68a0a4f07e8b18f70 [file] [log] [blame]
Weijia He32282f62024-11-05 22:21:04 +00001// Copyright 2024 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package tradefed_modules
16
17import (
Weijia He0c79e262024-11-06 19:57:25 +000018 "encoding/json"
19 "path"
20 "path/filepath"
Weijia Heaa37c162024-11-06 19:46:03 +000021
Weijia He32282f62024-11-05 22:21:04 +000022 "android/soong/android"
Weijia He0c79e262024-11-06 19:57:25 +000023 "android/soong/tradefed"
24 "github.com/google/blueprint"
Weijia He32282f62024-11-05 22:21:04 +000025)
26
Weijia He32a95572024-11-06 23:56:33 +000027const testSuiteModuleType = "test_suite"
28
Weijia He0c79e262024-11-06 19:57:25 +000029type testSuiteTag struct{
30 blueprint.BaseDependencyTag
31}
32
33type testSuiteManifest struct {
34 Name string `json:"name"`
35 Files []string `json:"files"`
36}
37
Weijia He32282f62024-11-05 22:21:04 +000038func init() {
39 RegisterTestSuiteBuildComponents(android.InitRegistrationContext)
40}
41
42func RegisterTestSuiteBuildComponents(ctx android.RegistrationContext) {
Weijia He32a95572024-11-06 23:56:33 +000043 ctx.RegisterModuleType(testSuiteModuleType, TestSuiteFactory)
Weijia He32282f62024-11-05 22:21:04 +000044}
45
46var PrepareForTestWithTestSuiteBuildComponents = android.GroupFixturePreparers(
47 android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
48)
49
50type testSuiteProperties struct {
51 Description string
52 Tests []string `android:"path,arch_variant"`
53}
54
55type testSuiteModule struct {
56 android.ModuleBase
57 android.DefaultableModuleBase
58 testSuiteProperties
59}
60
Weijia He0c79e262024-11-06 19:57:25 +000061func (t *testSuiteModule) DepsMutator(ctx android.BottomUpMutatorContext) {
62 for _, test := range t.Tests {
63 if ctx.OtherModuleDependencyVariantExists(ctx.Config().BuildOSCommonTarget.Variations(), test) {
64 // Host tests.
65 ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), testSuiteTag{}, test)
66 } else {
67 // Target tests.
68 ctx.AddDependency(ctx.Module(), testSuiteTag{}, test)
69 }
70 }
71}
72
Weijia He32282f62024-11-05 22:21:04 +000073func (t *testSuiteModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
Weijia He32a95572024-11-06 23:56:33 +000074 suiteName := ctx.ModuleName()
Weijia He0c79e262024-11-06 19:57:25 +000075 modulesByName := make(map[string]android.Module)
76 ctx.WalkDeps(func(child, parent android.Module) bool {
Weijia He32a95572024-11-06 23:56:33 +000077 // Recurse into test_suite dependencies.
78 if ctx.OtherModuleType(child) == testSuiteModuleType {
79 ctx.Phony(suiteName, android.PathForPhony(ctx, child.Name()))
80 return true
81 }
82
Weijia He0c79e262024-11-06 19:57:25 +000083 // Only write out top level test suite dependencies here.
84 if _, ok := ctx.OtherModuleDependencyTag(child).(testSuiteTag); !ok {
85 return false
86 }
87
88 if !child.InstallInTestcases() {
89 ctx.ModuleErrorf("test_suite only supports modules installed in testcases. %q is not installed in testcases.", child.Name())
90 return false
91 }
92
93 modulesByName[child.Name()] = child
94 return false
95 })
96
Weijia He0c79e262024-11-06 19:57:25 +000097 var files []string
98 for name, module := range modulesByName {
99 // Get the test provider data from the child.
100 tp, ok := android.OtherModuleProvider(ctx, module, tradefed.BaseTestProviderKey)
101 if !ok {
102 // TODO: Consider printing out a list of all module types.
103 ctx.ModuleErrorf("%q is not a test module.", name)
104 continue
105 }
106
107 files = append(files, packageModuleFiles(ctx, suiteName, module, tp)...)
108 ctx.Phony(suiteName, android.PathForPhony(ctx, name))
109 }
110
Weijia Heaa37c162024-11-06 19:46:03 +0000111 manifestPath := android.PathForSuiteInstall(ctx, suiteName, suiteName+".json")
Weijia He0c79e262024-11-06 19:57:25 +0000112 b, err := json.Marshal(testSuiteManifest{Name: suiteName, Files: files})
113 if err != nil {
114 ctx.ModuleErrorf("Failed to marshal manifest: %v", err)
115 return
116 }
117 android.WriteFileRule(ctx, manifestPath, string(b))
118
Weijia Heaa37c162024-11-06 19:46:03 +0000119 ctx.Phony(suiteName, manifestPath)
Weijia He32282f62024-11-05 22:21:04 +0000120}
121
122func TestSuiteFactory() android.Module {
123 module := &testSuiteModule{}
124 module.AddProperties(&module.testSuiteProperties)
125
Weijia He32a95572024-11-06 23:56:33 +0000126 android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
Weijia He32282f62024-11-05 22:21:04 +0000127 android.InitDefaultableModule(module)
128
129 return module
130}
Weijia He0c79e262024-11-06 19:57:25 +0000131
132func packageModuleFiles(ctx android.ModuleContext, suiteName string, module android.Module, tp tradefed.BaseTestProviderData) []string {
133
134 hostOrTarget := "target"
135 if tp.IsHost {
136 hostOrTarget = "host"
137 }
138
139 // suiteRoot at out/soong/packaging/<suiteName>.
140 suiteRoot := android.PathForSuiteInstall(ctx, suiteName)
141
142 var installed android.InstallPaths
143 // Install links to installed files from the module.
144 if installFilesInfo, ok := android.OtherModuleProvider(ctx, module, android.InstallFilesProvider); ok {
145 for _, f := range installFilesInfo.InstallFiles {
146 // rel is anything under .../<partition>, normally under .../testcases.
147 rel := android.Rel(ctx, f.PartitionDir(), f.String())
148
149 // Install the file under <suiteRoot>/<host|target>/<partition>.
150 installDir := suiteRoot.Join(ctx, hostOrTarget, f.Partition(), path.Dir(rel))
151 linkTo, err := filepath.Rel(installDir.String(), f.String())
152 if err != nil {
153 ctx.ModuleErrorf("Failed to get relative path from %s to %s: %v", installDir.String(), f.String(), err)
154 continue
155 }
156 installed = append(installed, ctx.InstallAbsoluteSymlink(installDir, path.Base(rel), linkTo))
157 }
158 }
159
160 // Install config file.
161 if tp.TestConfig != nil {
162 moduleRoot := suiteRoot.Join(ctx, hostOrTarget, "testcases", module.Name())
163 installed = append(installed, ctx.InstallFile(moduleRoot, module.Name() + ".config", tp.TestConfig))
164 }
165
166 // Add to phony and manifest, manifestpaths are relative to suiteRoot.
167 var manifestEntries []string
168 for _, f := range installed {
169 manifestEntries = append(manifestEntries, android.Rel(ctx, suiteRoot.String(), f.String()))
170 ctx.Phony(suiteName, f)
171 }
172 return manifestEntries
173}