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("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED")  # buildifier: disable=bzl-visibility
19load("//python/private/pypi:config_settings.bzl", "config_settings")  # buildifier: disable=bzl-visibility
20load(
21    "//python/private/pypi:render_pkg_aliases.bzl",
22    "get_filename_config_settings",
23    "get_whl_flag_versions",
24    "multiplatform_whl_aliases",
25    "render_multiplatform_pkg_aliases",
26    "render_pkg_aliases",
27    "whl_alias",
28)  # buildifier: disable=bzl-visibility
29
30def _normalize_label_strings(want):
31    """normalize expected strings.
32
33    This function ensures that the desired `render_pkg_aliases` outputs are
34    normalized from `bzlmod` to `WORKSPACE` values so that we don't have to
35    have to sets of expected strings. The main difference is that under
36    `bzlmod` the `str(Label("//my_label"))` results in `"@@//my_label"` whereas
37    under `non-bzlmod` we have `"@//my_label"`. This function does
38    `string.replace("@@", "@")` to normalize the strings.
39
40    NOTE, in tests, we should only use keep `@@` usage in expectation values
41    for the test cases where the whl_alias has the `config_setting` constructed
42    from a `Label` instance.
43    """
44    if "@@" not in want:
45        fail("The expected string does not have '@@' labels, consider not using the function")
46
47    if BZLMOD_ENABLED:
48        # our expectations are already with double @
49        return want
50
51    return want.replace("@@", "@")
52
53_tests = []
54
55def _test_empty(env):
56    actual = render_pkg_aliases(
57        aliases = None,
58    )
59
60    want = {}
61
62    env.expect.that_dict(actual).contains_exactly(want)
63
64_tests.append(_test_empty)
65
66def _test_legacy_aliases(env):
67    actual = render_pkg_aliases(
68        aliases = {
69            "foo": [
70                whl_alias(repo = "pypi_foo"),
71            ],
72        },
73    )
74
75    want_key = "foo/BUILD.bazel"
76    want_content = """\
77load("@bazel_skylib//lib:selects.bzl", "selects")
78
79package(default_visibility = ["//visibility:public"])
80
81alias(
82    name = "foo",
83    actual = ":pkg",
84)
85
86alias(
87    name = "pkg",
88    actual = "@pypi_foo//:pkg",
89)
90
91alias(
92    name = "whl",
93    actual = "@pypi_foo//:whl",
94)
95
96alias(
97    name = "data",
98    actual = "@pypi_foo//:data",
99)
100
101alias(
102    name = "dist_info",
103    actual = "@pypi_foo//:dist_info",
104)"""
105
106    env.expect.that_dict(actual).contains_exactly({want_key: want_content})
107
108_tests.append(_test_legacy_aliases)
109
110def _test_bzlmod_aliases(env):
111    # Use this function as it is used in pip_repository
112    actual = render_multiplatform_pkg_aliases(
113        default_config_setting = "//:my_config_setting",
114        aliases = {
115            "bar-baz": [
116                whl_alias(version = "3.2", repo = "pypi_32_bar_baz", config_setting = "//:my_config_setting"),
117            ],
118        },
119    )
120
121    want_key = "bar_baz/BUILD.bazel"
122    want_content = """\
123load("@bazel_skylib//lib:selects.bzl", "selects")
124
125package(default_visibility = ["//visibility:public"])
126
127alias(
128    name = "bar_baz",
129    actual = ":pkg",
130)
131
132alias(
133    name = "pkg",
134    actual = selects.with_or(
135        {
136            (
137                "//:my_config_setting",
138                "//conditions:default",
139            ): "@pypi_32_bar_baz//:pkg",
140        },
141    ),
142)
143
144alias(
145    name = "whl",
146    actual = selects.with_or(
147        {
148            (
149                "//:my_config_setting",
150                "//conditions:default",
151            ): "@pypi_32_bar_baz//:whl",
152        },
153    ),
154)
155
156alias(
157    name = "data",
158    actual = selects.with_or(
159        {
160            (
161                "//:my_config_setting",
162                "//conditions:default",
163            ): "@pypi_32_bar_baz//:data",
164        },
165    ),
166)
167
168alias(
169    name = "dist_info",
170    actual = selects.with_or(
171        {
172            (
173                "//:my_config_setting",
174                "//conditions:default",
175            ): "@pypi_32_bar_baz//:dist_info",
176        },
177    ),
178)"""
179
180    env.expect.that_str(actual.pop("_config/BUILD.bazel")).equals(
181        """\
182load("@rules_python//python/private/pypi:config_settings.bzl", "config_settings")
183
184config_settings(
185    name = "config_settings",
186    glibc_versions = [],
187    muslc_versions = [],
188    osx_versions = [],
189    python_versions = ["3.2"],
190    target_platforms = [],
191    visibility = ["//:__subpackages__"],
192)""",
193    )
194    env.expect.that_collection(actual.keys()).contains_exactly([want_key])
195    env.expect.that_str(actual[want_key]).equals(want_content)
196
197_tests.append(_test_bzlmod_aliases)
198
199def _test_bzlmod_aliases_with_no_default_version(env):
200    actual = render_multiplatform_pkg_aliases(
201        default_config_setting = None,
202        aliases = {
203            "bar-baz": [
204                whl_alias(
205                    version = "3.2",
206                    repo = "pypi_32_bar_baz",
207                    # pass the label to ensure that it gets converted to string
208                    config_setting = Label("//python/config_settings:is_python_3.2"),
209                ),
210                whl_alias(version = "3.1", repo = "pypi_31_bar_baz"),
211            ],
212        },
213    )
214
215    want_key = "bar_baz/BUILD.bazel"
216    want_content = """\
217load("@bazel_skylib//lib:selects.bzl", "selects")
218
219package(default_visibility = ["//visibility:public"])
220
221_NO_MATCH_ERROR = \"\"\"\\
222No matching wheel for current configuration's Python version.
223
224The current build configuration's Python version doesn't match any of the Python
225wheels available for this wheel. This wheel supports the following Python
226configuration settings:
227    //_config:is_python_3.1
228    @@//python/config_settings:is_python_3.2
229
230To determine the current configuration's Python version, run:
231    `bazel config <config id>` (shown further below)
232and look for
233    rules_python//python/config_settings:python_version
234
235If the value is missing, then the "default" Python version is being used,
236which has a "null" version value and will not match version constraints.
237\"\"\"
238
239alias(
240    name = "bar_baz",
241    actual = ":pkg",
242)
243
244alias(
245    name = "pkg",
246    actual = selects.with_or(
247        {
248            "//_config:is_python_3.1": "@pypi_31_bar_baz//:pkg",
249            "@@//python/config_settings:is_python_3.2": "@pypi_32_bar_baz//:pkg",
250        },
251        no_match_error = _NO_MATCH_ERROR,
252    ),
253)
254
255alias(
256    name = "whl",
257    actual = selects.with_or(
258        {
259            "//_config:is_python_3.1": "@pypi_31_bar_baz//:whl",
260            "@@//python/config_settings:is_python_3.2": "@pypi_32_bar_baz//:whl",
261        },
262        no_match_error = _NO_MATCH_ERROR,
263    ),
264)
265
266alias(
267    name = "data",
268    actual = selects.with_or(
269        {
270            "//_config:is_python_3.1": "@pypi_31_bar_baz//:data",
271            "@@//python/config_settings:is_python_3.2": "@pypi_32_bar_baz//:data",
272        },
273        no_match_error = _NO_MATCH_ERROR,
274    ),
275)
276
277alias(
278    name = "dist_info",
279    actual = selects.with_or(
280        {
281            "//_config:is_python_3.1": "@pypi_31_bar_baz//:dist_info",
282            "@@//python/config_settings:is_python_3.2": "@pypi_32_bar_baz//:dist_info",
283        },
284        no_match_error = _NO_MATCH_ERROR,
285    ),
286)"""
287
288    actual.pop("_config/BUILD.bazel")
289    env.expect.that_collection(actual.keys()).contains_exactly([want_key])
290    env.expect.that_str(actual[want_key]).equals(_normalize_label_strings(want_content))
291
292_tests.append(_test_bzlmod_aliases_with_no_default_version)
293
294def _test_bzlmod_aliases_for_non_root_modules(env):
295    actual = render_pkg_aliases(
296        # NOTE @aignas 2024-01-17: if the default X.Y version coincides with the
297        # versions that are used in the root module, then this would be the same as
298        # as _test_bzlmod_aliases.
299        #
300        # However, if the root module uses a different default version than the
301        # non-root module, then we will have a no-match-error because the
302        # default_config_setting is not in the list of the versions in the
303        # whl_map.
304        default_config_setting = "//_config:is_python_3.3",
305        aliases = {
306            "bar-baz": [
307                whl_alias(version = "3.2", repo = "pypi_32_bar_baz"),
308                whl_alias(version = "3.1", repo = "pypi_31_bar_baz"),
309            ],
310        },
311    )
312
313    want_key = "bar_baz/BUILD.bazel"
314    want_content = """\
315load("@bazel_skylib//lib:selects.bzl", "selects")
316
317package(default_visibility = ["//visibility:public"])
318
319_NO_MATCH_ERROR = \"\"\"\\
320No matching wheel for current configuration's Python version.
321
322The current build configuration's Python version doesn't match any of the Python
323wheels available for this wheel. This wheel supports the following Python
324configuration settings:
325    //_config:is_python_3.1
326    //_config:is_python_3.2
327
328To determine the current configuration's Python version, run:
329    `bazel config <config id>` (shown further below)
330and look for
331    rules_python//python/config_settings:python_version
332
333If the value is missing, then the "default" Python version is being used,
334which has a "null" version value and will not match version constraints.
335\"\"\"
336
337alias(
338    name = "bar_baz",
339    actual = ":pkg",
340)
341
342alias(
343    name = "pkg",
344    actual = selects.with_or(
345        {
346            "//_config:is_python_3.1": "@pypi_31_bar_baz//:pkg",
347            "//_config:is_python_3.2": "@pypi_32_bar_baz//:pkg",
348        },
349        no_match_error = _NO_MATCH_ERROR,
350    ),
351)
352
353alias(
354    name = "whl",
355    actual = selects.with_or(
356        {
357            "//_config:is_python_3.1": "@pypi_31_bar_baz//:whl",
358            "//_config:is_python_3.2": "@pypi_32_bar_baz//:whl",
359        },
360        no_match_error = _NO_MATCH_ERROR,
361    ),
362)
363
364alias(
365    name = "data",
366    actual = selects.with_or(
367        {
368            "//_config:is_python_3.1": "@pypi_31_bar_baz//:data",
369            "//_config:is_python_3.2": "@pypi_32_bar_baz//:data",
370        },
371        no_match_error = _NO_MATCH_ERROR,
372    ),
373)
374
375alias(
376    name = "dist_info",
377    actual = selects.with_or(
378        {
379            "//_config:is_python_3.1": "@pypi_31_bar_baz//:dist_info",
380            "//_config:is_python_3.2": "@pypi_32_bar_baz//:dist_info",
381        },
382        no_match_error = _NO_MATCH_ERROR,
383    ),
384)"""
385
386    env.expect.that_collection(actual.keys()).contains_exactly([want_key])
387    env.expect.that_str(actual[want_key]).equals(want_content)
388
389_tests.append(_test_bzlmod_aliases_for_non_root_modules)
390
391def _test_aliases_are_created_for_all_wheels(env):
392    actual = render_pkg_aliases(
393        default_config_setting = "//_config:is_python_3.2",
394        aliases = {
395            "bar": [
396                whl_alias(version = "3.1", repo = "pypi_31_bar"),
397                whl_alias(version = "3.2", repo = "pypi_32_bar"),
398            ],
399            "foo": [
400                whl_alias(version = "3.1", repo = "pypi_32_foo"),
401                whl_alias(version = "3.2", repo = "pypi_31_foo"),
402            ],
403        },
404    )
405
406    want_files = [
407        "bar/BUILD.bazel",
408        "foo/BUILD.bazel",
409    ]
410
411    env.expect.that_dict(actual).keys().contains_exactly(want_files)
412
413_tests.append(_test_aliases_are_created_for_all_wheels)
414
415def _test_aliases_with_groups(env):
416    actual = render_pkg_aliases(
417        default_config_setting = "//_config:is_python_3.2",
418        aliases = {
419            "bar": [
420                whl_alias(version = "3.1", repo = "pypi_31_bar"),
421                whl_alias(version = "3.2", repo = "pypi_32_bar"),
422            ],
423            "baz": [
424                whl_alias(version = "3.1", repo = "pypi_31_baz"),
425                whl_alias(version = "3.2", repo = "pypi_32_baz"),
426            ],
427            "foo": [
428                whl_alias(version = "3.1", repo = "pypi_32_foo"),
429                whl_alias(version = "3.2", repo = "pypi_31_foo"),
430            ],
431        },
432        requirement_cycles = {
433            "group": ["bar", "baz"],
434        },
435    )
436
437    want_files = [
438        "bar/BUILD.bazel",
439        "foo/BUILD.bazel",
440        "baz/BUILD.bazel",
441        "_groups/BUILD.bazel",
442    ]
443    env.expect.that_dict(actual).keys().contains_exactly(want_files)
444
445    want_key = "_groups/BUILD.bazel"
446
447    # Just check that it contains a private whl
448    env.expect.that_str(actual[want_key]).contains("//bar:_whl")
449
450    want_key = "bar/BUILD.bazel"
451
452    # Just check that it contains a private whl
453    env.expect.that_str(actual[want_key]).contains("name = \"_whl\"")
454    env.expect.that_str(actual[want_key]).contains("name = \"whl\"")
455    env.expect.that_str(actual[want_key]).contains("\"//_groups:group_whl\"")
456
457_tests.append(_test_aliases_with_groups)
458
459def _test_empty_flag_versions(env):
460    got = get_whl_flag_versions(
461        aliases = [],
462    )
463    want = {}
464    env.expect.that_dict(got).contains_exactly(want)
465
466_tests.append(_test_empty_flag_versions)
467
468def _test_get_python_versions(env):
469    got = get_whl_flag_versions(
470        aliases = [
471            whl_alias(repo = "foo", version = "3.3"),
472            whl_alias(repo = "foo", version = "3.2"),
473        ],
474    )
475    want = {
476        "python_versions": ["3.2", "3.3"],
477    }
478    env.expect.that_dict(got).contains_exactly(want)
479
480_tests.append(_test_get_python_versions)
481
482def _test_get_python_versions_from_filenames(env):
483    got = get_whl_flag_versions(
484        aliases = [
485            whl_alias(
486                repo = "foo",
487                version = "3.3",
488                filename = "foo-0.0.0-py3-none-" + plat + ".whl",
489            )
490            for plat in [
491                "linux_x86_64",
492                "manylinux_2_17_x86_64",
493                "manylinux_2_14_aarch64.musllinux_1_1_aarch64",
494                "musllinux_1_0_x86_64",
495                "manylinux2014_x86_64.manylinux_2_17_x86_64",
496                "macosx_11_0_arm64",
497                "macosx_10_9_x86_64",
498                "macosx_10_9_universal2",
499                "windows_x86_64",
500            ]
501        ],
502    )
503    want = {
504        "glibc_versions": [(2, 14), (2, 17)],
505        "muslc_versions": [(1, 0), (1, 1)],
506        "osx_versions": [(10, 9), (11, 0)],
507        "python_versions": ["3.3"],
508        "target_platforms": [
509            "linux_aarch64",
510            "linux_x86_64",
511            "osx_aarch64",
512            "osx_x86_64",
513            "windows_x86_64",
514        ],
515    }
516    env.expect.that_dict(got).contains_exactly(want)
517
518_tests.append(_test_get_python_versions_from_filenames)
519
520def _test_get_flag_versions_from_alias_target_platforms(env):
521    got = get_whl_flag_versions(
522        aliases = [
523            whl_alias(
524                repo = "foo",
525                version = "3.3",
526                filename = "foo-0.0.0-py3-none-" + plat + ".whl",
527            )
528            for plat in [
529                "windows_x86_64",
530            ]
531        ] + [
532            whl_alias(
533                repo = "foo",
534                version = "3.3",
535                filename = "foo-0.0.0-py3-none-any.whl",
536                target_platforms = [
537                    "cp33_linux_x86_64",
538                ],
539            ),
540        ],
541    )
542    want = {
543        "python_versions": ["3.3"],
544        "target_platforms": [
545            "linux_x86_64",
546            "windows_x86_64",
547        ],
548    }
549    env.expect.that_dict(got).contains_exactly(want)
550
551_tests.append(_test_get_flag_versions_from_alias_target_platforms)
552
553def _test_config_settings(
554        env,
555        *,
556        filename,
557        want,
558        want_versions = {},
559        target_platforms = [],
560        glibc_versions = [],
561        muslc_versions = [],
562        osx_versions = [],
563        python_version = "",
564        python_default = True):
565    got, got_default_version_settings = get_filename_config_settings(
566        filename = filename,
567        target_platforms = target_platforms,
568        glibc_versions = glibc_versions,
569        muslc_versions = muslc_versions,
570        osx_versions = osx_versions,
571        python_version = python_version,
572        python_default = python_default,
573    )
574    env.expect.that_collection(got).contains_exactly(want)
575    env.expect.that_dict(got_default_version_settings).contains_exactly(want_versions)
576
577def _test_sdist(env):
578    # Do the first test for multiple extensions
579    for ext in [".tar.gz", ".zip"]:
580        _test_config_settings(
581            env,
582            filename = "foo-0.0.1" + ext,
583            want = [":is_sdist"],
584        )
585
586    ext = ".zip"
587    _test_config_settings(
588        env,
589        filename = "foo-0.0.1" + ext,
590        target_platforms = [
591            "linux_aarch64",
592        ],
593        want = [":is_sdist_linux_aarch64"],
594    )
595
596    _test_config_settings(
597        env,
598        filename = "foo-0.0.1" + ext,
599        python_version = "3.2",
600        want = [
601            ":is_sdist",
602            ":is_cp3.2_sdist",
603        ],
604    )
605
606    _test_config_settings(
607        env,
608        filename = "foo-0.0.1" + ext,
609        python_version = "3.2",
610        python_default = True,
611        target_platforms = [
612            "linux_aarch64",
613            "linux_x86_64",
614        ],
615        want = [
616            ":is_sdist_linux_aarch64",
617            ":is_cp3.2_sdist_linux_aarch64",
618            ":is_sdist_linux_x86_64",
619            ":is_cp3.2_sdist_linux_x86_64",
620        ],
621    )
622
623_tests.append(_test_sdist)
624
625def _test_py2_py3_none_any(env):
626    _test_config_settings(
627        env,
628        filename = "foo-0.0.1-py2.py3-none-any.whl",
629        want = [":is_py_none_any"],
630    )
631
632    _test_config_settings(
633        env,
634        filename = "foo-0.0.1-py2.py3-none-any.whl",
635        target_platforms = [
636            "linux_aarch64",
637        ],
638        want = [":is_py_none_any_linux_aarch64"],
639    )
640
641    _test_config_settings(
642        env,
643        filename = "foo-0.0.1-py2.py3-none-any.whl",
644        python_version = "3.2",
645        python_default = True,
646        want = [
647            ":is_py_none_any",
648            ":is_cp3.2_py_none_any",
649        ],
650    )
651
652    _test_config_settings(
653        env,
654        filename = "foo-0.0.1-py2.py3-none-any.whl",
655        python_version = "3.2",
656        python_default = False,
657        target_platforms = [
658            "osx_x86_64",
659        ],
660        want = [
661            ":is_cp3.2_py_none_any_osx_x86_64",
662        ],
663    )
664
665_tests.append(_test_py2_py3_none_any)
666
667def _test_py3_none_any(env):
668    _test_config_settings(
669        env,
670        filename = "foo-0.0.1-py3-none-any.whl",
671        want = [":is_py3_none_any"],
672    )
673
674    _test_config_settings(
675        env,
676        filename = "foo-0.0.1-py3-none-any.whl",
677        target_platforms = ["linux_x86_64"],
678        want = [":is_py3_none_any_linux_x86_64"],
679    )
680
681_tests.append(_test_py3_none_any)
682
683def _test_py3_none_macosx_10_9_universal2(env):
684    _test_config_settings(
685        env,
686        filename = "foo-0.0.1-py3-none-macosx_10_9_universal2.whl",
687        osx_versions = [
688            (10, 9),
689            (11, 0),
690        ],
691        want = [],
692        want_versions = {
693            ":is_py3_none_osx_aarch64_universal2": {
694                (10, 9): ":is_py3_none_osx_10_9_aarch64_universal2",
695                (11, 0): ":is_py3_none_osx_11_0_aarch64_universal2",
696            },
697            ":is_py3_none_osx_x86_64_universal2": {
698                (10, 9): ":is_py3_none_osx_10_9_x86_64_universal2",
699                (11, 0): ":is_py3_none_osx_11_0_x86_64_universal2",
700            },
701        },
702    )
703
704_tests.append(_test_py3_none_macosx_10_9_universal2)
705
706def _test_cp37_abi3_linux_x86_64(env):
707    _test_config_settings(
708        env,
709        filename = "foo-0.0.1-cp37-abi3-linux_x86_64.whl",
710        want = [
711            ":is_cp3x_abi3_linux_x86_64",
712        ],
713    )
714
715    _test_config_settings(
716        env,
717        filename = "foo-0.0.1-cp37-abi3-linux_x86_64.whl",
718        python_version = "3.2",
719        python_default = True,
720        want = [
721            ":is_cp3x_abi3_linux_x86_64",
722            ":is_cp3.2_cp3x_abi3_linux_x86_64",
723        ],
724    )
725
726_tests.append(_test_cp37_abi3_linux_x86_64)
727
728def _test_cp37_abi3_windows_x86_64(env):
729    _test_config_settings(
730        env,
731        filename = "foo-0.0.1-cp37-abi3-windows_x86_64.whl",
732        want = [
733            ":is_cp3x_abi3_windows_x86_64",
734        ],
735    )
736
737_tests.append(_test_cp37_abi3_windows_x86_64)
738
739def _test_cp37_abi3_manylinux_2_17_x86_64(env):
740    _test_config_settings(
741        env,
742        filename = "foo-0.0.1-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl",
743        glibc_versions = [
744            (2, 16),
745            (2, 17),
746            (2, 18),
747        ],
748        want = [],
749        want_versions = {
750            ":is_cp3x_abi3_manylinux_x86_64": {
751                (2, 17): ":is_cp3x_abi3_manylinux_2_17_x86_64",
752                (2, 18): ":is_cp3x_abi3_manylinux_2_18_x86_64",
753            },
754        },
755    )
756
757_tests.append(_test_cp37_abi3_manylinux_2_17_x86_64)
758
759def _test_cp37_abi3_manylinux_2_17_musllinux_1_1_aarch64(env):
760    # I've seen such a wheel being built for `uv`
761    _test_config_settings(
762        env,
763        filename = "foo-0.0.1-cp37-cp37-manylinux_2_17_arm64.musllinux_1_1_arm64.whl",
764        glibc_versions = [
765            (2, 16),
766            (2, 17),
767            (2, 18),
768        ],
769        muslc_versions = [
770            (1, 1),
771        ],
772        want = [],
773        want_versions = {
774            ":is_cp3x_cp_manylinux_aarch64": {
775                (2, 17): ":is_cp3x_cp_manylinux_2_17_aarch64",
776                (2, 18): ":is_cp3x_cp_manylinux_2_18_aarch64",
777            },
778            ":is_cp3x_cp_musllinux_aarch64": {
779                (1, 1): ":is_cp3x_cp_musllinux_1_1_aarch64",
780            },
781        },
782    )
783
784_tests.append(_test_cp37_abi3_manylinux_2_17_musllinux_1_1_aarch64)
785
786def _test_multiplatform_whl_aliases_empty(env):
787    # Check that we still work with an empty requirements.txt
788    got = multiplatform_whl_aliases(aliases = [], default_version = None)
789    env.expect.that_collection(got).contains_exactly([])
790
791_tests.append(_test_multiplatform_whl_aliases_empty)
792
793def _test_multiplatform_whl_aliases_nofilename(env):
794    aliases = [
795        whl_alias(
796            repo = "foo",
797            config_setting = "//:label",
798            version = "3.1",
799        ),
800    ]
801    got = multiplatform_whl_aliases(aliases = aliases, default_version = None)
802    env.expect.that_collection(got).contains_exactly(aliases)
803
804_tests.append(_test_multiplatform_whl_aliases_nofilename)
805
806def _test_multiplatform_whl_aliases_filename(env):
807    aliases = [
808        whl_alias(
809            repo = "foo-py3-0.0.3",
810            filename = "foo-0.0.3-py3-none-any.whl",
811            version = "3.2",
812        ),
813        whl_alias(
814            repo = "foo-py3-0.0.1",
815            filename = "foo-0.0.1-py3-none-any.whl",
816            version = "3.1",
817        ),
818        whl_alias(
819            repo = "foo-0.0.2",
820            filename = "foo-0.0.2-py3-none-any.whl",
821            version = "3.1",
822            target_platforms = [
823                "cp31_linux_x86_64",
824                "cp31_linux_aarch64",
825            ],
826        ),
827    ]
828    got = multiplatform_whl_aliases(
829        aliases = aliases,
830        default_version = "3.1",
831        glibc_versions = [],
832        muslc_versions = [],
833        osx_versions = [],
834    )
835    want = [
836        whl_alias(config_setting = "//_config:is_cp3.1_py3_none_any", repo = "foo-py3-0.0.1", version = "3.1"),
837        whl_alias(config_setting = "//_config:is_cp3.1_py3_none_any_linux_aarch64", repo = "foo-0.0.2", version = "3.1"),
838        whl_alias(config_setting = "//_config:is_cp3.1_py3_none_any_linux_x86_64", repo = "foo-0.0.2", version = "3.1"),
839        whl_alias(config_setting = "//_config:is_cp3.2_py3_none_any", repo = "foo-py3-0.0.3", version = "3.2"),
840        whl_alias(config_setting = "//_config:is_py3_none_any", repo = "foo-py3-0.0.1", version = "3.1"),
841        whl_alias(config_setting = "//_config:is_py3_none_any_linux_aarch64", repo = "foo-0.0.2", version = "3.1"),
842        whl_alias(config_setting = "//_config:is_py3_none_any_linux_x86_64", repo = "foo-0.0.2", version = "3.1"),
843    ]
844    env.expect.that_collection(got).contains_exactly(want)
845
846_tests.append(_test_multiplatform_whl_aliases_filename)
847
848def _test_multiplatform_whl_aliases_filename_versioned(env):
849    aliases = [
850        whl_alias(
851            repo = "glibc-2.17",
852            filename = "foo-0.0.1-py3-none-manylinux_2_17_x86_64.whl",
853            version = "3.1",
854        ),
855        whl_alias(
856            repo = "glibc-2.18",
857            filename = "foo-0.0.1-py3-none-manylinux_2_18_x86_64.whl",
858            version = "3.1",
859        ),
860        whl_alias(
861            repo = "musl",
862            filename = "foo-0.0.1-py3-none-musllinux_1_1_x86_64.whl",
863            version = "3.1",
864        ),
865    ]
866    got = multiplatform_whl_aliases(
867        aliases = aliases,
868        default_version = None,
869        glibc_versions = [(2, 17), (2, 18)],
870        muslc_versions = [(1, 1), (1, 2)],
871        osx_versions = [],
872    )
873    want = [
874        whl_alias(config_setting = "//_config:is_cp3.1_py3_none_manylinux_2_17_x86_64", repo = "glibc-2.17", version = "3.1"),
875        whl_alias(config_setting = "//_config:is_cp3.1_py3_none_manylinux_2_18_x86_64", repo = "glibc-2.18", version = "3.1"),
876        whl_alias(config_setting = "//_config:is_cp3.1_py3_none_manylinux_x86_64", repo = "glibc-2.17", version = "3.1"),
877        whl_alias(config_setting = "//_config:is_cp3.1_py3_none_musllinux_1_1_x86_64", repo = "musl", version = "3.1"),
878        whl_alias(config_setting = "//_config:is_cp3.1_py3_none_musllinux_1_2_x86_64", repo = "musl", version = "3.1"),
879        whl_alias(config_setting = "//_config:is_cp3.1_py3_none_musllinux_x86_64", repo = "musl", version = "3.1"),
880    ]
881    env.expect.that_collection(got).contains_exactly(want)
882
883_tests.append(_test_multiplatform_whl_aliases_filename_versioned)
884
885def _test_config_settings_exist(env):
886    for py_tag in ["py2.py3", "py3", "py311", "cp311"]:
887        if py_tag == "py2.py3":
888            abis = ["none"]
889        elif py_tag.startswith("py"):
890            abis = ["none", "abi3"]
891        else:
892            abis = ["none", "abi3", "cp311"]
893
894        for abi_tag in abis:
895            for platform_tag, kwargs in {
896                "any": {},
897                "macosx_11_0_arm64": {
898                    "osx_versions": [(11, 0)],
899                    "target_platforms": ["osx_aarch64"],
900                },
901                "manylinux_2_17_x86_64": {
902                    "glibc_versions": [(2, 17), (2, 18)],
903                    "target_platforms": ["linux_x86_64"],
904                },
905                "manylinux_2_18_x86_64": {
906                    "glibc_versions": [(2, 17), (2, 18)],
907                    "target_platforms": ["linux_x86_64"],
908                },
909                "musllinux_1_1_aarch64": {
910                    "muslc_versions": [(1, 2), (1, 1), (1, 0)],
911                    "target_platforms": ["linux_aarch64"],
912                },
913            }.items():
914                aliases = [
915                    whl_alias(
916                        repo = "repo",
917                        filename = "foo-0.0.1-{}-{}-{}.whl".format(py_tag, abi_tag, platform_tag),
918                        version = "3.11",
919                    ),
920                ]
921                available_config_settings = []
922                mock_rule = lambda name, **kwargs: available_config_settings.append(name)
923                config_settings(
924                    python_versions = ["3.11"],
925                    native = struct(
926                        alias = mock_rule,
927                        config_setting = mock_rule,
928                    ),
929                    **kwargs
930                )
931
932                got_aliases = multiplatform_whl_aliases(
933                    aliases = aliases,
934                    default_version = None,
935                    glibc_versions = kwargs.get("glibc_versions", []),
936                    muslc_versions = kwargs.get("muslc_versions", []),
937                    osx_versions = kwargs.get("osx_versions", []),
938                )
939                got = [a.config_setting.partition(":")[-1] for a in got_aliases]
940
941                env.expect.that_collection(available_config_settings).contains_at_least(got)
942
943_tests.append(_test_config_settings_exist)
944
945def render_pkg_aliases_test_suite(name):
946    """Create the test suite.
947
948    Args:
949        name: the name of the test suite
950    """
951    test_suite(name = name, basic_tests = _tests)
952