• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1package bp2build
2
3import (
4	"fmt"
5	"strings"
6	"testing"
7
8	"android/soong/android"
9	"android/soong/python"
10)
11
12// TODO(alexmarquez): Should be lifted into a generic Bp2Build file
13type PythonLibBp2Build func(ctx android.TopDownMutatorContext)
14
15type pythonLibBp2BuildTestCase struct {
16	description          string
17	filesystem           map[string]string
18	blueprint            string
19	expectedBazelTargets []testBazelTarget
20	dir                  string
21	expectedError        error
22}
23
24func convertPythonLibTestCaseToBp2build_Host(tc pythonLibBp2BuildTestCase) Bp2buildTestCase {
25	for i := range tc.expectedBazelTargets {
26		tc.expectedBazelTargets[i].attrs["target_compatible_with"] = `select({
27        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
28        "//conditions:default": [],
29    })`
30	}
31
32	return convertPythonLibTestCaseToBp2build(tc)
33}
34
35func convertPythonLibTestCaseToBp2build(tc pythonLibBp2BuildTestCase) Bp2buildTestCase {
36	var bp2BuildTargets []string
37	for _, t := range tc.expectedBazelTargets {
38		bp2BuildTargets = append(bp2BuildTargets, MakeBazelTarget(t.typ, t.name, t.attrs))
39	}
40	// Copy the filesystem so that we can change stuff in it later without it
41	// affecting the original pythonLibBp2BuildTestCase
42	filesystemCopy := make(map[string]string)
43	for k, v := range tc.filesystem {
44		filesystemCopy[k] = v
45	}
46	return Bp2buildTestCase{
47		Description:          tc.description,
48		Filesystem:           filesystemCopy,
49		Blueprint:            tc.blueprint,
50		ExpectedBazelTargets: bp2BuildTargets,
51		Dir:                  tc.dir,
52		ExpectedErr:          tc.expectedError,
53	}
54}
55
56func runPythonLibraryTestCase(t *testing.T, tc pythonLibBp2BuildTestCase) {
57	t.Helper()
58	testCase := convertPythonLibTestCaseToBp2build(tc)
59	testCase.Description = fmt.Sprintf(testCase.Description, "python_library")
60	testCase.Blueprint = fmt.Sprintf(testCase.Blueprint, "python_library")
61	for name, contents := range testCase.Filesystem {
62		if strings.HasSuffix(name, "Android.bp") {
63			testCase.Filesystem[name] = fmt.Sprintf(contents, "python_library")
64		}
65	}
66	testCase.ModuleTypeUnderTest = "python_library"
67	testCase.ModuleTypeUnderTestFactory = python.PythonLibraryFactory
68
69	RunBp2BuildTestCaseSimple(t, testCase)
70}
71
72func runPythonLibraryHostTestCase(t *testing.T, tc pythonLibBp2BuildTestCase) {
73	t.Helper()
74	testCase := convertPythonLibTestCaseToBp2build_Host(tc)
75	testCase.Description = fmt.Sprintf(testCase.Description, "python_library_host")
76	testCase.Blueprint = fmt.Sprintf(testCase.Blueprint, "python_library_host")
77	for name, contents := range testCase.Filesystem {
78		if strings.HasSuffix(name, "Android.bp") {
79			testCase.Filesystem[name] = fmt.Sprintf(contents, "python_library_host")
80		}
81	}
82	testCase.ModuleTypeUnderTest = "python_library_host"
83	testCase.ModuleTypeUnderTestFactory = python.PythonLibraryHostFactory
84	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
85		ctx.RegisterModuleType("python_library", python.PythonLibraryFactory)
86	},
87		testCase)
88}
89
90func runPythonLibraryTestCases(t *testing.T, tc pythonLibBp2BuildTestCase) {
91	t.Helper()
92	runPythonLibraryTestCase(t, tc)
93	runPythonLibraryHostTestCase(t, tc)
94}
95
96func TestSimplePythonLib(t *testing.T) {
97	testCases := []pythonLibBp2BuildTestCase{
98		{
99			description: "simple %s converts to a native py_library",
100			filesystem: map[string]string{
101				"a.py":           "",
102				"b/c.py":         "",
103				"b/d.py":         "",
104				"b/e.py":         "",
105				"files/data.txt": "",
106			},
107			blueprint: `%s {
108    name: "foo",
109    srcs: ["**/*.py"],
110    exclude_srcs: ["b/e.py"],
111    data: ["files/data.txt",],
112    libs: ["bar"],
113    bazel_module: { bp2build_available: true },
114}
115    python_library {
116      name: "bar",
117      srcs: ["b/e.py"],
118      bazel_module: { bp2build_available: false },
119    }`,
120			expectedBazelTargets: []testBazelTarget{
121				{
122					typ:  "py_library",
123					name: "foo",
124					attrs: AttrNameToString{
125						"data": `["files/data.txt"]`,
126						"deps": `[":bar"]`,
127						"srcs": `[
128        "a.py",
129        "b/c.py",
130        "b/d.py",
131    ]`,
132						"srcs_version": `"PY3"`,
133						"imports":      `["."]`,
134					},
135				},
136			},
137		},
138		{
139			description: "py2 %s converts to a native py_library",
140			blueprint: `%s {
141    name: "foo",
142    srcs: ["a.py"],
143    version: {
144        py2: {
145            enabled: true,
146        },
147        py3: {
148            enabled: false,
149        },
150    },
151
152    bazel_module: { bp2build_available: true },
153}`,
154			expectedBazelTargets: []testBazelTarget{
155				{
156					typ:  "py_library",
157					name: "foo",
158					attrs: AttrNameToString{
159						"srcs":         `["a.py"]`,
160						"srcs_version": `"PY2"`,
161						"imports":      `["."]`,
162					},
163				},
164			},
165		},
166		{
167			description: "py3 %s converts to a native py_library",
168			blueprint: `%s {
169    name: "foo",
170    srcs: ["a.py"],
171    version: {
172        py2: {
173            enabled: false,
174        },
175        py3: {
176            enabled: true,
177        },
178    },
179
180    bazel_module: { bp2build_available: true },
181}`,
182			expectedBazelTargets: []testBazelTarget{
183				{
184					typ:  "py_library",
185					name: "foo",
186					attrs: AttrNameToString{
187						"srcs":         `["a.py"]`,
188						"srcs_version": `"PY3"`,
189						"imports":      `["."]`,
190					},
191				},
192			},
193		},
194		{
195			description: "py2&3 %s converts to a native py_library",
196			blueprint: `%s {
197    name: "foo",
198    srcs: ["a.py"],
199    version: {
200        py2: {
201            enabled: true,
202        },
203        py3: {
204            enabled: true,
205        },
206    },
207
208    bazel_module: { bp2build_available: true },
209}`,
210			expectedBazelTargets: []testBazelTarget{
211				{
212					// srcs_version is PY2ANDPY3 by default.
213					typ:  "py_library",
214					name: "foo",
215					attrs: AttrNameToString{
216						"srcs":    `["a.py"]`,
217						"imports": `["."]`,
218					},
219				},
220			},
221		},
222		{
223			description: "%s: pkg_path in a subdirectory of the same name converts correctly",
224			dir:         "mylib/subpackage",
225			filesystem: map[string]string{
226				"mylib/subpackage/a.py": "",
227				"mylib/subpackage/Android.bp": `%s {
228				name: "foo",
229				srcs: ["a.py"],
230				pkg_path: "mylib/subpackage",
231
232				bazel_module: { bp2build_available: true },
233			}`,
234			},
235			blueprint: `%s {name: "bar"}`,
236			expectedBazelTargets: []testBazelTarget{
237				{
238					// srcs_version is PY2ANDPY3 by default.
239					typ:  "py_library",
240					name: "foo",
241					attrs: AttrNameToString{
242						"srcs":         `["a.py"]`,
243						"imports":      `["../.."]`,
244						"srcs_version": `"PY3"`,
245					},
246				},
247			},
248		},
249		{
250			description: "%s: pkg_path in a subdirectory of a different name fails",
251			dir:         "mylib/subpackage",
252			filesystem: map[string]string{
253				"mylib/subpackage/a.py": "",
254				"mylib/subpackage/Android.bp": `%s {
255				name: "foo",
256				srcs: ["a.py"],
257				pkg_path: "mylib/subpackage2",
258				bazel_module: { bp2build_available: true },
259			}`,
260			},
261			blueprint:     `%s {name: "bar"}`,
262			expectedError: fmt.Errorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in."),
263		},
264	}
265
266	for _, tc := range testCases {
267		t.Run(tc.description, func(t *testing.T) {
268			runPythonLibraryTestCases(t, tc)
269		})
270	}
271}
272
273func TestPythonArchVariance(t *testing.T) {
274	runPythonLibraryTestCases(t, pythonLibBp2BuildTestCase{
275		description: "test %s arch variants",
276		filesystem: map[string]string{
277			"dir/arm.py": "",
278			"dir/x86.py": "",
279		},
280		blueprint: `%s {
281					 name: "foo",
282					 arch: {
283						 arm: {
284							 srcs: ["arm.py"],
285						 },
286						 x86: {
287							 srcs: ["x86.py"],
288						 },
289					},
290				 }`,
291		expectedBazelTargets: []testBazelTarget{
292			{
293				typ:  "py_library",
294				name: "foo",
295				attrs: AttrNameToString{
296					"srcs": `select({
297        "//build/bazel/platforms/arch:arm": ["arm.py"],
298        "//build/bazel/platforms/arch:x86": ["x86.py"],
299        "//conditions:default": [],
300    })`,
301					"srcs_version": `"PY3"`,
302					"imports":      `["."]`,
303				},
304			},
305		},
306	})
307}
308
309func TestPythonLibraryWithProtobufs(t *testing.T) {
310	runPythonLibraryTestCases(t, pythonLibBp2BuildTestCase{
311		description: "test %s protobuf",
312		filesystem: map[string]string{
313			"dir/mylib.py":      "",
314			"dir/myproto.proto": "",
315		},
316		blueprint: `%s {
317					 name: "foo",
318					 srcs: [
319						"dir/mylib.py",
320						"dir/myproto.proto",
321					 ],
322				 }`,
323		expectedBazelTargets: []testBazelTarget{
324			{
325				typ:  "proto_library",
326				name: "foo_proto",
327				attrs: AttrNameToString{
328					"srcs": `["dir/myproto.proto"]`,
329				},
330			},
331			{
332				typ:  "py_proto_library",
333				name: "foo_py_proto",
334				attrs: AttrNameToString{
335					"deps": `[":foo_proto"]`,
336				},
337			},
338			{
339				typ:  "py_library",
340				name: "foo",
341				attrs: AttrNameToString{
342					"srcs":         `["dir/mylib.py"]`,
343					"srcs_version": `"PY3"`,
344					"imports":      `["."]`,
345					"deps":         `[":foo_py_proto"]`,
346				},
347			},
348		},
349	})
350}
351