Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

106 Zeilen
3.1KB

  1. from pathlib import Path
  2. from contextlib import contextmanager
  3. import json
  4. from http.server import SimpleHTTPRequestHandler, HTTPServer
  5. from typing import NamedTuple
  6. from concurrent.futures import ThreadPoolExecutor
  7. from functools import partial
  8. import tempfile
  9. import sys
  10. import subprocess
  11. import pytest
  12. class DirectoryServingHTTPRequestHandler(SimpleHTTPRequestHandler):
  13. """
  14. A simple HTTP request handler that simply serves files from a directory given to the constructor.
  15. """
  16. def __init__(self, *args, **kwargs) -> None:
  17. self.dir = kwargs.pop('dir')
  18. super().__init__(*args, **kwargs)
  19. def translate_path(self, path) -> str:
  20. # Convert the given URL path to a path relative to the directory we are serving
  21. abspath = Path(super().translate_path(path))
  22. relpath = abspath.relative_to(Path.cwd())
  23. return str(self.dir / relpath)
  24. class ServerInfo(NamedTuple):
  25. """
  26. Information about an HTTP server fixture
  27. """
  28. base_url: str
  29. root: Path
  30. @contextmanager
  31. def run_http_server(dirpath: Path, port: int):
  32. """
  33. Context manager that spawns an HTTP server that serves thegiven directory on
  34. the given TCP port.
  35. """
  36. handler = partial(DirectoryServingHTTPRequestHandler, dir=dirpath)
  37. addr = ('localhost', port)
  38. pool = ThreadPoolExecutor()
  39. with HTTPServer(addr, handler) as httpd:
  40. pool.submit(lambda: httpd.serve_forever(poll_interval=0.1))
  41. try:
  42. yield ServerInfo(f'http://localhost:{port}', dirpath)
  43. finally:
  44. httpd.shutdown()
  45. @pytest.yield_fixture()
  46. def http_tmp_dir_server(tmp_path: Path, unused_tcp_port: int):
  47. """
  48. Creates an HTTP server that serves the contents of a new
  49. temporary directory.
  50. """
  51. with run_http_server(tmp_path, unused_tcp_port) as s:
  52. yield s
  53. class RepoFixture:
  54. """
  55. A fixture handle to a dds HTTP repository, including a path and URL.
  56. """
  57. def __init__(self, dds_exe: Path, info: ServerInfo) -> None:
  58. self.server = info
  59. self.url = info.base_url
  60. self.dds_exe = dds_exe
  61. def import_json_data(self, data) -> None:
  62. """
  63. Import some packages into the repo for the given JSON data. Uses
  64. mkrepo.py
  65. """
  66. with tempfile.NamedTemporaryFile() as f:
  67. f.write(json.dumps(data).encode())
  68. f.flush()
  69. self.import_json_file(Path(f.name))
  70. def import_json_file(self, fpath: Path) -> None:
  71. """
  72. Import some package into the repo for the given JSON file. Uses mkrepo.py
  73. """
  74. subprocess.check_call([
  75. sys.executable,
  76. str(Path.cwd() / 'tools/mkrepo.py'),
  77. f'--dir={self.server.root}',
  78. f'--spec={fpath}',
  79. ])
  80. @pytest.yield_fixture()
  81. def http_repo(dds_exe: Path, http_tmp_dir_server: ServerInfo):
  82. """
  83. Fixture that creates a new empty dds repository and an HTTP server to serve
  84. it.
  85. """
  86. subprocess.check_call([dds_exe, 'repoman', 'init', str(http_tmp_dir_server.root)])
  87. yield RepoFixture(dds_exe, http_tmp_dir_server)