• 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	"strings"
22	"testing"
23
24	"android/soong/android"
25	"android/soong/cc"
26
27	"github.com/google/blueprint"
28)
29
30type pyModule struct {
31	name          string
32	actualVersion string
33	pyRunfiles    []string
34	srcsZip       string
35	depsSrcsZips  []string
36}
37
38var (
39	buildNamePrefix          = "soong_python_test"
40	moduleVariantErrTemplate = `%s: module %q variant "[a-zA-Z0-9_]*": `
41	pkgPathErrTemplate       = moduleVariantErrTemplate +
42		"pkg_path: %q must be a relative path contained in par file."
43	badIdentifierErrTemplate = moduleVariantErrTemplate +
44		"srcs: the path %q contains invalid subpath %q."
45	dupRunfileErrTemplate = moduleVariantErrTemplate +
46		"found two files to be placed at the same location within zip %q." +
47		" First file: in module %s at path %q." +
48		" Second file: in module %s at path %q."
49	badSrcFileExtErr  = moduleVariantErrTemplate + `srcs: found non \(.py\|.proto\) file: %q!`
50	badDataFileExtErr = moduleVariantErrTemplate + `data: found \(.py\) 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 with bad src file ext",
62			mockFiles: map[string][]byte{
63				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,
75					"dir/Android.bp:3:11", "lib1", "dir/file1.exe"),
76			},
77		},
78		{
79			desc: "module with bad data file ext",
80			mockFiles: map[string][]byte{
81				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,
97					"dir/Android.bp:6:11", "lib1", "dir/file2.py"),
98			},
99		},
100		{
101			desc: "module with bad pkg_path format",
102			mockFiles: map[string][]byte{
103				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,
132					"dir/Android.bp:11:15", "lib2", "a/c/../../../"),
133				fmt.Sprintf(pkgPathErrTemplate,
134					"dir/Android.bp:19:15", "lib3", "/a/c/../../"),
135			},
136		},
137		{
138			desc: "module with bad runfile src path format",
139			mockFiles: map[string][]byte{
140				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{
156				fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
157					"lib1", "a/b/c/-e/f/file1.py", "-e"),
158				fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
159					"lib1", "a/b/c/.file1.py", ".file1"),
160				fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
161					"lib1", "a/b/c/123/file1.py", "123"),
162			},
163		},
164		{
165			desc: "module with duplicate runfile path",
166			mockFiles: map[string][]byte{
167				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					}
186
187					python_binary_host {
188						name: "bin",
189						pkg_path: "e/",
190						srcs: [
191							"bin.py",
192						],
193						libs: [
194							"lib2",
195						],
196					}
197					`,
198				),
199				"dir/c/file1.py": nil,
200				"dir/file1.py":   nil,
201				"dir/bin.py":     nil,
202			},
203			errors: []string{
204				fmt.Sprintf(dupRunfileErrTemplate, "dir/Android.bp:20:6",
205					"bin", "a/b/c/file1.py", "bin", "dir/file1.py",
206					"lib1", "dir/c/file1.py"),
207			},
208		},
209	}
210)
211
212func TestPythonModule(t *testing.T) {
213	for _, d := range data {
214		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`)
224
225		t.Run(d.desc, func(t *testing.T) {
226			result := android.GroupFixturePreparers(
227				android.PrepareForTestWithDefaults,
228				android.PrepareForTestWithArchMutator,
229				android.PrepareForTestWithAllowMissingDependencies,
230				cc.PrepareForTestWithCcDefaultModules,
231				PrepareForTestWithPythonBuildComponents,
232				d.mockFiles.AddToFixture(),
233			).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(d.errors)).
234				RunTest(t)
235
236			if len(result.Errs) > 0 {
237				return
238			}
239
240			for _, e := range d.expectedBinaries {
241				t.Run(e.name, func(t *testing.T) {
242					expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles)
243				})
244			}
245		})
246	}
247}
248
249func 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,
302			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
315func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles []string) {
316	module := ctx.ModuleForTests(t, name, variant)
317
318	base, baseOk := module.Module().(*PythonLibraryModule)
319	if !baseOk {
320		t.Fatalf("%s is not Python module!", name)
321	}
322
323	actualPyRunfiles := []string{}
324	for _, path := range base.srcsPathMappings {
325		actualPyRunfiles = append(actualPyRunfiles, path.dest)
326	}
327
328	android.AssertDeepEquals(t, "pyRunfiles", expectedPyRunfiles, actualPyRunfiles)
329
330	android.AssertPathRelativeToTopEquals(t, "srcsZip", expectedSrcsZip, base.srcsZip)
331}
332
333func TestMain(m *testing.M) {
334	os.Exit(m.Run())
335}
336