blob: 6a6bd1d915fd5e5986047c442120bc7a219ee173 [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 Faust5c503d12023-01-24 11:48:08 -080039 buildNamePrefix = "soong_python_test"
40 // We allow maching almost anything before the actual variant so that the os/arch variant
41 // is matched.
42 moduleVariantErrTemplate = `%s: module %q variant "[a-zA-Z0-9_]*%s": `
Nan Zhangdb0b9a32017-02-27 10:12:13 -080043 pkgPathErrTemplate = moduleVariantErrTemplate +
Nan Zhangd4e641b2017-07-12 12:55:28 -070044 "pkg_path: %q must be a relative path contained in par file."
Nan Zhangdb0b9a32017-02-27 10:12:13 -080045 badIdentifierErrTemplate = moduleVariantErrTemplate +
Liz Kammerd737d022020-11-16 15:42:51 -080046 "srcs: the path %q contains invalid subpath %q."
Nan Zhangdb0b9a32017-02-27 10:12:13 -080047 dupRunfileErrTemplate = moduleVariantErrTemplate +
Nan Zhangbea09752018-05-31 12:49:33 -070048 "found two files to be placed at the same location within zip %q." +
Nan Zhangdb0b9a32017-02-27 10:12:13 -080049 " First file: in module %s at path %q." +
50 " Second file: in module %s at path %q."
51 noSrcFileErr = moduleVariantErrTemplate + "doesn't have any source files!"
Nan Zhangb8fa1972017-12-22 16:12:00 -080052 badSrcFileExtErr = moduleVariantErrTemplate + "srcs: found non (.py|.proto) file: %q!"
Raphael Blistein59858462024-05-08 16:15:53 +000053 badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py) file: %q!"
Colin Cross98be1bb2019-12-13 20:41:13 -080054 bpFile = "Android.bp"
Nan Zhangdb0b9a32017-02-27 10:12:13 -080055
56 data = []struct {
57 desc string
Paul Duffin803876aa2021-03-17 22:38:23 +000058 mockFiles android.MockFS
Nan Zhangdb0b9a32017-02-27 10:12:13 -080059
60 errors []string
Nan Zhangd4e641b2017-07-12 12:55:28 -070061 expectedBinaries []pyModule
Nan Zhangdb0b9a32017-02-27 10:12:13 -080062 }{
63 {
64 desc: "module without any src files",
65 mockFiles: map[string][]byte{
Nan Zhangdb0b9a32017-02-27 10:12:13 -080066 filepath.Join("dir", bpFile): []byte(
67 `python_library_host {
68 name: "lib1",
69 }`,
70 ),
71 },
72 errors: []string{
73 fmt.Sprintf(noSrcFileErr,
Colin Cross98be1bb2019-12-13 20:41:13 -080074 "dir/Android.bp:1:1", "lib1", "PY3"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -080075 },
76 },
77 {
78 desc: "module with bad src file ext",
79 mockFiles: map[string][]byte{
Nan Zhangdb0b9a32017-02-27 10:12:13 -080080 filepath.Join("dir", bpFile): []byte(
81 `python_library_host {
82 name: "lib1",
83 srcs: [
84 "file1.exe",
85 ],
86 }`,
87 ),
88 "dir/file1.exe": nil,
89 },
90 errors: []string{
91 fmt.Sprintf(badSrcFileExtErr,
Colin Cross98be1bb2019-12-13 20:41:13 -080092 "dir/Android.bp:3:11", "lib1", "PY3", "dir/file1.exe"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -080093 },
94 },
95 {
96 desc: "module with bad data file ext",
97 mockFiles: map[string][]byte{
Nan Zhangdb0b9a32017-02-27 10:12:13 -080098 filepath.Join("dir", bpFile): []byte(
99 `python_library_host {
100 name: "lib1",
101 srcs: [
102 "file1.py",
103 ],
104 data: [
105 "file2.py",
106 ],
107 }`,
108 ),
109 "dir/file1.py": nil,
110 "dir/file2.py": nil,
111 },
112 errors: []string{
113 fmt.Sprintf(badDataFileExtErr,
Colin Cross98be1bb2019-12-13 20:41:13 -0800114 "dir/Android.bp:6:11", "lib1", "PY3", "dir/file2.py"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800115 },
116 },
117 {
118 desc: "module with bad pkg_path format",
119 mockFiles: map[string][]byte{
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800120 filepath.Join("dir", bpFile): []byte(
121 `python_library_host {
122 name: "lib1",
123 pkg_path: "a/c/../../",
124 srcs: [
125 "file1.py",
126 ],
127 }
128
129 python_library_host {
130 name: "lib2",
131 pkg_path: "a/c/../../../",
132 srcs: [
133 "file1.py",
134 ],
135 }
136
137 python_library_host {
138 name: "lib3",
139 pkg_path: "/a/c/../../",
140 srcs: [
141 "file1.py",
142 ],
143 }`,
144 ),
145 "dir/file1.py": nil,
146 },
147 errors: []string{
148 fmt.Sprintf(pkgPathErrTemplate,
Colin Cross98be1bb2019-12-13 20:41:13 -0800149 "dir/Android.bp:11:15", "lib2", "PY3", "a/c/../../../"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800150 fmt.Sprintf(pkgPathErrTemplate,
Colin Cross98be1bb2019-12-13 20:41:13 -0800151 "dir/Android.bp:19:15", "lib3", "PY3", "/a/c/../../"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800152 },
153 },
154 {
155 desc: "module with bad runfile src path format",
156 mockFiles: map[string][]byte{
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800157 filepath.Join("dir", bpFile): []byte(
158 `python_library_host {
159 name: "lib1",
160 pkg_path: "a/b/c/",
161 srcs: [
162 ".file1.py",
163 "123/file1.py",
164 "-e/f/file1.py",
165 ],
166 }`,
167 ),
168 "dir/.file1.py": nil,
169 "dir/123/file1.py": nil,
170 "dir/-e/f/file1.py": nil,
171 },
172 errors: []string{
Colin Cross98be1bb2019-12-13 20:41:13 -0800173 fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
Nan Zhangbea09752018-05-31 12:49:33 -0700174 "lib1", "PY3", "a/b/c/-e/f/file1.py", "-e"),
Colin Cross98be1bb2019-12-13 20:41:13 -0800175 fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
Nan Zhangbea09752018-05-31 12:49:33 -0700176 "lib1", "PY3", "a/b/c/.file1.py", ".file1"),
Colin Cross98be1bb2019-12-13 20:41:13 -0800177 fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
Nan Zhangbea09752018-05-31 12:49:33 -0700178 "lib1", "PY3", "a/b/c/123/file1.py", "123"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800179 },
180 },
181 {
182 desc: "module with duplicate runfile path",
183 mockFiles: map[string][]byte{
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800184 filepath.Join("dir", bpFile): []byte(
185 `python_library_host {
186 name: "lib1",
187 pkg_path: "a/b/",
188 srcs: [
189 "c/file1.py",
190 ],
191 }
192
193 python_library_host {
194 name: "lib2",
195 pkg_path: "a/b/c/",
196 srcs: [
197 "file1.py",
198 ],
199 libs: [
200 "lib1",
201 ],
202 }
Paul Duffinc60dd802021-03-17 23:01:06 +0000203
204 python_binary_host {
205 name: "bin",
206 pkg_path: "e/",
207 srcs: [
208 "bin.py",
209 ],
210 libs: [
211 "lib2",
212 ],
213 }
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800214 `,
215 ),
216 "dir/c/file1.py": nil,
217 "dir/file1.py": nil,
Paul Duffinc60dd802021-03-17 23:01:06 +0000218 "dir/bin.py": nil,
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800219 },
220 errors: []string{
Paul Duffinc60dd802021-03-17 23:01:06 +0000221 fmt.Sprintf(dupRunfileErrTemplate, "dir/Android.bp:20:6",
222 "bin", "PY3", "a/b/c/file1.py", "bin", "dir/file1.py",
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800223 "lib1", "dir/c/file1.py"),
224 },
225 },
226 {
227 desc: "module for testing dependencies",
228 mockFiles: map[string][]byte{
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800229 filepath.Join("dir", bpFile): []byte(
Nan Zhanga3fc4ba2017-07-20 17:43:37 -0700230 `python_defaults {
231 name: "default_lib",
232 srcs: [
233 "default.py",
234 ],
235 version: {
236 py2: {
237 enabled: true,
238 srcs: [
239 "default_py2.py",
240 ],
241 },
242 py3: {
243 enabled: false,
244 srcs: [
245 "default_py3.py",
246 ],
247 },
248 },
249 }
250
251 python_library_host {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800252 name: "lib5",
253 pkg_path: "a/b/",
254 srcs: [
255 "file1.py",
256 ],
257 version: {
258 py2: {
259 enabled: true,
260 },
261 py3: {
262 enabled: true,
263 },
264 },
265 }
266
267 python_library_host {
268 name: "lib6",
269 pkg_path: "c/d/",
270 srcs: [
271 "file2.py",
272 ],
273 libs: [
274 "lib5",
275 ],
276 }
277
278 python_binary_host {
279 name: "bin",
Nan Zhanga3fc4ba2017-07-20 17:43:37 -0700280 defaults: ["default_lib"],
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800281 pkg_path: "e/",
282 srcs: [
283 "bin.py",
284 ],
285 libs: [
286 "lib5",
287 ],
288 version: {
289 py3: {
290 enabled: true,
291 srcs: [
292 "file4.py",
293 ],
294 libs: [
295 "lib6",
296 ],
297 },
298 },
299 }`,
300 ),
Nan Zhanga3fc4ba2017-07-20 17:43:37 -0700301 filepath.Join("dir", "default.py"): nil,
302 filepath.Join("dir", "default_py2.py"): nil,
303 filepath.Join("dir", "default_py3.py"): nil,
304 filepath.Join("dir", "file1.py"): nil,
305 filepath.Join("dir", "file2.py"): nil,
306 filepath.Join("dir", "bin.py"): nil,
307 filepath.Join("dir", "file4.py"): nil,
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800308 },
Nan Zhangd4e641b2017-07-12 12:55:28 -0700309 expectedBinaries: []pyModule{
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800310 {
311 name: "bin",
312 actualVersion: "PY3",
313 pyRunfiles: []string{
Nan Zhangbea09752018-05-31 12:49:33 -0700314 "e/default.py",
315 "e/bin.py",
316 "e/default_py3.py",
317 "e/file4.py",
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800318 },
Paul Duffin803876aa2021-03-17 22:38:23 +0000319 srcsZip: "out/soong/.intermediates/dir/bin/PY3/bin.py.srcszip",
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800320 },
321 },
322 },
323 }
324)
325
326func TestPythonModule(t *testing.T) {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800327 for _, d := range data {
Paul Duffin803876aa2021-03-17 22:38:23 +0000328 if d.desc != "module with duplicate runfile path" {
329 continue
330 }
Cole Faust5c503d12023-01-24 11:48:08 -0800331 d.mockFiles[filepath.Join("common", bpFile)] = []byte(`
332python_library {
333 name: "py3-stdlib",
334 host_supported: true,
335}
336cc_binary {
337 name: "py3-launcher",
338 host_supported: true,
339}
340`)
Paul Duffin803876aa2021-03-17 22:38:23 +0000341
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800342 t.Run(d.desc, func(t *testing.T) {
Paul Duffin89648f92021-03-20 00:36:55 +0000343 result := android.GroupFixturePreparers(
344 android.PrepareForTestWithDefaults,
Cole Faust5c503d12023-01-24 11:48:08 -0800345 android.PrepareForTestWithArchMutator,
346 android.PrepareForTestWithAllowMissingDependencies,
347 cc.PrepareForTestWithCcDefaultModules,
Paul Duffin89648f92021-03-20 00:36:55 +0000348 PrepareForTestWithPythonBuildComponents,
349 d.mockFiles.AddToFixture(),
Cole Faust5c503d12023-01-24 11:48:08 -0800350 ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(d.errors)).
Paul Duffin89648f92021-03-20 00:36:55 +0000351 RunTest(t)
Paul Duffin803876aa2021-03-17 22:38:23 +0000352
353 if len(result.Errs) > 0 {
354 return
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800355 }
Paul Duffin803876aa2021-03-17 22:38:23 +0000356
357 for _, e := range d.expectedBinaries {
358 t.Run(e.name, func(t *testing.T) {
Cole Faust4d247e62023-01-23 10:14:58 -0800359 expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles)
Paul Duffin803876aa2021-03-17 22:38:23 +0000360 })
361 }
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800362 })
363 }
364}
365
Ronald Braunsteinc4cd7a12024-04-16 16:39:48 -0700366func TestTestOnlyProvider(t *testing.T) {
367 t.Parallel()
368 ctx := android.GroupFixturePreparers(
369 PrepareForTestWithPythonBuildComponents,
370 android.PrepareForTestWithAllowMissingDependencies,
371 ).RunTestWithBp(t, `
372 // These should be test-only
373 python_library { name: "py-lib-test", test_only: true }
374 python_library { name: "py-lib-test-host", test_only: true, host_supported: true }
375 python_test { name: "py-test", srcs: ["py-test.py"] }
376 python_test_host { name: "py-test-host", srcs: ["py-test-host.py"] }
377 python_binary_host { name: "py-bin-test", srcs: ["py-bin-test.py"] }
378
379 // These should not be.
380 python_library { name: "py-lib" }
381 python_binary_host { name: "py-bin", srcs: ["py-bin.py"] }
382 `)
383
384 // Visit all modules and ensure only the ones that should
385 // marked as test-only are marked as test-only.
386
387 actualTestOnly := []string{}
388 ctx.VisitAllModules(func(m blueprint.Module) {
389 if provider, ok := android.OtherModuleProvider(ctx.TestContext.OtherModuleProviderAdaptor(), m, android.TestOnlyProviderKey); ok {
390 if provider.TestOnly {
391 actualTestOnly = append(actualTestOnly, m.Name())
392 }
393 }
394 })
395 expectedTestOnlyModules := []string{
396 "py-lib-test",
397 "py-lib-test-host",
398 "py-test",
399 "py-test-host",
400 }
401
402 notEqual, left, right := android.ListSetDifference(expectedTestOnlyModules, actualTestOnly)
403 if notEqual {
404 t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right)
405 }
406}
407
408// Don't allow setting test-only on things that are always tests or never tests.
409func TestInvalidTestOnlyTargets(t *testing.T) {
410 testCases := []string{
411 ` python_test { name: "py-test", test_only: true, srcs: ["py-test.py"] } `,
412 ` python_test_host { name: "py-test-host", test_only: true, srcs: ["py-test-host.py"] } `,
413 ` python_defaults { name: "py-defaults", test_only: true, srcs: ["foo.py"] } `,
414 }
415
416 for i, bp := range testCases {
417 ctx := android.GroupFixturePreparers(
418 PrepareForTestWithPythonBuildComponents,
419 android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
420
421 ctx.RegisterModuleType("python_defaults", DefaultsFactory)
422 }),
423 android.PrepareForTestWithAllowMissingDependencies).
424 ExtendWithErrorHandler(android.FixtureIgnoreErrors).
425 RunTestWithBp(t, bp)
426 if len(ctx.Errs) != 1 {
427 t.Errorf("Expected err setting test_only in testcase #%d: %d errs", i, len(ctx.Errs))
428 continue
429 }
430 if !strings.Contains(ctx.Errs[0].Error(), "unrecognized property \"test_only\"") {
431 t.Errorf("ERR: %s bad bp: %s", ctx.Errs[0], bp)
432 }
433 }
434}
435
Cole Faust4d247e62023-01-23 10:14:58 -0800436func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles []string) {
Colin Crosscec81712017-07-13 14:43:27 -0700437 module := ctx.ModuleForTests(name, variant)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800438
Cole Faust4d247e62023-01-23 10:14:58 -0800439 base, baseOk := module.Module().(*PythonLibraryModule)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800440 if !baseOk {
441 t.Fatalf("%s is not Python module!", name)
442 }
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800443
Nan Zhang1db85402017-12-18 13:20:23 -0800444 actualPyRunfiles := []string{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800445 for _, path := range base.srcsPathMappings {
Nan Zhang1db85402017-12-18 13:20:23 -0800446 actualPyRunfiles = append(actualPyRunfiles, path.dest)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800447 }
448
Paul Duffin803876aa2021-03-17 22:38:23 +0000449 android.AssertDeepEquals(t, "pyRunfiles", expectedPyRunfiles, actualPyRunfiles)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800450
Paul Duffin803876aa2021-03-17 22:38:23 +0000451 android.AssertPathRelativeToTopEquals(t, "srcsZip", expectedSrcsZip, base.srcsZip)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800452}
453
Colin Cross98be1bb2019-12-13 20:41:13 -0800454func TestMain(m *testing.M) {
Paul Duffin803876aa2021-03-17 22:38:23 +0000455 os.Exit(m.Run())
Colin Cross98be1bb2019-12-13 20:41:13 -0800456}