xref: /aosp_15_r20/external/bazelbuild-rules_python/tests/py_wheel/py_wheel_tests.bzl (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
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"""Test for py_wheel."""
15
16load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite")
17load("@rules_testing//lib:truth.bzl", "matching")
18load("@rules_testing//lib:util.bzl", rt_util = "util")
19load("//python:packaging.bzl", "py_wheel")
20load("//python/private:py_wheel_normalize_pep440.bzl", "normalize_pep440")  # buildifier: disable=bzl-visibility
21
22_basic_tests = []
23_tests = []
24
25def _test_metadata(name):
26    rt_util.helper_target(
27        py_wheel,
28        name = name + "_subject",
29        distribution = "mydist_" + name,
30        version = "0.0.0",
31    )
32    analysis_test(
33        name = name,
34        impl = _test_metadata_impl,
35        target = name + "_subject",
36    )
37
38def _test_metadata_impl(env, target):
39    action = env.expect.that_target(target).action_generating(
40        "{package}/{name}.metadata.txt",
41    )
42    action.content().split("\n").contains_exactly([
43        env.expect.meta.format_str("Name: mydist_{test_name}"),
44        "Metadata-Version: 2.1",
45        "",
46    ])
47
48_tests.append(_test_metadata)
49
50def _test_data(name):
51    rt_util.helper_target(
52        py_wheel,
53        name = name + "_data",
54        distribution = "mydist_" + name,
55        version = "0.0.0",
56        data_files = {
57            "source_name": "scripts/wheel_name",
58        },
59    )
60    analysis_test(
61        name = name,
62        impl = _test_data_impl,
63        target = name + "_data",
64    )
65
66def _test_data_impl(env, target):
67    action = env.expect.that_target(target).action_named(
68        "PyWheel",
69    )
70    action.contains_at_least_args(["--data_files", "scripts/wheel_name;tests/py_wheel/source_name"])
71    action.contains_at_least_inputs(["tests/py_wheel/source_name"])
72
73_tests.append(_test_data)
74
75def _test_data_bad_path(name):
76    rt_util.helper_target(
77        py_wheel,
78        name = name + "_data",
79        distribution = "mydist_" + name,
80        version = "0.0.0",
81        data_files = {
82            "source_name": "unsupported_path/wheel_name",
83        },
84    )
85    analysis_test(
86        name = name,
87        impl = _test_data_bad_path_impl,
88        target = name + "_data",
89        expect_failure = True,
90    )
91
92def _test_data_bad_path_impl(env, target):
93    env.expect.that_target(target).failures().contains_predicate(
94        matching.str_matches("target data file must start with"),
95    )
96
97_tests.append(_test_data_bad_path)
98
99def _test_data_bad_path_but_right_prefix(name):
100    rt_util.helper_target(
101        py_wheel,
102        name = name + "_data",
103        distribution = "mydist_" + name,
104        version = "0.0.0",
105        data_files = {
106            "source_name": "scripts2/wheel_name",
107        },
108    )
109    analysis_test(
110        name = name,
111        impl = _test_data_bad_path_but_right_prefix_impl,
112        target = name + "_data",
113        expect_failure = True,
114    )
115
116def _test_data_bad_path_but_right_prefix_impl(env, target):
117    env.expect.that_target(target).failures().contains_predicate(
118        matching.str_matches("target data file must start with"),
119    )
120
121_tests.append(_test_data_bad_path_but_right_prefix)
122
123def _test_content_type_from_attr(name):
124    rt_util.helper_target(
125        py_wheel,
126        name = name + "_subject",
127        distribution = "mydist_" + name,
128        version = "0.0.0",
129        description_content_type = "text/x-rst",
130    )
131    analysis_test(
132        name = name,
133        impl = _test_content_type_from_attr_impl,
134        target = name + "_subject",
135    )
136
137def _test_content_type_from_attr_impl(env, target):
138    action = env.expect.that_target(target).action_generating(
139        "{package}/{name}.metadata.txt",
140    )
141    action.content().split("\n").contains(
142        "Description-Content-Type: text/x-rst",
143    )
144
145_tests.append(_test_content_type_from_attr)
146
147def _test_content_type_from_description(name):
148    rt_util.helper_target(
149        py_wheel,
150        name = name + "_subject",
151        distribution = "mydist_" + name,
152        version = "0.0.0",
153        description_file = "desc.md",
154    )
155    analysis_test(
156        name = name,
157        impl = _test_content_type_from_description_impl,
158        target = name + "_subject",
159    )
160
161def _test_content_type_from_description_impl(env, target):
162    action = env.expect.that_target(target).action_generating(
163        "{package}/{name}.metadata.txt",
164    )
165    action.content().split("\n").contains(
166        "Description-Content-Type: text/markdown",
167    )
168
169_tests.append(_test_content_type_from_description)
170
171def _test_pep440_normalization(env):
172    prefixes = ["v", "  v", " \t\r\nv"]
173    epochs = {
174        "": ["", "0!", "00!"],
175        "1!": ["1!", "001!"],
176        "200!": ["200!", "00200!"],
177    }
178    releases = {
179        "0.1": ["0.1", "0.01"],
180        "2023.7.19": ["2023.7.19", "2023.07.19"],
181    }
182    pres = {
183        "": [""],
184        "a0": ["a", ".a", "-ALPHA0", "_alpha0", ".a0"],
185        "a4": ["alpha4", ".a04"],
186        "b0": ["b", ".b", "-BETA0", "_beta0", ".b0"],
187        "b5": ["beta05", ".b5"],
188        "rc0": ["C", "_c0", "RC", "_rc0", "-preview_0"],
189    }
190    explicit_posts = {
191        "": [""],
192        ".post0": [],
193        ".post1": [".post1", "-r1", "_rev1"],
194    }
195    implicit_posts = [[".post1", "-1"], [".post2", "-2"]]
196    devs = {
197        "": [""],
198        ".dev0": ["dev", "-DEV", "_Dev-0"],
199        ".dev9": ["DEV9", ".dev09", ".dev9"],
200        ".dev{BUILD_TIMESTAMP}": [
201            "-DEV{BUILD_TIMESTAMP}",
202            "_dev_{BUILD_TIMESTAMP}",
203        ],
204    }
205    locals = {
206        "": [""],
207        "+ubuntu.7": ["+Ubuntu_7", "+ubuntu-007"],
208        "+ubuntu.r007": ["+Ubuntu_R007"],
209    }
210    epochs = [
211        [normalized_epoch, input_epoch]
212        for normalized_epoch, input_epochs in epochs.items()
213        for input_epoch in input_epochs
214    ]
215    releases = [
216        [normalized_release, input_release]
217        for normalized_release, input_releases in releases.items()
218        for input_release in input_releases
219    ]
220    pres = [
221        [normalized_pre, input_pre]
222        for normalized_pre, input_pres in pres.items()
223        for input_pre in input_pres
224    ]
225    explicit_posts = [
226        [normalized_post, input_post]
227        for normalized_post, input_posts in explicit_posts.items()
228        for input_post in input_posts
229    ]
230    pres_and_posts = [
231        [normalized_pre + normalized_post, input_pre + input_post]
232        for normalized_pre, input_pre in pres
233        for normalized_post, input_post in explicit_posts
234    ] + [
235        [normalized_pre + normalized_post, input_pre + input_post]
236        for normalized_pre, input_pre in pres
237        for normalized_post, input_post in implicit_posts
238        if input_pre == "" or input_pre[-1].isdigit()
239    ]
240    devs = [
241        [normalized_dev, input_dev]
242        for normalized_dev, input_devs in devs.items()
243        for input_dev in input_devs
244    ]
245    locals = [
246        [normalized_local, input_local]
247        for normalized_local, input_locals in locals.items()
248        for input_local in input_locals
249    ]
250    postfixes = ["", "  ", " \t\r\n"]
251    i = 0
252    for nepoch, iepoch in epochs:
253        for nrelease, irelease in releases:
254            for nprepost, iprepost in pres_and_posts:
255                for ndev, idev in devs:
256                    for nlocal, ilocal in locals:
257                        prefix = prefixes[i % len(prefixes)]
258                        postfix = postfixes[(i // len(prefixes)) % len(postfixes)]
259                        env.expect.that_str(
260                            normalize_pep440(
261                                prefix + iepoch + irelease + iprepost +
262                                idev + ilocal + postfix,
263                            ),
264                        ).equals(
265                            nepoch + nrelease + nprepost + ndev + nlocal,
266                        )
267                        i += 1
268
269_basic_tests.append(_test_pep440_normalization)
270
271def py_wheel_test_suite(name):
272    test_suite(
273        name = name,
274        basic_tests = _basic_tests,
275        tests = _tests,
276    )
277