blob: 60a1c82a9c7392499291ce666fa37269b4d105af [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 +
47 "found two files to be placed at the same runfiles location %q." +
48 " 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",
178 "lib1", "PY3", "runfiles/a/b/c/-e/f/file1.py", "-e"),
179 fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
180 "lib1", "PY3", "runfiles/a/b/c/.file1.py", ".file1"),
181 fmt.Sprintf(badIdentifierErrTemplate, "dir/Blueprints:4:11",
182 "lib1", "PY3", "runfiles/a/b/c/123/file1.py", "123"),
183 },
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",
215 "lib2", "PY3", "runfiles/a/b/c/file1.py", "lib2", "dir/file1.py",
216 "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 Zhanga3fc4ba2017-07-20 17:43:37 -0700310 "runfiles/e/default.py",
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800311 "runfiles/e/bin.py",
Nan Zhanga3fc4ba2017-07-20 17:43:37 -0700312 "runfiles/e/default_py3.py",
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800313 "runfiles/e/file4.py",
314 },
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 Cross36242852017-06-23 15:06:31 -0700335 ctx.RegisterModuleType("python_library_host",
336 android.ModuleFactoryAdaptor(PythonLibraryHostFactory))
337 ctx.RegisterModuleType("python_binary_host",
338 android.ModuleFactoryAdaptor(PythonBinaryHostFactory))
Nan Zhanga3fc4ba2017-07-20 17:43:37 -0700339 ctx.RegisterModuleType("python_defaults",
340 android.ModuleFactoryAdaptor(defaultsFactory))
341 ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
Colin Crosscec81712017-07-13 14:43:27 -0700342 ctx.Register()
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800343 ctx.MockFileSystem(d.mockFiles)
344 _, testErrs := ctx.ParseBlueprintsFiles(bpFile)
Logan Chien42039712018-03-12 16:29:17 +0800345 android.FailIfErrored(t, testErrs)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800346 _, actErrs := ctx.PrepareBuildActions(config)
347 if len(actErrs) > 0 {
348 testErrs = append(testErrs, expectErrors(t, actErrs, d.errors)...)
349 } else {
350 for _, e := range d.expectedBinaries {
351 testErrs = append(testErrs,
352 expectModule(t, ctx, buildDir, e.name,
353 e.actualVersion,
Nan Zhang1db85402017-12-18 13:20:23 -0800354 e.srcsZip,
355 e.pyRunfiles,
356 e.depsSrcsZips)...)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800357 }
358 }
Logan Chien42039712018-03-12 16:29:17 +0800359 android.FailIfErrored(t, testErrs)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800360 })
361 }
362}
363
364func expectErrors(t *testing.T, actErrs []error, expErrs []string) (testErrs []error) {
365 actErrStrs := []string{}
366 for _, v := range actErrs {
367 actErrStrs = append(actErrStrs, v.Error())
368 }
369 sort.Strings(actErrStrs)
370 if len(actErrStrs) != len(expErrs) {
371 t.Errorf("got (%d) errors, expected (%d) errors!", len(actErrStrs), len(expErrs))
372 for _, v := range actErrStrs {
373 testErrs = append(testErrs, errors.New(v))
374 }
375 } else {
376 sort.Strings(expErrs)
377 for i, v := range actErrStrs {
378 if v != expErrs[i] {
379 testErrs = append(testErrs, errors.New(v))
380 }
381 }
382 }
383
384 return
385}
386
Nan Zhang1db85402017-12-18 13:20:23 -0800387func expectModule(t *testing.T, ctx *android.TestContext, buildDir, name, variant, expectedSrcsZip string,
388 expectedPyRunfiles, expectedDepsSrcsZips []string) (testErrs []error) {
Colin Crosscec81712017-07-13 14:43:27 -0700389 module := ctx.ModuleForTests(name, variant)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800390
Nan Zhangd4e641b2017-07-12 12:55:28 -0700391 base, baseOk := module.Module().(*Module)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800392 if !baseOk {
393 t.Fatalf("%s is not Python module!", name)
394 }
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800395
Nan Zhang1db85402017-12-18 13:20:23 -0800396 actualPyRunfiles := []string{}
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800397 for _, path := range base.srcsPathMappings {
Nan Zhang1db85402017-12-18 13:20:23 -0800398 actualPyRunfiles = append(actualPyRunfiles, path.dest)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800399 }
400
Nan Zhang1db85402017-12-18 13:20:23 -0800401 if !reflect.DeepEqual(actualPyRunfiles, expectedPyRunfiles) {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800402 testErrs = append(testErrs, errors.New(fmt.Sprintf(
403 `binary "%s" variant "%s" has unexpected pyRunfiles: %q!`,
404 base.Name(),
Nan Zhangd4e641b2017-07-12 12:55:28 -0700405 base.properties.Actual_version,
Nan Zhang1db85402017-12-18 13:20:23 -0800406 actualPyRunfiles)))
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800407 }
408
Nan Zhang1db85402017-12-18 13:20:23 -0800409 if base.srcsZip.String() != strings.Replace(expectedSrcsZip, "@prefix@", buildDir, 1) {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800410 testErrs = append(testErrs, errors.New(fmt.Sprintf(
Nan Zhang1db85402017-12-18 13:20:23 -0800411 `binary "%s" variant "%s" has unexpected srcsZip: %q!`,
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800412 base.Name(),
Nan Zhangd4e641b2017-07-12 12:55:28 -0700413 base.properties.Actual_version,
Nan Zhang1db85402017-12-18 13:20:23 -0800414 base.srcsZip)))
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800415 }
416
Nan Zhang1db85402017-12-18 13:20:23 -0800417 for i, _ := range expectedDepsSrcsZips {
418 expectedDepsSrcsZips[i] = strings.Replace(expectedDepsSrcsZips[i], "@prefix@", buildDir, 1)
419 }
420 if !reflect.DeepEqual(base.depsSrcsZips.Strings(), expectedDepsSrcsZips) {
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800421 testErrs = append(testErrs, errors.New(fmt.Sprintf(
Nan Zhang1db85402017-12-18 13:20:23 -0800422 `binary "%s" variant "%s" has unexpected depsSrcsZips: %q!`,
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800423 base.Name(),
Nan Zhangd4e641b2017-07-12 12:55:28 -0700424 base.properties.Actual_version,
Nan Zhang1db85402017-12-18 13:20:23 -0800425 base.depsSrcsZips)))
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800426 }
427
428 return
429}
430
431func setupBuildEnv(t *testing.T) (config android.Config, buildDir string) {
432 buildDir, err := ioutil.TempDir("", buildNamePrefix)
433 if err != nil {
434 t.Fatal(err)
435 }
436
Colin Cross6ccbc912017-10-10 23:07:38 -0700437 config = android.TestConfig(buildDir, nil)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800438
439 return
440}
441
Nan Zhangaac67d32017-06-12 10:49:42 -0700442func tearDownBuildEnv(buildDir string) {
443 os.RemoveAll(buildDir)
Nan Zhangdb0b9a32017-02-27 10:12:13 -0800444}