Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

110 lines
3.5KB

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