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