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 15"" 16 17load("@rules_testing//lib:test_suite.bzl", "test_suite") 18load("//python/private/pypi:parse_requirements.bzl", "parse_requirements", "select_requirement") # buildifier: disable=bzl-visibility 19 20def _mock_ctx(): 21 testdata = { 22 "requirements_different_package_version": """\ 23foo==0.0.1+local --hash=sha256:deadbeef 24foo==0.0.1 --hash=sha256:deadb00f 25""", 26 "requirements_direct": """\ 27foo[extra] @ https://some-url 28""", 29 "requirements_extra_args": """\ 30--index-url=example.org 31 32foo[extra]==0.0.1 --hash=sha256:deadbeef 33""", 34 "requirements_linux": """\ 35foo==0.0.3 --hash=sha256:deadbaaf 36""", 37 # download_only = True 38 "requirements_linux_download_only": """\ 39--platform=manylinux_2_17_x86_64 40--python-version=39 41--implementation=cp 42--abi=cp39 43 44foo==0.0.1 --hash=sha256:deadbeef 45bar==0.0.1 --hash=sha256:deadb00f 46""", 47 "requirements_lock": """\ 48foo[extra]==0.0.1 --hash=sha256:deadbeef 49""", 50 "requirements_lock_dupe": """\ 51foo[extra,extra_2]==0.0.1 --hash=sha256:deadbeef 52foo==0.0.1 --hash=sha256:deadbeef 53foo[extra]==0.0.1 --hash=sha256:deadbeef 54""", 55 "requirements_marker": """\ 56foo[extra]==0.0.1 ;marker --hash=sha256:deadbeef 57bar==0.0.1 --hash=sha256:deadbeef 58""", 59 "requirements_osx": """\ 60foo==0.0.3 --hash=sha256:deadbaaf 61""", 62 "requirements_osx_download_only": """\ 63--platform=macosx_10_9_arm64 64--python-version=39 65--implementation=cp 66--abi=cp39 67 68foo==0.0.3 --hash=sha256:deadbaaf 69""", 70 "requirements_windows": """\ 71foo[extra]==0.0.2 --hash=sha256:deadbeef 72bar==0.0.1 --hash=sha256:deadb00f 73""", 74 } 75 76 return struct( 77 os = struct( 78 name = "linux", 79 arch = "x86_64", 80 ), 81 read = lambda x: testdata[x], 82 ) 83 84_tests = [] 85 86def _test_simple(env): 87 got = parse_requirements( 88 ctx = _mock_ctx(), 89 requirements_by_platform = { 90 "requirements_lock": ["linux_x86_64", "windows_x86_64"], 91 }, 92 ) 93 env.expect.that_dict(got).contains_exactly({ 94 "foo": [ 95 struct( 96 distribution = "foo", 97 extra_pip_args = [], 98 requirement_line = "foo[extra]==0.0.1 --hash=sha256:deadbeef", 99 srcs = struct( 100 requirement = "foo[extra]==0.0.1", 101 shas = ["deadbeef"], 102 version = "0.0.1", 103 ), 104 target_platforms = [ 105 "linux_x86_64", 106 "windows_x86_64", 107 ], 108 whls = [], 109 sdist = None, 110 is_exposed = True, 111 ), 112 ], 113 }) 114 env.expect.that_str( 115 select_requirement( 116 got["foo"], 117 platform = "linux_x86_64", 118 ).srcs.version, 119 ).equals("0.0.1") 120 121_tests.append(_test_simple) 122 123def _test_extra_pip_args(env): 124 got = parse_requirements( 125 ctx = _mock_ctx(), 126 requirements_by_platform = { 127 "requirements_extra_args": ["linux_x86_64"], 128 }, 129 extra_pip_args = ["--trusted-host=example.org"], 130 ) 131 env.expect.that_dict(got).contains_exactly({ 132 "foo": [ 133 struct( 134 distribution = "foo", 135 extra_pip_args = ["--index-url=example.org", "--trusted-host=example.org"], 136 requirement_line = "foo[extra]==0.0.1 --hash=sha256:deadbeef", 137 srcs = struct( 138 requirement = "foo[extra]==0.0.1", 139 shas = ["deadbeef"], 140 version = "0.0.1", 141 ), 142 target_platforms = [ 143 "linux_x86_64", 144 ], 145 whls = [], 146 sdist = None, 147 is_exposed = True, 148 ), 149 ], 150 }) 151 env.expect.that_str( 152 select_requirement( 153 got["foo"], 154 platform = "linux_x86_64", 155 ).srcs.version, 156 ).equals("0.0.1") 157 158_tests.append(_test_extra_pip_args) 159 160def _test_dupe_requirements(env): 161 got = parse_requirements( 162 ctx = _mock_ctx(), 163 requirements_by_platform = { 164 "requirements_lock_dupe": ["linux_x86_64"], 165 }, 166 ) 167 env.expect.that_dict(got).contains_exactly({ 168 "foo": [ 169 struct( 170 distribution = "foo", 171 extra_pip_args = [], 172 requirement_line = "foo[extra,extra_2]==0.0.1 --hash=sha256:deadbeef", 173 srcs = struct( 174 requirement = "foo[extra,extra_2]==0.0.1", 175 shas = ["deadbeef"], 176 version = "0.0.1", 177 ), 178 target_platforms = ["linux_x86_64"], 179 whls = [], 180 sdist = None, 181 is_exposed = True, 182 ), 183 ], 184 }) 185 186_tests.append(_test_dupe_requirements) 187 188def _test_multi_os(env): 189 got = parse_requirements( 190 ctx = _mock_ctx(), 191 requirements_by_platform = { 192 "requirements_linux": ["linux_x86_64"], 193 "requirements_windows": ["windows_x86_64"], 194 }, 195 ) 196 197 env.expect.that_dict(got).contains_exactly({ 198 "bar": [ 199 struct( 200 distribution = "bar", 201 extra_pip_args = [], 202 requirement_line = "bar==0.0.1 --hash=sha256:deadb00f", 203 srcs = struct( 204 requirement = "bar==0.0.1", 205 shas = ["deadb00f"], 206 version = "0.0.1", 207 ), 208 target_platforms = ["windows_x86_64"], 209 whls = [], 210 sdist = None, 211 is_exposed = False, 212 ), 213 ], 214 "foo": [ 215 struct( 216 distribution = "foo", 217 extra_pip_args = [], 218 requirement_line = "foo==0.0.3 --hash=sha256:deadbaaf", 219 srcs = struct( 220 requirement = "foo==0.0.3", 221 shas = ["deadbaaf"], 222 version = "0.0.3", 223 ), 224 target_platforms = ["linux_x86_64"], 225 whls = [], 226 sdist = None, 227 is_exposed = True, 228 ), 229 struct( 230 distribution = "foo", 231 extra_pip_args = [], 232 requirement_line = "foo[extra]==0.0.2 --hash=sha256:deadbeef", 233 srcs = struct( 234 requirement = "foo[extra]==0.0.2", 235 shas = ["deadbeef"], 236 version = "0.0.2", 237 ), 238 target_platforms = ["windows_x86_64"], 239 whls = [], 240 sdist = None, 241 is_exposed = True, 242 ), 243 ], 244 }) 245 env.expect.that_str( 246 select_requirement( 247 got["foo"], 248 platform = "windows_x86_64", 249 ).srcs.version, 250 ).equals("0.0.2") 251 252_tests.append(_test_multi_os) 253 254def _test_multi_os_legacy(env): 255 got = parse_requirements( 256 ctx = _mock_ctx(), 257 requirements_by_platform = { 258 "requirements_linux_download_only": ["cp39_linux_x86_64"], 259 "requirements_osx_download_only": ["cp39_osx_aarch64"], 260 }, 261 ) 262 263 env.expect.that_dict(got).contains_exactly({ 264 "bar": [ 265 struct( 266 distribution = "bar", 267 extra_pip_args = ["--platform=manylinux_2_17_x86_64", "--python-version=39", "--implementation=cp", "--abi=cp39"], 268 is_exposed = False, 269 requirement_line = "bar==0.0.1 --hash=sha256:deadb00f", 270 sdist = None, 271 srcs = struct( 272 requirement = "bar==0.0.1", 273 shas = ["deadb00f"], 274 version = "0.0.1", 275 ), 276 target_platforms = ["cp39_linux_x86_64"], 277 whls = [], 278 ), 279 ], 280 "foo": [ 281 struct( 282 distribution = "foo", 283 extra_pip_args = ["--platform=manylinux_2_17_x86_64", "--python-version=39", "--implementation=cp", "--abi=cp39"], 284 is_exposed = True, 285 requirement_line = "foo==0.0.1 --hash=sha256:deadbeef", 286 sdist = None, 287 srcs = struct( 288 requirement = "foo==0.0.1", 289 shas = ["deadbeef"], 290 version = "0.0.1", 291 ), 292 target_platforms = ["cp39_linux_x86_64"], 293 whls = [], 294 ), 295 struct( 296 distribution = "foo", 297 extra_pip_args = ["--platform=macosx_10_9_arm64", "--python-version=39", "--implementation=cp", "--abi=cp39"], 298 is_exposed = True, 299 requirement_line = "foo==0.0.3 --hash=sha256:deadbaaf", 300 sdist = None, 301 srcs = struct( 302 requirement = "foo==0.0.3", 303 shas = ["deadbaaf"], 304 version = "0.0.3", 305 ), 306 target_platforms = ["cp39_osx_aarch64"], 307 whls = [], 308 ), 309 ], 310 }) 311 312_tests.append(_test_multi_os_legacy) 313 314def _test_select_requirement_none_platform(env): 315 got = select_requirement( 316 [ 317 struct( 318 some_attr = "foo", 319 target_platforms = ["linux_x86_64"], 320 ), 321 ], 322 platform = None, 323 ) 324 env.expect.that_str(got.some_attr).equals("foo") 325 326_tests.append(_test_select_requirement_none_platform) 327 328def _test_env_marker_resolution(env): 329 def _mock_eval_markers(_, input): 330 ret = { 331 "foo[extra]==0.0.1 ;marker --hash=sha256:deadbeef": ["cp311_windows_x86_64"], 332 } 333 334 env.expect.that_collection(input.keys()).contains_exactly(ret.keys()) 335 env.expect.that_collection(input.values()[0]).contains_exactly(["cp311_linux_super_exotic", "cp311_windows_x86_64"]) 336 return ret 337 338 got = parse_requirements( 339 ctx = _mock_ctx(), 340 requirements_by_platform = { 341 "requirements_marker": ["cp311_linux_super_exotic", "cp311_windows_x86_64"], 342 }, 343 evaluate_markers = _mock_eval_markers, 344 ) 345 env.expect.that_dict(got).contains_exactly({ 346 "bar": [ 347 struct( 348 distribution = "bar", 349 extra_pip_args = [], 350 is_exposed = True, 351 requirement_line = "bar==0.0.1 --hash=sha256:deadbeef", 352 sdist = None, 353 srcs = struct( 354 requirement = "bar==0.0.1", 355 shas = ["deadbeef"], 356 version = "0.0.1", 357 ), 358 target_platforms = ["cp311_linux_super_exotic", "cp311_windows_x86_64"], 359 whls = [], 360 ), 361 ], 362 "foo": [ 363 struct( 364 distribution = "foo", 365 extra_pip_args = [], 366 # This is not exposed because we also have `linux_super_exotic` in the platform list 367 is_exposed = False, 368 requirement_line = "foo[extra]==0.0.1 ;marker --hash=sha256:deadbeef", 369 sdist = None, 370 srcs = struct( 371 requirement = "foo[extra]==0.0.1 ;marker", 372 shas = ["deadbeef"], 373 version = "0.0.1", 374 ), 375 target_platforms = ["cp311_windows_x86_64"], 376 whls = [], 377 ), 378 ], 379 }) 380 env.expect.that_str( 381 select_requirement( 382 got["foo"], 383 platform = "windows_x86_64", 384 ).srcs.version, 385 ).equals("0.0.1") 386 387_tests.append(_test_env_marker_resolution) 388 389def _test_different_package_version(env): 390 got = parse_requirements( 391 ctx = _mock_ctx(), 392 requirements_by_platform = { 393 "requirements_different_package_version": ["linux_x86_64"], 394 }, 395 ) 396 env.expect.that_dict(got).contains_exactly({ 397 "foo": [ 398 struct( 399 distribution = "foo", 400 extra_pip_args = [], 401 requirement_line = "foo==0.0.1 --hash=sha256:deadb00f", 402 srcs = struct( 403 requirement = "foo==0.0.1", 404 shas = ["deadb00f"], 405 version = "0.0.1", 406 ), 407 target_platforms = ["linux_x86_64"], 408 whls = [], 409 sdist = None, 410 is_exposed = True, 411 ), 412 struct( 413 distribution = "foo", 414 extra_pip_args = [], 415 requirement_line = "foo==0.0.1+local --hash=sha256:deadbeef", 416 srcs = struct( 417 requirement = "foo==0.0.1+local", 418 shas = ["deadbeef"], 419 version = "0.0.1+local", 420 ), 421 target_platforms = ["linux_x86_64"], 422 whls = [], 423 sdist = None, 424 is_exposed = True, 425 ), 426 ], 427 }) 428 429_tests.append(_test_different_package_version) 430 431def parse_requirements_test_suite(name): 432 """Create the test suite. 433 434 Args: 435 name: the name of the test suite 436 """ 437 test_suite(name = name, basic_tests = _tests) 438