blob: 5f971cdd11b0d46fa57a8c88ca07bfeddc13dd90 [file] [log] [blame]
Nan Zhangdb0b9a32017-02-27 10:12:13 -08001// Copyright 2017 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 python
16
17import (
Nan Zhangdb0b9a32017-02-27 10:12:13 -080018 "fmt"
Nan Zhangdb0b9a32017-02-27 10:12:13 -080019 "os"
20 "path/filepath"
Ronald Braunsteinc4cd7a12024-04-16 16:39:48 -070021 "strings"
Nan Zhangdb0b9a32017-02-27 10:12:13 -080022 "testing"
23
24 "android/soong/android"
Cole Faust5c503d12023-01-24 11:48:08 -080025 "android/soong/cc"
Ronald Braunsteinc4cd7a12024-04-16 16:39:48 -070026
27 "github.com/google/blueprint"
Nan Zhangdb0b9a32017-02-27 10:12:13 -080028)
29
Nan Zhangd4e641b2017-07-12 12:55:28 -070030type pyModule struct {
Nan Zhang1db85402017-12-18 13:20:23 -080031 name string
32 actualVersion string
33 pyRunfiles []string
34 srcsZip string
35 depsSrcsZips []string
Nan Zhangdb0b9a32017-02-27 10:12:13 -080036}
37
38var (
Cole Faustd85c6772025-01-31 10:54:46 -080039 buildNamePrefix = "soong_python_test"
40 moduleVariantErrTemplate = `%s: module %q variant "[a-zA-Z0-9_]*": `
Nan Zhangdb0b9a32017-02-27 10:12:13 -080041 pkgPathErrTemplate = moduleVariantErrTemplate +
Nan Zhangd4e641b2017-07-12 12:55:28 -070042 "pkg_path: %q must be a relative path contained in par file."
Nan Zhangdb0b9a32017-02-27 10:12:13 -080043 badIdentifierErrTemplate = moduleVariantErrTemplate +
Liz Kammerd737d022020-11-16 15:42:51 -080044 "srcs: the path %q contains invalid subpath %q."
Nan Zhangdb0b9a32017-02-27 10:12:13 -080045 dupRunfileErrTemplate = moduleVariantErrTemplate +
Nan Zhangbea09752018-05-31 12:49:33 -070046 "found two files to be placed at the same location within zip %q." +
Nan Zhangdb0b9a32017-02-27 10:12:13 -080047 " First file: in module %s at path %q." +
48 " Second file: in module %s at path %q."
Cole Faustd85c6772025-01-31 10:54:46 -080049 badSrcFileExtErr = moduleVariantErrTemplate + `srcs: found non \(.py\|.proto\) file: %q!`
50 badDataFileExtErr = moduleVariantErrTemplate + `data: found \(.py\) file: %q!`
Colin Cross98be1bb2019-12-13 20:41:13 -080051 bpFile = "Android.bp"
Nan Zhangdb0b9a32017-02-27 10:12:13 -080052
53 data = []struct {
54 desc string
Paul Duffin803876aa2021-03-17 22:38:23 +000055 mockFiles android.MockFS
Nan Zhangdb0b9a32017-02-27 10:12:13 -080056
57 errors []string
Nan Zhangd4e641b2017-07-12 12:55:28 -070058 expectedBinaries []pyModule
Nan Zhangdb0b9a32017-02-27 10:12:13 -080059 }{
60 {
Nan Zhangdb0b9a32017-02-27 10:12:13 -080061 desc: "module with bad src file ext",
62 mockFiles: map[string][]byte{
Nan Zhangdb0b9a32017-02-27 10:12:13 -080063 filepath.Join("dir", bpFile): []byte(
64 `python_library_host {
65 name: "lib1",
66 srcs: [
67 "file1.exe",
68 ],
69 }`,
70 ),
71 "dir/file1.exe": nil,
72 },
73 errors: []string{
74 fmt.Sprintf(badSrcFileExtErr,
Cole Faustd85c6772025-01-31 10:54:46 -080075 "dir/Android.bp:3:11", "lib1", "dir/file1.exe"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -080076 },
77 },
78 {
79 desc: "module with bad data file ext",
80 mockFiles: map[string][]byte{
Nan Zhangdb0b9a32017-02-27 10:12:13 -080081 filepath.Join("dir", bpFile): []byte(
82 `python_library_host {
83 name: "lib1",
84 srcs: [
85 "file1.py",
86 ],
87 data: [
88 "file2.py",
89 ],
90 }`,
91 ),
92 "dir/file1.py": nil,
93 "dir/file2.py": nil,
94 },
95 errors: []string{
96 fmt.Sprintf(badDataFileExtErr,
Cole Faustd85c6772025-01-31 10:54:46 -080097 "dir/Android.bp:6:11", "lib1", "dir/file2.py"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -080098 },
99 },
100 {
101 desc: "module with bad pkg_path format",
102 mockFiles: map[string][]byte{
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800103 filepath.Join("dir", bpFile): []byte(
104 `python_library_host {
105 name: "lib1",
106 pkg_path: "a/c/../../",
107 srcs: [
108 "file1.py",
109 ],
110 }
111
112 python_library_host {
113 name: "lib2",
114 pkg_path: "a/c/../../../",
115 srcs: [
116 "file1.py",
117 ],
118 }
119
120 python_library_host {
121 name: "lib3",
122 pkg_path: "/a/c/../../",
123 srcs: [
124 "file1.py",
125 ],
126 }`,
127 ),
128 "dir/file1.py": nil,
129 },
130 errors: []string{
131 fmt.Sprintf(pkgPathErrTemplate,
Cole Faustd85c6772025-01-31 10:54:46 -0800132 "dir/Android.bp:11:15", "lib2", "a/c/../../../"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800133 fmt.Sprintf(pkgPathErrTemplate,
Cole Faustd85c6772025-01-31 10:54:46 -0800134 "dir/Android.bp:19:15", "lib3", "/a/c/../../"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800135 },
136 },
137 {
138 desc: "module with bad runfile src path format",
139 mockFiles: map[string][]byte{
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800140 filepath.Join("dir", bpFile): []byte(
141 `python_library_host {
142 name: "lib1",
143 pkg_path: "a/b/c/",
144 srcs: [
145 ".file1.py",
146 "123/file1.py",
147 "-e/f/file1.py",
148 ],
149 }`,
150 ),
151 "dir/.file1.py": nil,
152 "dir/123/file1.py": nil,
153 "dir/-e/f/file1.py": nil,
154 },
155 errors: []string{
Colin Cross98be1bb2019-12-13 20:41:13 -0800156 fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
Cole Faustd85c6772025-01-31 10:54:46 -0800157 "lib1", "a/b/c/-e/f/file1.py", "-e"),
Colin Cross98be1bb2019-12-13 20:41:13 -0800158 fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
Cole Faustd85c6772025-01-31 10:54:46 -0800159 "lib1", "a/b/c/.file1.py", ".file1"),
Colin Cross98be1bb2019-12-13 20:41:13 -0800160 fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
Cole Faustd85c6772025-01-31 10:54:46 -0800161 "lib1", "a/b/c/123/file1.py", "123"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800162 },
163 },
164 {
165 desc: "module with duplicate runfile path",
166 mockFiles: map[string][]byte{
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800167 filepath.Join("dir", bpFile): []byte(
168 `python_library_host {
169 name: "lib1",
170 pkg_path: "a/b/",
171 srcs: [
172 "c/file1.py",
173 ],
174 }
175
176 python_library_host {
177 name: "lib2",
178 pkg_path: "a/b/c/",
179 srcs: [
180 "file1.py",
181 ],
182 libs: [
183 "lib1",
184 ],
185 }
Paul Duffinc60dd802021-03-17 23:01:06 +0000186
187 python_binary_host {
188 name: "bin",
189 pkg_path: "e/",
190 srcs: [
191 "bin.py",
192 ],
193 libs: [
194 "lib2",
195 ],
196 }
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800197 `,
198 ),
199 "dir/c/file1.py": nil,
200 "dir/file1.py": nil,
Paul Duffinc60dd802021-03-17 23:01:06 +0000201 "dir/bin.py": nil,
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800202 },
203 errors: []string{
Paul Duffinc60dd802021-03-17 23:01:06 +0000204 fmt.Sprintf(dupRunfileErrTemplate, "dir/Android.bp:20:6",
Cole Faustd85c6772025-01-31 10:54:46 -0800205 "bin", "a/b/c/file1.py", "bin", "dir/file1.py",
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800206 "lib1", "dir/c/file1.py"),
207 },
208 },
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800209 }
210)
211
212func TestPythonModule(t *testing.T) {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800213 for _, d := range data {
Cole Faust5c503d12023-01-24 11:48:08 -0800214 d.mockFiles[filepath.Join("common", bpFile)] = []byte(`
215python_library {
216 name: "py3-stdlib",
217 host_supported: true,
218}
219cc_binary {
220 name: "py3-launcher",
221 host_supported: true,
222}
223`)
Paul Duffin803876aa2021-03-17 22:38:23 +0000224
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800225 t.Run(d.desc, func(t *testing.T) {
Paul Duffin89648f92021-03-20 00:36:55 +0000226 result := android.GroupFixturePreparers(
227 android.PrepareForTestWithDefaults,
Cole Faust5c503d12023-01-24 11:48:08 -0800228 android.PrepareForTestWithArchMutator,
229 android.PrepareForTestWithAllowMissingDependencies,
230 cc.PrepareForTestWithCcDefaultModules,
Paul Duffin89648f92021-03-20 00:36:55 +0000231 PrepareForTestWithPythonBuildComponents,
232 d.mockFiles.AddToFixture(),
Cole Faust5c503d12023-01-24 11:48:08 -0800233 ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(d.errors)).
Paul Duffin89648f92021-03-20 00:36:55 +0000234 RunTest(t)
Paul Duffin803876aa2021-03-17 22:38:23 +0000235
236 if len(result.Errs) > 0 {
237 return
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800238 }
Paul Duffin803876aa2021-03-17 22:38:23 +0000239
240 for _, e := range d.expectedBinaries {
241 t.Run(e.name, func(t *testing.T) {
Cole Faust4d247e62023-01-23 10:14:58 -0800242 expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles)
Paul Duffin803876aa2021-03-17 22:38:23 +0000243 })
244 }
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800245 })
246 }
247}
248
Ronald Braunsteinc4cd7a12024-04-16 16:39:48 -0700249func TestTestOnlyProvider(t *testing.T) {
250 t.Parallel()
251 ctx := android.GroupFixturePreparers(
252 PrepareForTestWithPythonBuildComponents,
253 android.PrepareForTestWithAllowMissingDependencies,
254 ).RunTestWithBp(t, `
255 // These should be test-only
256 python_library { name: "py-lib-test", test_only: true }
257 python_library { name: "py-lib-test-host", test_only: true, host_supported: true }
258 python_test { name: "py-test", srcs: ["py-test.py"] }
259 python_test_host { name: "py-test-host", srcs: ["py-test-host.py"] }
260 python_binary_host { name: "py-bin-test", srcs: ["py-bin-test.py"] }
261
262 // These should not be.
263 python_library { name: "py-lib" }
264 python_binary_host { name: "py-bin", srcs: ["py-bin.py"] }
265 `)
266
267 // Visit all modules and ensure only the ones that should
268 // marked as test-only are marked as test-only.
269
270 actualTestOnly := []string{}
271 ctx.VisitAllModules(func(m blueprint.Module) {
272 if provider, ok := android.OtherModuleProvider(ctx.TestContext.OtherModuleProviderAdaptor(), m, android.TestOnlyProviderKey); ok {
273 if provider.TestOnly {
274 actualTestOnly = append(actualTestOnly, m.Name())
275 }
276 }
277 })
278 expectedTestOnlyModules := []string{
279 "py-lib-test",
280 "py-lib-test-host",
281 "py-test",
282 "py-test-host",
283 }
284
285 notEqual, left, right := android.ListSetDifference(expectedTestOnlyModules, actualTestOnly)
286 if notEqual {
287 t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right)
288 }
289}
290
291// Don't allow setting test-only on things that are always tests or never tests.
292func TestInvalidTestOnlyTargets(t *testing.T) {
293 testCases := []string{
294 ` python_test { name: "py-test", test_only: true, srcs: ["py-test.py"] } `,
295 ` python_test_host { name: "py-test-host", test_only: true, srcs: ["py-test-host.py"] } `,
296 ` python_defaults { name: "py-defaults", test_only: true, srcs: ["foo.py"] } `,
297 }
298
299 for i, bp := range testCases {
300 ctx := android.GroupFixturePreparers(
301 PrepareForTestWithPythonBuildComponents,
Ronald Braunsteinc4cd7a12024-04-16 16:39:48 -0700302 android.PrepareForTestWithAllowMissingDependencies).
303 ExtendWithErrorHandler(android.FixtureIgnoreErrors).
304 RunTestWithBp(t, bp)
305 if len(ctx.Errs) != 1 {
306 t.Errorf("Expected err setting test_only in testcase #%d: %d errs", i, len(ctx.Errs))
307 continue
308 }
309 if !strings.Contains(ctx.Errs[0].Error(), "unrecognized property \"test_only\"") {
310 t.Errorf("ERR: %s bad bp: %s", ctx.Errs[0], bp)
311 }
312 }
313}
314
Cole Faust4d247e62023-01-23 10:14:58 -0800315func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles []string) {
Colin Cross90607e92025-02-11 14:58:07 -0800316 module := ctx.ModuleForTests(t, name, variant)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800317
Cole Faust4d247e62023-01-23 10:14:58 -0800318 base, baseOk := module.Module().(*PythonLibraryModule)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800319 if !baseOk {
320 t.Fatalf("%s is not Python module!", name)
321 }
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800322
Nan Zhang1db85402017-12-18 13:20:23 -0800323 actualPyRunfiles := []string{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800324 for _, path := range base.srcsPathMappings {
Nan Zhang1db85402017-12-18 13:20:23 -0800325 actualPyRunfiles = append(actualPyRunfiles, path.dest)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800326 }
327
Paul Duffin803876aa2021-03-17 22:38:23 +0000328 android.AssertDeepEquals(t, "pyRunfiles", expectedPyRunfiles, actualPyRunfiles)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800329
Paul Duffin803876aa2021-03-17 22:38:23 +0000330 android.AssertPathRelativeToTopEquals(t, "srcsZip", expectedSrcsZip, base.srcsZip)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800331}
332
Colin Cross98be1bb2019-12-13 20:41:13 -0800333func TestMain(m *testing.M) {
Paul Duffin803876aa2021-03-17 22:38:23 +0000334 os.Exit(m.Run())
Colin Cross98be1bb2019-12-13 20:41:13 -0800335}