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