1# Copyright 2023 The Bazel Authors. 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 15"""render_pkg_aliases tests""" 16 17load("@rules_testing//lib:test_suite.bzl", "test_suite") 18load( 19 "//python/private/pypi:pkg_aliases.bzl", 20 "get_filename_config_settings", 21) # buildifier: disable=bzl-visibility 22load( 23 "//python/private/pypi:render_pkg_aliases.bzl", 24 "get_whl_flag_versions", 25 "render_multiplatform_pkg_aliases", 26 "render_pkg_aliases", 27) # buildifier: disable=bzl-visibility 28load("//python/private/pypi:whl_config_setting.bzl", "whl_config_setting") # buildifier: disable=bzl-visibility 29 30_tests = [] 31 32def _test_empty(env): 33 actual = render_pkg_aliases( 34 aliases = None, 35 ) 36 37 want = {} 38 39 env.expect.that_dict(actual).contains_exactly(want) 40 41_tests.append(_test_empty) 42 43def _test_legacy_aliases(env): 44 actual = render_pkg_aliases( 45 aliases = { 46 "foo": "pypi_foo", 47 }, 48 ) 49 50 want_key = "foo/BUILD.bazel" 51 want_content = """\ 52load("@rules_python//python/private/pypi:pkg_aliases.bzl", "pkg_aliases") 53 54package(default_visibility = ["//visibility:public"]) 55 56pkg_aliases( 57 name = "foo", 58 actual = "pypi_foo", 59)""" 60 61 env.expect.that_dict(actual).contains_exactly({want_key: want_content}) 62 63_tests.append(_test_legacy_aliases) 64 65def _test_bzlmod_aliases(env): 66 # Use this function as it is used in pip_repository 67 actual = render_multiplatform_pkg_aliases( 68 aliases = { 69 "bar-baz": { 70 whl_config_setting( 71 version = "3.2", 72 config_setting = "//:my_config_setting", 73 ): "pypi_32_bar_baz", 74 whl_config_setting( 75 version = "3.2", 76 config_setting = "//:my_config_setting", 77 target_platforms = [ 78 "cp32_linux_x86_64", 79 ], 80 ): "pypi_32_bar_baz_linux_x86_64", 81 whl_config_setting( 82 version = "3.2", 83 filename = "foo-0.0.0-py3-none-any.whl", 84 ): "filename_repo", 85 whl_config_setting( 86 version = "3.2", 87 filename = "foo-0.0.0-py3-none-any.whl", 88 target_platforms = [ 89 "cp32_linux_x86_64", 90 ], 91 ): "filename_repo_linux_x86_64", 92 }, 93 }, 94 extra_hub_aliases = {"bar_baz": ["foo"]}, 95 ) 96 97 want_key = "bar_baz/BUILD.bazel" 98 want_content = """\ 99load("@rules_python//python/private/pypi:pkg_aliases.bzl", "pkg_aliases") 100load("@rules_python//python/private/pypi:whl_config_setting.bzl", "whl_config_setting") 101 102package(default_visibility = ["//visibility:public"]) 103 104pkg_aliases( 105 name = "bar_baz", 106 actual = { 107 "//:my_config_setting": "pypi_32_bar_baz", 108 whl_config_setting( 109 target_platforms = ("cp32_linux_x86_64",), 110 config_setting = "//:my_config_setting", 111 version = "3.2", 112 ): "pypi_32_bar_baz_linux_x86_64", 113 whl_config_setting( 114 filename = "foo-0.0.0-py3-none-any.whl", 115 version = "3.2", 116 ): "filename_repo", 117 whl_config_setting( 118 filename = "foo-0.0.0-py3-none-any.whl", 119 target_platforms = ("cp32_linux_x86_64",), 120 version = "3.2", 121 ): "filename_repo_linux_x86_64", 122 }, 123 extra_aliases = ["foo"], 124)""" 125 126 env.expect.that_str(actual.pop("_config/BUILD.bazel")).equals( 127 """\ 128load("@rules_python//python/private/pypi:config_settings.bzl", "config_settings") 129 130config_settings( 131 name = "config_settings", 132 python_versions = ["3.2"], 133 target_platforms = ["linux_x86_64"], 134 visibility = ["//:__subpackages__"], 135)""", 136 ) 137 env.expect.that_collection(actual.keys()).contains_exactly([want_key]) 138 env.expect.that_str(actual[want_key]).equals(want_content) 139 140_tests.append(_test_bzlmod_aliases) 141 142def _test_aliases_are_created_for_all_wheels(env): 143 actual = render_pkg_aliases( 144 aliases = { 145 "bar": { 146 whl_config_setting(version = "3.1"): "pypi_31_bar", 147 whl_config_setting(version = "3.2"): "pypi_32_bar", 148 }, 149 "foo": { 150 whl_config_setting(version = "3.1"): "pypi_32_foo", 151 whl_config_setting(version = "3.2"): "pypi_31_foo", 152 }, 153 }, 154 ) 155 156 want_files = [ 157 "bar/BUILD.bazel", 158 "foo/BUILD.bazel", 159 ] 160 161 env.expect.that_dict(actual).keys().contains_exactly(want_files) 162 163_tests.append(_test_aliases_are_created_for_all_wheels) 164 165def _test_aliases_with_groups(env): 166 actual = render_pkg_aliases( 167 aliases = { 168 "bar": { 169 whl_config_setting(version = "3.1"): "pypi_31_bar", 170 whl_config_setting(version = "3.2"): "pypi_32_bar", 171 }, 172 "baz": { 173 whl_config_setting(version = "3.1"): "pypi_31_baz", 174 whl_config_setting(version = "3.2"): "pypi_32_baz", 175 }, 176 "foo": { 177 whl_config_setting(version = "3.1"): "pypi_32_foo", 178 whl_config_setting(version = "3.2"): "pypi_31_foo", 179 }, 180 }, 181 requirement_cycles = { 182 "group": ["bar", "baz"], 183 }, 184 ) 185 186 want_files = [ 187 "bar/BUILD.bazel", 188 "foo/BUILD.bazel", 189 "baz/BUILD.bazel", 190 "_groups/BUILD.bazel", 191 ] 192 env.expect.that_dict(actual).keys().contains_exactly(want_files) 193 194 want_key = "_groups/BUILD.bazel" 195 196 # Just check that it contains a private whl 197 env.expect.that_str(actual[want_key]).contains("//bar:_whl") 198 199 want_key = "bar/BUILD.bazel" 200 201 # Just check that we pass the group name 202 env.expect.that_str(actual[want_key]).contains("group_name = \"group\"") 203 204_tests.append(_test_aliases_with_groups) 205 206def _test_empty_flag_versions(env): 207 got = get_whl_flag_versions( 208 settings = [], 209 ) 210 want = {} 211 env.expect.that_dict(got).contains_exactly(want) 212 213_tests.append(_test_empty_flag_versions) 214 215def _test_get_python_versions(env): 216 got = get_whl_flag_versions( 217 settings = { 218 whl_config_setting(version = "3.3"): "foo", 219 whl_config_setting(version = "3.2"): "foo", 220 }, 221 ) 222 want = { 223 "python_versions": ["3.2", "3.3"], 224 } 225 env.expect.that_dict(got).contains_exactly(want) 226 227_tests.append(_test_get_python_versions) 228 229def _test_get_python_versions_with_target_platforms(env): 230 got = get_whl_flag_versions( 231 settings = [ 232 whl_config_setting(version = "3.3", target_platforms = ["cp33_linux_x86_64"]), 233 whl_config_setting(version = "3.2", target_platforms = ["cp32_linux_x86_64", "cp32_osx_aarch64"]), 234 ], 235 ) 236 want = { 237 "python_versions": ["3.2", "3.3"], 238 "target_platforms": [ 239 "linux_x86_64", 240 "osx_aarch64", 241 ], 242 } 243 env.expect.that_dict(got).contains_exactly(want) 244 245_tests.append(_test_get_python_versions_with_target_platforms) 246 247def _test_get_python_versions_from_filenames(env): 248 got = get_whl_flag_versions( 249 settings = [ 250 whl_config_setting( 251 version = "3.3", 252 filename = "foo-0.0.0-py3-none-" + plat + ".whl", 253 ) 254 for plat in [ 255 "linux_x86_64", 256 "manylinux_2_17_x86_64", 257 "manylinux_2_14_aarch64.musllinux_1_1_aarch64", 258 "musllinux_1_0_x86_64", 259 "manylinux2014_x86_64.manylinux_2_17_x86_64", 260 "macosx_11_0_arm64", 261 "macosx_10_9_x86_64", 262 "macosx_10_9_universal2", 263 "windows_x86_64", 264 ] 265 ], 266 ) 267 want = { 268 "glibc_versions": [(2, 14), (2, 17)], 269 "muslc_versions": [(1, 0), (1, 1)], 270 "osx_versions": [(10, 9), (11, 0)], 271 "python_versions": ["3.3"], 272 "target_platforms": [ 273 "linux_aarch64", 274 "linux_x86_64", 275 "osx_aarch64", 276 "osx_x86_64", 277 "windows_x86_64", 278 ], 279 } 280 env.expect.that_dict(got).contains_exactly(want) 281 282_tests.append(_test_get_python_versions_from_filenames) 283 284def _test_get_flag_versions_from_alias_target_platforms(env): 285 got = get_whl_flag_versions( 286 settings = [ 287 whl_config_setting( 288 version = "3.3", 289 filename = "foo-0.0.0-py3-none-" + plat + ".whl", 290 ) 291 for plat in [ 292 "windows_x86_64", 293 ] 294 ] + [ 295 whl_config_setting( 296 version = "3.3", 297 filename = "foo-0.0.0-py3-none-any.whl", 298 target_platforms = [ 299 "cp33_linux_x86_64", 300 ], 301 ), 302 ], 303 ) 304 want = { 305 "python_versions": ["3.3"], 306 "target_platforms": [ 307 "linux_x86_64", 308 "windows_x86_64", 309 ], 310 } 311 env.expect.that_dict(got).contains_exactly(want) 312 313_tests.append(_test_get_flag_versions_from_alias_target_platforms) 314 315def _test_config_settings( 316 env, 317 *, 318 filename, 319 want, 320 python_version, 321 want_versions = {}, 322 target_platforms = [], 323 glibc_versions = [], 324 muslc_versions = [], 325 osx_versions = []): 326 got, got_default_version_settings = get_filename_config_settings( 327 filename = filename, 328 target_platforms = target_platforms, 329 glibc_versions = glibc_versions, 330 muslc_versions = muslc_versions, 331 osx_versions = osx_versions, 332 python_version = python_version, 333 ) 334 env.expect.that_collection(got).contains_exactly(want) 335 env.expect.that_dict(got_default_version_settings).contains_exactly(want_versions) 336 337def _test_sdist(env): 338 # Do the first test for multiple extensions 339 for ext in [".tar.gz", ".zip"]: 340 _test_config_settings( 341 env, 342 filename = "foo-0.0.1" + ext, 343 python_version = "3.2", 344 want = [":is_cp3.2_sdist"], 345 ) 346 347 ext = ".zip" 348 _test_config_settings( 349 env, 350 filename = "foo-0.0.1" + ext, 351 python_version = "3.2", 352 target_platforms = [ 353 "linux_aarch64", 354 "linux_x86_64", 355 ], 356 want = [ 357 ":is_cp3.2_sdist_linux_aarch64", 358 ":is_cp3.2_sdist_linux_x86_64", 359 ], 360 ) 361 362_tests.append(_test_sdist) 363 364def _test_py2_py3_none_any(env): 365 _test_config_settings( 366 env, 367 filename = "foo-0.0.1-py2.py3-none-any.whl", 368 python_version = "3.2", 369 want = [ 370 ":is_cp3.2_py_none_any", 371 ], 372 ) 373 374 _test_config_settings( 375 env, 376 filename = "foo-0.0.1-py2.py3-none-any.whl", 377 python_version = "3.2", 378 target_platforms = [ 379 "osx_x86_64", 380 ], 381 want = [":is_cp3.2_py_none_any_osx_x86_64"], 382 ) 383 384_tests.append(_test_py2_py3_none_any) 385 386def _test_py3_none_any(env): 387 _test_config_settings( 388 env, 389 filename = "foo-0.0.1-py3-none-any.whl", 390 python_version = "3.1", 391 want = [":is_cp3.1_py3_none_any"], 392 ) 393 394 _test_config_settings( 395 env, 396 filename = "foo-0.0.1-py3-none-any.whl", 397 python_version = "3.1", 398 target_platforms = ["linux_x86_64"], 399 want = [":is_cp3.1_py3_none_any_linux_x86_64"], 400 ) 401 402_tests.append(_test_py3_none_any) 403 404def _test_py3_none_macosx_10_9_universal2(env): 405 _test_config_settings( 406 env, 407 filename = "foo-0.0.1-py3-none-macosx_10_9_universal2.whl", 408 python_version = "3.1", 409 osx_versions = [ 410 (10, 9), 411 (11, 0), 412 ], 413 want = [], 414 want_versions = { 415 ":is_cp3.1_py3_none_osx_aarch64_universal2": { 416 (10, 9): ":is_cp3.1_py3_none_osx_10_9_aarch64_universal2", 417 (11, 0): ":is_cp3.1_py3_none_osx_11_0_aarch64_universal2", 418 }, 419 ":is_cp3.1_py3_none_osx_x86_64_universal2": { 420 (10, 9): ":is_cp3.1_py3_none_osx_10_9_x86_64_universal2", 421 (11, 0): ":is_cp3.1_py3_none_osx_11_0_x86_64_universal2", 422 }, 423 }, 424 ) 425 426_tests.append(_test_py3_none_macosx_10_9_universal2) 427 428def _test_cp37_abi3_linux_x86_64(env): 429 _test_config_settings( 430 env, 431 filename = "foo-0.0.1-cp37-abi3-linux_x86_64.whl", 432 python_version = "3.7", 433 want = [":is_cp3.7_cp3x_abi3_linux_x86_64"], 434 ) 435 436_tests.append(_test_cp37_abi3_linux_x86_64) 437 438def _test_cp37_abi3_windows_x86_64(env): 439 _test_config_settings( 440 env, 441 filename = "foo-0.0.1-cp37-abi3-windows_x86_64.whl", 442 python_version = "3.7", 443 want = [":is_cp3.7_cp3x_abi3_windows_x86_64"], 444 ) 445 446_tests.append(_test_cp37_abi3_windows_x86_64) 447 448def _test_cp37_abi3_manylinux_2_17_x86_64(env): 449 _test_config_settings( 450 env, 451 filename = "foo-0.0.1-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", 452 python_version = "3.7", 453 glibc_versions = [ 454 (2, 16), 455 (2, 17), 456 (2, 18), 457 ], 458 want = [], 459 want_versions = { 460 ":is_cp3.7_cp3x_abi3_manylinux_x86_64": { 461 (2, 17): ":is_cp3.7_cp3x_abi3_manylinux_2_17_x86_64", 462 (2, 18): ":is_cp3.7_cp3x_abi3_manylinux_2_18_x86_64", 463 }, 464 }, 465 ) 466 467_tests.append(_test_cp37_abi3_manylinux_2_17_x86_64) 468 469def _test_cp37_abi3_manylinux_2_17_musllinux_1_1_aarch64(env): 470 # I've seen such a wheel being built for `uv` 471 _test_config_settings( 472 env, 473 filename = "foo-0.0.1-cp37-cp37-manylinux_2_17_arm64.musllinux_1_1_arm64.whl", 474 python_version = "3.7", 475 glibc_versions = [ 476 (2, 16), 477 (2, 17), 478 (2, 18), 479 ], 480 muslc_versions = [ 481 (1, 1), 482 ], 483 want = [], 484 want_versions = { 485 ":is_cp3.7_cp3x_cp_manylinux_aarch64": { 486 (2, 17): ":is_cp3.7_cp3x_cp_manylinux_2_17_aarch64", 487 (2, 18): ":is_cp3.7_cp3x_cp_manylinux_2_18_aarch64", 488 }, 489 ":is_cp3.7_cp3x_cp_musllinux_aarch64": { 490 (1, 1): ":is_cp3.7_cp3x_cp_musllinux_1_1_aarch64", 491 }, 492 }, 493 ) 494 495_tests.append(_test_cp37_abi3_manylinux_2_17_musllinux_1_1_aarch64) 496 497def render_pkg_aliases_test_suite(name): 498 """Create the test suite. 499 500 Args: 501 name: the name of the test suite 502 """ 503 test_suite(name = name, basic_tests = _tests) 504