1# Copyright 2024 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"""Tests for construction of Python version matching config settings."""
15
16load("@//python:versions.bzl", "MINOR_MAPPING")
17load("@rules_testing//lib:analysis_test.bzl", "analysis_test")
18load("@rules_testing//lib:test_suite.bzl", "test_suite")
19load("@rules_testing//lib:truth.bzl", "subjects")
20load("@rules_testing//lib:util.bzl", rt_util = "util")
21
22_tests = []
23
24def _subject_impl(ctx):
25    _ = ctx  # @unused
26    return [DefaultInfo()]
27
28_subject = rule(
29    implementation = _subject_impl,
30    attrs = {
31        "match_cpu": attr.string(),
32        "match_micro": attr.string(),
33        "match_minor": attr.string(),
34        "match_os": attr.string(),
35        "match_os_cpu": attr.string(),
36        "no_match": attr.string(),
37        "no_match_micro": attr.string(),
38    },
39)
40
41def _test_minor_version_matching(name):
42    minor_matches = {
43        # Having it here ensures that we can mix and match config settings defined in
44        # the repo and elsewhere
45        str(Label("//python/config_settings:is_python_3.11")): "matched-3.11",
46        "//conditions:default": "matched-default",
47    }
48    minor_cpu_matches = {
49        str(Label(":is_python_3.11_aarch64")): "matched-3.11-aarch64",
50        str(Label(":is_python_3.11_ppc")): "matched-3.11-ppc",
51        str(Label(":is_python_3.11_s390x")): "matched-3.11-s390x",
52        str(Label(":is_python_3.11_x86_64")): "matched-3.11-x86_64",
53    }
54    minor_os_matches = {
55        str(Label(":is_python_3.11_linux")): "matched-3.11-linux",
56        str(Label(":is_python_3.11_osx")): "matched-3.11-osx",
57        str(Label(":is_python_3.11_windows")): "matched-3.11-windows",
58    }
59    minor_os_cpu_matches = {
60        str(Label(":is_python_3.11_linux_aarch64")): "matched-3.11-linux-aarch64",
61        str(Label(":is_python_3.11_linux_ppc")): "matched-3.11-linux-ppc",
62        str(Label(":is_python_3.11_linux_s390x")): "matched-3.11-linux-s390x",
63        str(Label(":is_python_3.11_linux_x86_64")): "matched-3.11-linux-x86_64",
64        str(Label(":is_python_3.11_osx_aarch64")): "matched-3.11-osx-aarch64",
65        str(Label(":is_python_3.11_osx_x86_64")): "matched-3.11-osx-x86_64",
66        str(Label(":is_python_3.11_windows_x86_64")): "matched-3.11-windows-x86_64",
67    }
68
69    rt_util.helper_target(
70        _subject,
71        name = name + "_subject",
72        match_minor = select(minor_matches),
73        match_cpu = select(minor_matches | minor_cpu_matches),
74        match_os = select(minor_matches | minor_os_matches),
75        match_os_cpu = select(minor_matches | minor_cpu_matches | minor_os_matches | minor_os_cpu_matches),
76        no_match = select({
77            "//python/config_settings:is_python_3.12": "matched-3.12",
78            "//conditions:default": "matched-default",
79        }),
80    )
81
82    analysis_test(
83        name = name,
84        target = name + "_subject",
85        impl = _test_minor_version_matching_impl,
86        config_settings = {
87            str(Label("//python/config_settings:python_version")): "3.11.1",
88            "//command_line_option:platforms": str(Label("//tests/config_settings:linux_aarch64")),
89        },
90    )
91
92def _test_minor_version_matching_impl(env, target):
93    target = env.expect.that_target(target)
94    target.attr("match_cpu", factory = subjects.str).equals(
95        "matched-3.11-aarch64",
96    )
97    target.attr("match_minor", factory = subjects.str).equals(
98        "matched-3.11",
99    )
100    target.attr("match_os", factory = subjects.str).equals(
101        "matched-3.11-linux",
102    )
103    target.attr("match_os_cpu", factory = subjects.str).equals(
104        "matched-3.11-linux-aarch64",
105    )
106    target.attr("no_match", factory = subjects.str).equals(
107        "matched-default",
108    )
109
110_tests.append(_test_minor_version_matching)
111
112def _test_latest_micro_version_matching(name):
113    rt_util.helper_target(
114        _subject,
115        name = name + "_subject",
116        match_minor = select({
117            "//python/config_settings:is_python_3.12": "matched-3.12",
118            "//conditions:default": "matched-default",
119        }),
120        match_micro = select({
121            "//python/config_settings:is_python_" + MINOR_MAPPING["3.12"]: "matched-3.12",
122            "//conditions:default": "matched-default",
123        }),
124        no_match_micro = select({
125            "//python/config_settings:is_python_3.12.0": "matched-3.12",
126            "//conditions:default": "matched-default",
127        }),
128        no_match = select({
129            "//python/config_settings:is_python_" + MINOR_MAPPING["3.11"]: "matched-3.11",
130            "//conditions:default": "matched-default",
131        }),
132    )
133
134    analysis_test(
135        name = name,
136        target = name + "_subject",
137        impl = _test_latest_micro_version_matching_impl,
138        config_settings = {
139            str(Label("//python/config_settings:python_version")): "3.12",
140        },
141    )
142
143def _test_latest_micro_version_matching_impl(env, target):
144    target = env.expect.that_target(target)
145    target.attr("match_minor", factory = subjects.str).equals(
146        "matched-3.12",
147    )
148    target.attr("match_micro", factory = subjects.str).equals(
149        "matched-3.12",
150    )
151    target.attr("no_match_micro", factory = subjects.str).equals(
152        "matched-default",
153    )
154    target.attr("no_match", factory = subjects.str).equals(
155        "matched-default",
156    )
157
158_tests.append(_test_latest_micro_version_matching)
159
160def construct_config_settings_test_suite(name):  # buildifier: disable=function-docstring
161    # We have CI runners running on a great deal of the platforms from the list below,
162    # hence use all of them within tests.
163    for os in ["linux", "osx", "windows"]:
164        native.config_setting(
165            name = "is_python_3.11_" + os,
166            constraint_values = [
167                "@platforms//os:" + os,
168            ],
169            flag_values = {
170                "//python/config_settings:_python_version_major_minor": "3.11",
171            },
172        )
173
174    for cpu in ["s390x", "ppc", "x86_64", "aarch64"]:
175        native.config_setting(
176            name = "is_python_3.11_" + cpu,
177            constraint_values = [
178                "@platforms//cpu:" + cpu,
179            ],
180            flag_values = {
181                "//python/config_settings:_python_version_major_minor": "3.11",
182            },
183        )
184
185    for (os, cpu) in [
186        ("linux", "aarch64"),
187        ("linux", "ppc"),
188        ("linux", "s390x"),
189        ("linux", "x86_64"),
190        ("osx", "aarch64"),
191        ("osx", "x86_64"),
192        ("windows", "x86_64"),
193    ]:
194        native.config_setting(
195            name = "is_python_3.11_{}_{}".format(os, cpu),
196            constraint_values = [
197                "@platforms//cpu:" + cpu,
198                "@platforms//os:" + os,
199            ],
200            flag_values = {
201                "//python/config_settings:_python_version_major_minor": "3.11",
202            },
203        )
204
205    test_suite(
206        name = name,
207        tests = _tests,
208    )
209
210    native.platform(
211        name = "linux_aarch64",
212        constraint_values = [
213            "@platforms//os:linux",
214            "@platforms//cpu:aarch64",
215        ],
216    )
217