blob: ba5e7fadc47d47bf696fb05f0580ac98ddea39d6 [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 (
18 "errors"
19 "fmt"
20 "io/ioutil"
21 "os"
22 "path/filepath"
23 "reflect"
24 "sort"
25 "strings"
26 "testing"
27
28 "android/soong/android"
Nan Zhangdb0b9a32017-02-27 10:12:13 -080029)
30
Nan Zhangd4e641b2017-07-12 12:55:28 -070031type pyModule struct {
Nan Zhang1db85402017-12-18 13:20:23 -080032 name string
33 actualVersion string
34 pyRunfiles []string
35 srcsZip string
36 depsSrcsZips []string
Nan Zhangdb0b9a32017-02-27 10:12:13 -080037}
38
39var (
40 buildNamePrefix = "soong_python_test"
41 moduleVariantErrTemplate = "%s: module %q variant %q: "
42 pkgPathErrTemplate = moduleVariantErrTemplate +
Nan Zhangd4e641b2017-07-12 12:55:28 -070043 "pkg_path: %q must be a relative path contained in par file."
Nan Zhangdb0b9a32017-02-27 10:12:13 -080044 badIdentifierErrTemplate = moduleVariantErrTemplate +
45 "srcs: the path %q contains invalid token %q."
46 dupRunfileErrTemplate = moduleVariantErrTemplate +
Nan Zhangbea09752018-05-31 12:49:33 -070047 "found two files to be placed at the same location within zip %q." +
Nan Zhangdb0b9a32017-02-27 10:12:13 -080048 " First file: in module %s at path %q." +
49 " Second file: in module %s at path %q."
50 noSrcFileErr = moduleVariantErrTemplate + "doesn't have any source files!"
Nan Zhangb8fa1972017-12-22 16:12:00 -080051 badSrcFileExtErr = moduleVariantErrTemplate + "srcs: found non (.py|.proto) file: %q!"
52 badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py|.proto) file: %q!"
Nan Zhangdb0b9a32017-02-27 10:12:13 -080053 bpFile = "Blueprints"
54
55 data = []struct {
56 desc string
57 mockFiles map[string][]byte
58
59 errors []string
Nan Zhangd4e641b2017-07-12 12:55:28 -070060 expectedBinaries []pyModule
Nan Zhangdb0b9a32017-02-27 10:12:13 -080061 }{
62 {
63 desc: "module without any src files",
64 mockFiles: map[string][]byte{
65 bpFile: []byte(`subdirs = ["dir"]`),
66 filepath.Join("dir", bpFile): []byte(
67 `python_library_host {
68 name: "lib1",
69 }`,
70 ),
71 },
72 errors: []string{
73 fmt.Sprintf(noSrcFileErr,
74 "dir/Blueprints:1:1", "lib1", "PY3"),
75 },
76 },
77 {
78 desc: "module with bad src file ext",
79 mockFiles: map[string][]byte{
80 bpFile: []byte(`subdirs = ["dir"]`),
81 filepath.Join("dir", bpFile): []byte(
82 `python_library_host {
83 name: "lib1",
84 srcs: [
85 "file1.exe",
86 ],
87 }`,
88 ),
89 "dir/file1.exe": nil,
90 },
91 errors: []string{
92 fmt.Sprintf(badSrcFileExtErr,
93 "dir/Blueprints:3:11", "lib1", "PY3", "dir/file1.exe"),
94 },
95 },
96 {
97 desc: "module with bad data file ext",
98 mockFiles: map[string][]byte{
99 bpFile: []byte(`subdirs = ["dir"]`),
100 filepath.Join("dir", bpFile): []byte(
101 `python_library_host {
102 name: "lib1",
103 srcs: [
104 "file1.py",
105 ],
106 data: [
107 "file2.py",
108 ],
109 }`,
110 ),
111 "dir/file1.py": nil,
112 "dir/file2.py": nil,
113 },
114 errors: []string{
115 fmt.Sprintf(badDataFileExtErr,
116 "dir/Blueprints:6:11", "lib1", "PY3", "dir/file2.py"),
117 },
118 },
119 {
120 desc: "module with bad pkg_path format",
121 mockFiles: map[string][]byte{
122 bpFile: []byte(`subdirs = ["dir"]`),
123 filepath.Join("dir", bpFile): []byte(
124 `python_library_host {
125 name: "lib1",
126 pkg_path: "a/c/../../",
127 srcs: [
128 "file1.py",
129 ],
130 }
131
132 python_library_host {
133 name: "lib2",
134 pkg_path: "a/c/../../../",
135 srcs: [
136 "file1.py",
137 ],
138 }
139
140 python_library_host {
141 name: "lib3",
142 pkg_path: "/a/c/../../",
143 srcs: [
144 "file1.py",
145 ],
146 }`,
147 ),
148 "dir/file1.py": nil,
149 },
150 errors: []string{
151 fmt.Sprintf(pkgPathErrTemplate,
152 "dir/Blueprints:11:15", "lib2", "PY3", "a/c/../../../"),
153 fmt.Sprintf(pkgPathErrTemplate,
154 "dir/Blueprints:19:15", "lib3", "PY3", "/a/c/../../"),
155 },
156 },
157 {
158 desc: "module with bad runfile src path format",
159 mockFiles: map[string][]byte{
160 bpFile: []byte(`subdirs = ["dir"]`),
161 filepath.Join("dir", bpFile): []byte(
162 `python_library_host {
163 name: "lib1",
164 pkg_path: "a/b/c/",
165 srcs: [
166 ".file1.py",
167 "123/file1.py",
168 "-e/f/file1.py",
169 ],
170 }`,
171 ),
172 "dir/.file1.py": nil,
173 "dir/123/file1.py": nil,
174 "dir/-e/f/file1.py": nil,
175 },
176 errors: []string{
177 fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
Nan Zhangbea09752018-05-31 12:49:33 -0700178 "lib1", "PY3", "a/b/c/-e/f/file1.py", "-e"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800179 fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
Nan Zhangbea09752018-05-31 12:49:33 -0700180 "lib1", "PY3", "a/b/c/.file1.py", ".file1"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800181 fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
Nan Zhangbea09752018-05-31 12:49:33 -0700182 "lib1", "PY3", "a/b/c/123/file1.py", "123"),
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800183 },
184 },
185 {
186 desc: "module with duplicate runfile path",
187 mockFiles: map[string][]byte{
188 bpFile: []byte(`subdirs = ["dir"]`),
189 filepath.Join("dir", bpFile): []byte(
190 `python_library_host {
191 name: "lib1",
192 pkg_path: "a/b/",
193 srcs: [
194 "c/file1.py",
195 ],
196 }
197
198 python_library_host {
199 name: "lib2",
200 pkg_path: "a/b/c/",
201 srcs: [
202 "file1.py",
203 ],
204 libs: [
205 "lib1",
206 ],
207 }
208 `,
209 ),
210 "dir/c/file1.py": nil,
211 "dir/file1.py": nil,
212 },
213 errors: []string{
214 fmt.Sprintf(dupRunfileErrTemplate, "dir/Blueprints:9:6",
Nan Zhangbea09752018-05-31 12:49:33 -0700215 "lib2", "PY3", "a/b/c/file1.py", "lib2", "dir/file1.py",
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800216 "lib1", "dir/c/file1.py"),
217 },
218 },
219 {
220 desc: "module for testing dependencies",
221 mockFiles: map[string][]byte{
222 bpFile: []byte(`subdirs = ["dir"]`),
223 filepath.Join("dir", bpFile): []byte(
Nan Zhanga3fc4ba2017-07-20 17:43:37 -0700224 `python_defaults {
225 name: "default_lib",
226 srcs: [
227 "default.py",
228 ],
229 version: {
230 py2: {
231 enabled: true,
232 srcs: [
233 "default_py2.py",
234 ],
235 },
236 py3: {
237 enabled: false,
238 srcs: [
239 "default_py3.py",
240 ],
241 },
242 },
243 }
244
245 python_library_host {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800246 name: "lib5",
247 pkg_path: "a/b/",
248 srcs: [
249 "file1.py",
250 ],
251 version: {
252 py2: {
253 enabled: true,
254 },
255 py3: {
256 enabled: true,
257 },
258 },
259 }
260
261 python_library_host {
262 name: "lib6",
263 pkg_path: "c/d/",
264 srcs: [
265 "file2.py",
266 ],
267 libs: [
268 "lib5",
269 ],
270 }
271
272 python_binary_host {
273 name: "bin",
Nan Zhanga3fc4ba2017-07-20 17:43:37 -0700274 defaults: ["default_lib"],
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800275 pkg_path: "e/",
276 srcs: [
277 "bin.py",
278 ],
279 libs: [
280 "lib5",
281 ],
282 version: {
283 py3: {
284 enabled: true,
285 srcs: [
286 "file4.py",
287 ],
288 libs: [
289 "lib6",
290 ],
291 },
292 },
293 }`,
294 ),
Nan Zhanga3fc4ba2017-07-20 17:43:37 -0700295 filepath.Join("dir", "default.py"): nil,
296 filepath.Join("dir", "default_py2.py"): nil,
297 filepath.Join("dir", "default_py3.py"): nil,
298 filepath.Join("dir", "file1.py"): nil,
299 filepath.Join("dir", "file2.py"): nil,
300 filepath.Join("dir", "bin.py"): nil,
301 filepath.Join("dir", "file4.py"): nil,
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800302 stubTemplateHost: []byte(`PYTHON_BINARY = '%interpreter%'
303 MAIN_FILE = '%main%'`),
304 },
Nan Zhangd4e641b2017-07-12 12:55:28 -0700305 expectedBinaries: []pyModule{
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800306 {
307 name: "bin",
308 actualVersion: "PY3",
309 pyRunfiles: []string{
Nan Zhangbea09752018-05-31 12:49:33 -0700310 "e/default.py",
311 "e/bin.py",
312 "e/default_py3.py",
313 "e/file4.py",
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800314 },
Nan Zhangb8fa1972017-12-22 16:12:00 -0800315 srcsZip: "@prefix@/.intermediates/dir/bin/PY3/bin.py.srcszip",
Nan Zhang1db85402017-12-18 13:20:23 -0800316 depsSrcsZips: []string{
Nan Zhangb8fa1972017-12-22 16:12:00 -0800317 "@prefix@/.intermediates/dir/lib5/PY3/lib5.py.srcszip",
318 "@prefix@/.intermediates/dir/lib6/PY3/lib6.py.srcszip",
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800319 },
320 },
321 },
322 },
323 }
324)
325
326func TestPythonModule(t *testing.T) {
327 config, buildDir := setupBuildEnv(t)
Nan Zhangaac67d32017-06-12 10:49:42 -0700328 defer tearDownBuildEnv(buildDir)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800329 for _, d := range data {
330 t.Run(d.desc, func(t *testing.T) {
Colin Crosscec81712017-07-13 14:43:27 -0700331 ctx := android.NewTestContext()
332 ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
333 ctx.BottomUp("version_split", versionSplitMutator()).Parallel()
334 })
Colin Cross4b49b762019-11-22 15:25:03 -0800335 ctx.RegisterModuleType("python_library_host", PythonLibraryHostFactory)
336 ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
337 ctx.RegisterModuleType("python_defaults", defaultsFactory)
Nan Zhanga3fc4ba2017-07-20 17:43:37 -0700338 ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
Colin Crosscec81712017-07-13 14:43:27 -0700339 ctx.Register()
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800340 ctx.MockFileSystem(d.mockFiles)
341 _, testErrs := ctx.ParseBlueprintsFiles(bpFile)
Logan Chien42039712018-03-12 16:29:17 +0800342 android.FailIfErrored(t, testErrs)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800343 _, actErrs := ctx.PrepareBuildActions(config)
344 if len(actErrs) > 0 {
345 testErrs = append(testErrs, expectErrors(t, actErrs, d.errors)...)
346 } else {
347 for _, e := range d.expectedBinaries {
348 testErrs = append(testErrs,
349 expectModule(t, ctx, buildDir, e.name,
350 e.actualVersion,
Nan Zhang1db85402017-12-18 13:20:23 -0800351 e.srcsZip,
352 e.pyRunfiles,
353 e.depsSrcsZips)...)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800354 }
355 }
Logan Chien42039712018-03-12 16:29:17 +0800356 android.FailIfErrored(t, testErrs)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800357 })
358 }
359}
360
361func expectErrors(t *testing.T, actErrs []error, expErrs []string) (testErrs []error) {
362 actErrStrs := []string{}
363 for _, v := range actErrs {
364 actErrStrs = append(actErrStrs, v.Error())
365 }
366 sort.Strings(actErrStrs)
367 if len(actErrStrs) != len(expErrs) {
368 t.Errorf("got (%d) errors, expected (%d) errors!", len(actErrStrs), len(expErrs))
369 for _, v := range actErrStrs {
370 testErrs = append(testErrs, errors.New(v))
371 }
372 } else {
373 sort.Strings(expErrs)
374 for i, v := range actErrStrs {
375 if v != expErrs[i] {
376 testErrs = append(testErrs, errors.New(v))
377 }
378 }
379 }
380
381 return
382}
383
Nan Zhang1db85402017-12-18 13:20:23 -0800384func expectModule(t *testing.T, ctx *android.TestContext, buildDir, name, variant, expectedSrcsZip string,
385 expectedPyRunfiles, expectedDepsSrcsZips []string) (testErrs []error) {
Colin Crosscec81712017-07-13 14:43:27 -0700386 module := ctx.ModuleForTests(name, variant)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800387
Nan Zhangd4e641b2017-07-12 12:55:28 -0700388 base, baseOk := module.Module().(*Module)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800389 if !baseOk {
390 t.Fatalf("%s is not Python module!", name)
391 }
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800392
Nan Zhang1db85402017-12-18 13:20:23 -0800393 actualPyRunfiles := []string{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800394 for _, path := range base.srcsPathMappings {
Nan Zhang1db85402017-12-18 13:20:23 -0800395 actualPyRunfiles = append(actualPyRunfiles, path.dest)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800396 }
397
Nan Zhang1db85402017-12-18 13:20:23 -0800398 if !reflect.DeepEqual(actualPyRunfiles, expectedPyRunfiles) {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800399 testErrs = append(testErrs, errors.New(fmt.Sprintf(
400 `binary "%s" variant "%s" has unexpected pyRunfiles: %q!`,
401 base.Name(),
Nan Zhangd4e641b2017-07-12 12:55:28 -0700402 base.properties.Actual_version,
Nan Zhang1db85402017-12-18 13:20:23 -0800403 actualPyRunfiles)))
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800404 }
405
Nan Zhang1db85402017-12-18 13:20:23 -0800406 if base.srcsZip.String() != strings.Replace(expectedSrcsZip, "@prefix@", buildDir, 1) {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800407 testErrs = append(testErrs, errors.New(fmt.Sprintf(
Nan Zhang1db85402017-12-18 13:20:23 -0800408 `binary "%s" variant "%s" has unexpected srcsZip: %q!`,
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800409 base.Name(),
Nan Zhangd4e641b2017-07-12 12:55:28 -0700410 base.properties.Actual_version,
Nan Zhang1db85402017-12-18 13:20:23 -0800411 base.srcsZip)))
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800412 }
413
Nan Zhang1db85402017-12-18 13:20:23 -0800414 for i, _ := range expectedDepsSrcsZips {
415 expectedDepsSrcsZips[i] = strings.Replace(expectedDepsSrcsZips[i], "@prefix@", buildDir, 1)
416 }
417 if !reflect.DeepEqual(base.depsSrcsZips.Strings(), expectedDepsSrcsZips) {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800418 testErrs = append(testErrs, errors.New(fmt.Sprintf(
Nan Zhang1db85402017-12-18 13:20:23 -0800419 `binary "%s" variant "%s" has unexpected depsSrcsZips: %q!`,
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800420 base.Name(),
Nan Zhangd4e641b2017-07-12 12:55:28 -0700421 base.properties.Actual_version,
Nan Zhang1db85402017-12-18 13:20:23 -0800422 base.depsSrcsZips)))
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800423 }
424
425 return
426}
427
428func setupBuildEnv(t *testing.T) (config android.Config, buildDir string) {
429 buildDir, err := ioutil.TempDir("", buildNamePrefix)
430 if err != nil {
431 t.Fatal(err)
432 }
433
Colin Cross6ccbc912017-10-10 23:07:38 -0700434 config = android.TestConfig(buildDir, nil)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800435
436 return
437}
438
Nan Zhangaac67d32017-06-12 10:49:42 -0700439func tearDownBuildEnv(buildDir string) {
440 os.RemoveAll(buildDir)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800441}