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"" 16 17load("@rules_testing//lib:test_suite.bzl", "test_suite") 18load("@rules_testing//lib:truth.bzl", "subjects") 19load("//python/private/pypi:parse_simpleapi_html.bzl", "parse_simpleapi_html") # buildifier: disable=bzl-visibility 20 21_tests = [] 22 23def _generate_html(*items): 24 return """\ 25<html> 26 <head> 27 <meta name="pypi:repository-version" content="1.1"> 28 <title>Links for foo</title> 29 </head> 30 <body> 31 <h1>Links for cengal</h1> 32{} 33</body> 34</html> 35""".format( 36 "\n".join([ 37 "<a {}>{}</a><br />".format( 38 " ".join(item.attrs), 39 item.filename, 40 ) 41 for item in items 42 ]), 43 ) 44 45def _test_sdist(env): 46 # buildifier: disable=unsorted-dict-items 47 tests = [ 48 ( 49 struct( 50 attrs = [ 51 'href="https://example.org/full-url/foo-0.0.1.tar.gz#sha256=deadbeefasource"', 52 'data-requires-python=">=3.7"', 53 ], 54 filename = "foo-0.0.1.tar.gz", 55 url = "ignored", 56 ), 57 struct( 58 filename = "foo-0.0.1.tar.gz", 59 sha256 = "deadbeefasource", 60 url = "https://example.org/full-url/foo-0.0.1.tar.gz", 61 yanked = False, 62 ), 63 ), 64 ( 65 struct( 66 attrs = [ 67 'href="https://example.org/full-url/foo-0.0.1.tar.gz#sha256=deadbeefasource"', 68 'data-requires-python=">=3.7"', 69 ], 70 filename = "foo-0.0.1.tar.gz", 71 url = "ignored", 72 ), 73 struct( 74 filename = "foo-0.0.1.tar.gz", 75 sha256 = "deadbeefasource", 76 url = "https://example.org/full-url/foo-0.0.1.tar.gz", 77 yanked = False, 78 ), 79 ), 80 ] 81 82 for (input, want) in tests: 83 html = _generate_html(input) 84 got = parse_simpleapi_html(url = input.url, content = html) 85 env.expect.that_collection(got.sdists).has_size(1) 86 env.expect.that_collection(got.whls).has_size(0) 87 if not got: 88 fail("expected at least one element, but did not get anything from:\n{}".format(html)) 89 90 actual = env.expect.that_struct( 91 got.sdists[want.sha256], 92 attrs = dict( 93 filename = subjects.str, 94 sha256 = subjects.str, 95 url = subjects.str, 96 yanked = subjects.bool, 97 ), 98 ) 99 actual.filename().equals(want.filename) 100 actual.sha256().equals(want.sha256) 101 actual.url().equals(want.url) 102 actual.yanked().equals(want.yanked) 103 104_tests.append(_test_sdist) 105 106def _test_whls(env): 107 # buildifier: disable=unsorted-dict-items 108 tests = [ 109 ( 110 struct( 111 attrs = [ 112 'href="https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=deadbeef"', 113 'data-requires-python=">=3.7"', 114 'data-dist-info-metadata="sha256=deadb00f"', 115 'data-core-metadata="sha256=deadb00f"', 116 ], 117 filename = "foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 118 url = "ignored", 119 ), 120 struct( 121 filename = "foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 122 metadata_sha256 = "deadb00f", 123 metadata_url = "https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata", 124 sha256 = "deadbeef", 125 url = "https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 126 yanked = False, 127 ), 128 ), 129 ( 130 struct( 131 attrs = [ 132 'href="https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=deadbeef"', 133 'data-requires-python=">=3.7"', 134 'data-dist-info-metadata="sha256=deadb00f"', 135 'data-core-metadata="sha256=deadb00f"', 136 ], 137 filename = "foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 138 url = "ignored", 139 ), 140 struct( 141 filename = "foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 142 metadata_sha256 = "deadb00f", 143 metadata_url = "https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata", 144 sha256 = "deadbeef", 145 url = "https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 146 yanked = False, 147 ), 148 ), 149 ( 150 struct( 151 attrs = [ 152 'href="https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=deadbeef"', 153 'data-requires-python=">=3.7"', 154 'data-core-metadata="sha256=deadb00f"', 155 ], 156 filename = "foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 157 url = "ignored", 158 ), 159 struct( 160 filename = "foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 161 metadata_sha256 = "deadb00f", 162 metadata_url = "https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata", 163 sha256 = "deadbeef", 164 url = "https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 165 yanked = False, 166 ), 167 ), 168 ( 169 struct( 170 attrs = [ 171 'href="https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=deadbeef"', 172 'data-requires-python=">=3.7"', 173 'data-dist-info-metadata="sha256=deadb00f"', 174 ], 175 filename = "foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 176 url = "ignored", 177 ), 178 struct( 179 filename = "foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 180 metadata_sha256 = "deadb00f", 181 metadata_url = "https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata", 182 sha256 = "deadbeef", 183 url = "https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 184 yanked = False, 185 ), 186 ), 187 ( 188 struct( 189 attrs = [ 190 'href="https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=deadbeef"', 191 'data-requires-python=">=3.7"', 192 ], 193 filename = "foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 194 url = "ignored", 195 ), 196 struct( 197 filename = "foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 198 metadata_sha256 = "", 199 metadata_url = "", 200 sha256 = "deadbeef", 201 url = "https://example.org/full-url/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 202 yanked = False, 203 ), 204 ), 205 ( 206 struct( 207 attrs = [ 208 'href="../../foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=deadbeef"', 209 'data-requires-python=">=3.7"', 210 'data-dist-info-metadata="sha256=deadb00f"', 211 ], 212 filename = "foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 213 url = "https://example.org/python-wheels/bar/foo/", 214 ), 215 struct( 216 filename = "foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 217 metadata_sha256 = "deadb00f", 218 metadata_url = "https://example.org/python-wheels/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata", 219 sha256 = "deadbeef", 220 url = "https://example.org/python-wheels/foo-0.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 221 yanked = False, 222 ), 223 ), 224 ( 225 struct( 226 attrs = [ 227 'href="/whl/torch-2.0.0-cp38-cp38-manylinux2014_aarch64.whl#sha256=deadbeef"', 228 ], 229 filename = "torch-2.0.0-cp38-cp38-manylinux2014_aarch64.whl", 230 url = "https://download.pytorch.org/whl/cpu/torch", 231 ), 232 struct( 233 filename = "torch-2.0.0-cp38-cp38-manylinux2014_aarch64.whl", 234 metadata_sha256 = "", 235 metadata_url = "", 236 sha256 = "deadbeef", 237 url = "https://download.pytorch.org/whl/torch-2.0.0-cp38-cp38-manylinux2014_aarch64.whl", 238 yanked = False, 239 ), 240 ), 241 ( 242 struct( 243 attrs = [ 244 'href="/whl/torch-2.0.0-cp38-cp38-manylinux2014_aarch64.whl#sha256=notdeadbeef"', 245 ], 246 filename = "torch-2.0.0-cp38-cp38-manylinux2014_aarch64.whl", 247 url = "http://download.pytorch.org/whl/cpu/torch", 248 ), 249 struct( 250 filename = "torch-2.0.0-cp38-cp38-manylinux2014_aarch64.whl", 251 metadata_sha256 = "", 252 metadata_url = "", 253 sha256 = "notdeadbeef", 254 url = "http://download.pytorch.org/whl/torch-2.0.0-cp38-cp38-manylinux2014_aarch64.whl", 255 yanked = False, 256 ), 257 ), 258 ( 259 struct( 260 attrs = [ 261 'href="1.0.0/mypy_extensions-1.0.0-py3-none-any.whl#sha256=deadbeef"', 262 ], 263 filename = "mypy_extensions-1.0.0-py3-none-any.whl", 264 url = "https://example.org/simple/mypy_extensions", 265 ), 266 struct( 267 filename = "mypy_extensions-1.0.0-py3-none-any.whl", 268 metadata_sha256 = "", 269 metadata_url = "", 270 sha256 = "deadbeef", 271 url = "https://example.org/simple/mypy_extensions/1.0.0/mypy_extensions-1.0.0-py3-none-any.whl", 272 yanked = False, 273 ), 274 ), 275 ( 276 struct( 277 attrs = [ 278 'href="unknown://example.com/mypy_extensions-1.0.0-py3-none-any.whl#sha256=deadbeef"', 279 ], 280 filename = "mypy_extensions-1.0.0-py3-none-any.whl", 281 url = "https://example.org/simple/mypy_extensions", 282 ), 283 struct( 284 filename = "mypy_extensions-1.0.0-py3-none-any.whl", 285 metadata_sha256 = "", 286 metadata_url = "", 287 sha256 = "deadbeef", 288 url = "https://example.org/simple/mypy_extensions/unknown://example.com/mypy_extensions-1.0.0-py3-none-any.whl", 289 yanked = False, 290 ), 291 ), 292 ] 293 294 for (input, want) in tests: 295 html = _generate_html(input) 296 got = parse_simpleapi_html(url = input.url, content = html) 297 env.expect.that_collection(got.sdists).has_size(0) 298 env.expect.that_collection(got.whls).has_size(1) 299 if not got: 300 fail("expected at least one element, but did not get anything from:\n{}".format(html)) 301 302 actual = env.expect.that_struct( 303 got.whls[want.sha256], 304 attrs = dict( 305 filename = subjects.str, 306 metadata_sha256 = subjects.str, 307 metadata_url = subjects.str, 308 sha256 = subjects.str, 309 url = subjects.str, 310 yanked = subjects.bool, 311 ), 312 ) 313 actual.filename().equals(want.filename) 314 actual.metadata_sha256().equals(want.metadata_sha256) 315 actual.metadata_url().equals(want.metadata_url) 316 actual.sha256().equals(want.sha256) 317 actual.url().equals(want.url) 318 actual.yanked().equals(want.yanked) 319 320_tests.append(_test_whls) 321 322def parse_simpleapi_html_test_suite(name): 323 """Create the test suite. 324 325 Args: 326 name: the name of the test suite 327 """ 328 test_suite(name = name, basic_tests = _tests) 329