• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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	"fmt"
19	"os"
20	"path/filepath"
21	"testing"
22
23	"android/soong/android"
24	"android/soong/cc"
25)
26
27type pyModule struct {
28	name          string
29	actualVersion string
30	pyRunfiles    []string
31	srcsZip       string
32	depsSrcsZips  []string
33}
34
35var (
36	buildNamePrefix = "soong_python_test"
37	// We allow maching almost anything before the actual variant so that the os/arch variant
38	// is matched.
39	moduleVariantErrTemplate = `%s: module %q variant "[a-zA-Z0-9_]*%s": `
40	pkgPathErrTemplate       = moduleVariantErrTemplate +
41		"pkg_path: %q must be a relative path contained in par file."
42	badIdentifierErrTemplate = moduleVariantErrTemplate +
43		"srcs: the path %q contains invalid subpath %q."
44	dupRunfileErrTemplate = moduleVariantErrTemplate +
45		"found two files to be placed at the same location within zip %q." +
46		" First file: in module %s at path %q." +
47		" Second file: in module %s at path %q."
48	noSrcFileErr      = moduleVariantErrTemplate + "doesn't have any source files!"
49	badSrcFileExtErr  = moduleVariantErrTemplate + "srcs: found non (.py|.proto) file: %q!"
50	badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py|.proto) file: %q!"
51	bpFile            = "Android.bp"
52
53	data = []struct {
54		desc      string
55		mockFiles android.MockFS
56
57		errors           []string
58		expectedBinaries []pyModule
59	}{
60		{
61			desc: "module without any src files",
62			mockFiles: map[string][]byte{
63				filepath.Join("dir", bpFile): []byte(
64					`python_library_host {
65						name: "lib1",
66					}`,
67				),
68			},
69			errors: []string{
70				fmt.Sprintf(noSrcFileErr,
71					"dir/Android.bp:1:1", "lib1", "PY3"),
72			},
73		},
74		{
75			desc: "module with bad src file ext",
76			mockFiles: map[string][]byte{
77				filepath.Join("dir", bpFile): []byte(
78					`python_library_host {
79						name: "lib1",
80						srcs: [
81							"file1.exe",
82						],
83					}`,
84				),
85				"dir/file1.exe": nil,
86			},
87			errors: []string{
88				fmt.Sprintf(badSrcFileExtErr,
89					"dir/Android.bp:3:11", "lib1", "PY3", "dir/file1.exe"),
90			},
91		},
92		{
93			desc: "module with bad data file ext",
94			mockFiles: map[string][]byte{
95				filepath.Join("dir", bpFile): []byte(
96					`python_library_host {
97						name: "lib1",
98						srcs: [
99							"file1.py",
100						],
101						data: [
102							"file2.py",
103						],
104					}`,
105				),
106				"dir/file1.py": nil,
107				"dir/file2.py": nil,
108			},
109			errors: []string{
110				fmt.Sprintf(badDataFileExtErr,
111					"dir/Android.bp:6:11", "lib1", "PY3", "dir/file2.py"),
112			},
113		},
114		{
115			desc: "module with bad pkg_path format",
116			mockFiles: map[string][]byte{
117				filepath.Join("dir", bpFile): []byte(
118					`python_library_host {
119						name: "lib1",
120						pkg_path: "a/c/../../",
121						srcs: [
122							"file1.py",
123						],
124					}
125
126					python_library_host {
127						name: "lib2",
128						pkg_path: "a/c/../../../",
129						srcs: [
130							"file1.py",
131						],
132					}
133
134					python_library_host {
135						name: "lib3",
136						pkg_path: "/a/c/../../",
137						srcs: [
138							"file1.py",
139						],
140					}`,
141				),
142				"dir/file1.py": nil,
143			},
144			errors: []string{
145				fmt.Sprintf(pkgPathErrTemplate,
146					"dir/Android.bp:11:15", "lib2", "PY3", "a/c/../../../"),
147				fmt.Sprintf(pkgPathErrTemplate,
148					"dir/Android.bp:19:15", "lib3", "PY3", "/a/c/../../"),
149			},
150		},
151		{
152			desc: "module with bad runfile src path format",
153			mockFiles: map[string][]byte{
154				filepath.Join("dir", bpFile): []byte(
155					`python_library_host {
156						name: "lib1",
157						pkg_path: "a/b/c/",
158						srcs: [
159							".file1.py",
160							"123/file1.py",
161							"-e/f/file1.py",
162						],
163					}`,
164				),
165				"dir/.file1.py":     nil,
166				"dir/123/file1.py":  nil,
167				"dir/-e/f/file1.py": nil,
168			},
169			errors: []string{
170				fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
171					"lib1", "PY3", "a/b/c/-e/f/file1.py", "-e"),
172				fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
173					"lib1", "PY3", "a/b/c/.file1.py", ".file1"),
174				fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
175					"lib1", "PY3", "a/b/c/123/file1.py", "123"),
176			},
177		},
178		{
179			desc: "module with duplicate runfile path",
180			mockFiles: map[string][]byte{
181				filepath.Join("dir", bpFile): []byte(
182					`python_library_host {
183						name: "lib1",
184						pkg_path: "a/b/",
185						srcs: [
186							"c/file1.py",
187						],
188					}
189
190					python_library_host {
191						name: "lib2",
192						pkg_path: "a/b/c/",
193						srcs: [
194							"file1.py",
195						],
196						libs: [
197							"lib1",
198						],
199					}
200
201					python_binary_host {
202						name: "bin",
203						pkg_path: "e/",
204						srcs: [
205							"bin.py",
206						],
207						libs: [
208							"lib2",
209						],
210					}
211					`,
212				),
213				"dir/c/file1.py": nil,
214				"dir/file1.py":   nil,
215				"dir/bin.py":     nil,
216			},
217			errors: []string{
218				fmt.Sprintf(dupRunfileErrTemplate, "dir/Android.bp:20:6",
219					"bin", "PY3", "a/b/c/file1.py", "bin", "dir/file1.py",
220					"lib1", "dir/c/file1.py"),
221			},
222		},
223		{
224			desc: "module for testing dependencies",
225			mockFiles: map[string][]byte{
226				filepath.Join("dir", bpFile): []byte(
227					`python_defaults {
228						name: "default_lib",
229						srcs: [
230							"default.py",
231						],
232						version: {
233							py2: {
234								enabled: true,
235								srcs: [
236									"default_py2.py",
237								],
238							},
239							py3: {
240								enabled: false,
241								srcs: [
242									"default_py3.py",
243								],
244							},
245						},
246					}
247
248					python_library_host {
249						name: "lib5",
250						pkg_path: "a/b/",
251						srcs: [
252							"file1.py",
253						],
254						version: {
255							py2: {
256								enabled: true,
257							},
258							py3: {
259								enabled: true,
260							},
261						},
262					}
263
264					python_library_host {
265						name: "lib6",
266						pkg_path: "c/d/",
267						srcs: [
268							"file2.py",
269						],
270						libs: [
271							"lib5",
272						],
273					}
274
275					python_binary_host {
276						name: "bin",
277						defaults: ["default_lib"],
278						pkg_path: "e/",
279						srcs: [
280							"bin.py",
281						],
282						libs: [
283							"lib5",
284						],
285						version: {
286							py3: {
287								enabled: true,
288								srcs: [
289									"file4.py",
290								],
291								libs: [
292									"lib6",
293								],
294							},
295						},
296					}`,
297				),
298				filepath.Join("dir", "default.py"):     nil,
299				filepath.Join("dir", "default_py2.py"): nil,
300				filepath.Join("dir", "default_py3.py"): nil,
301				filepath.Join("dir", "file1.py"):       nil,
302				filepath.Join("dir", "file2.py"):       nil,
303				filepath.Join("dir", "bin.py"):         nil,
304				filepath.Join("dir", "file4.py"):       nil,
305			},
306			expectedBinaries: []pyModule{
307				{
308					name:          "bin",
309					actualVersion: "PY3",
310					pyRunfiles: []string{
311						"e/default.py",
312						"e/bin.py",
313						"e/default_py3.py",
314						"e/file4.py",
315					},
316					srcsZip: "out/soong/.intermediates/dir/bin/PY3/bin.py.srcszip",
317				},
318			},
319		},
320	}
321)
322
323func TestPythonModule(t *testing.T) {
324	for _, d := range data {
325		if d.desc != "module with duplicate runfile path" {
326			continue
327		}
328		d.mockFiles[filepath.Join("common", bpFile)] = []byte(`
329python_library {
330  name: "py3-stdlib",
331  host_supported: true,
332}
333cc_binary {
334  name: "py3-launcher",
335  host_supported: true,
336}
337`)
338
339		t.Run(d.desc, func(t *testing.T) {
340			result := android.GroupFixturePreparers(
341				android.PrepareForTestWithDefaults,
342				android.PrepareForTestWithArchMutator,
343				android.PrepareForTestWithAllowMissingDependencies,
344				cc.PrepareForTestWithCcDefaultModules,
345				PrepareForTestWithPythonBuildComponents,
346				d.mockFiles.AddToFixture(),
347			).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(d.errors)).
348				RunTest(t)
349
350			if len(result.Errs) > 0 {
351				return
352			}
353
354			for _, e := range d.expectedBinaries {
355				t.Run(e.name, func(t *testing.T) {
356					expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles)
357				})
358			}
359		})
360	}
361}
362
363func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles []string) {
364	module := ctx.ModuleForTests(name, variant)
365
366	base, baseOk := module.Module().(*PythonLibraryModule)
367	if !baseOk {
368		t.Fatalf("%s is not Python module!", name)
369	}
370
371	actualPyRunfiles := []string{}
372	for _, path := range base.srcsPathMappings {
373		actualPyRunfiles = append(actualPyRunfiles, path.dest)
374	}
375
376	android.AssertDeepEquals(t, "pyRunfiles", expectedPyRunfiles, actualPyRunfiles)
377
378	android.AssertPathRelativeToTopEquals(t, "srcsZip", expectedSrcsZip, base.srcsZip)
379}
380
381func TestMain(m *testing.M) {
382	os.Exit(m.Run())
383}
384