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ů.

174 lines
5.1KB

  1. import os
  2. import itertools
  3. from contextlib import contextmanager, ExitStack
  4. from pathlib import Path
  5. from typing import Iterable, Union, Any, Dict, NamedTuple, ContextManager
  6. import subprocess
  7. import shutil
  8. import pytest
  9. from dds_ci import proc
  10. from . import fileutil
  11. class DDS:
  12. def __init__(self, dds_exe: Path, test_dir: Path, project_dir: Path,
  13. scope: ExitStack) -> None:
  14. self.dds_exe = dds_exe
  15. self.test_dir = test_dir
  16. self.source_root = project_dir
  17. self.scratch_dir = project_dir / '_test_scratch'
  18. self.scope = scope
  19. self.scope.callback(self.cleanup)
  20. @property
  21. def repo_dir(self) -> Path:
  22. return self.scratch_dir / 'repo'
  23. @property
  24. def deps_build_dir(self) -> Path:
  25. return self.scratch_dir / 'deps-build'
  26. @property
  27. def build_dir(self) -> Path:
  28. return self.scratch_dir / 'build'
  29. @property
  30. def lmi_path(self) -> Path:
  31. return self.scratch_dir / 'INDEX.lmi'
  32. def cleanup(self):
  33. if self.scratch_dir.exists():
  34. shutil.rmtree(self.scratch_dir)
  35. def run_unchecked(self, cmd: proc.CommandLine, *,
  36. cwd: Path = None) -> subprocess.CompletedProcess:
  37. full_cmd = itertools.chain([self.dds_exe], cmd)
  38. return proc.run(full_cmd, cwd=cwd or self.source_root)
  39. def run(self, cmd: proc.CommandLine, *,
  40. cwd: Path = None) -> subprocess.CompletedProcess:
  41. cmdline = list(proc.flatten_cmd(cmd))
  42. res = self.run_unchecked(cmd)
  43. if res.returncode != 0:
  44. raise subprocess.CalledProcessError(
  45. res.returncode, [self.dds_exe] + cmdline, res.stdout)
  46. return res
  47. @property
  48. def repo_dir_arg(self) -> str:
  49. return f'--repo-dir={self.repo_dir}'
  50. @property
  51. def project_dir_arg(self) -> str:
  52. return f'--project-dir={self.source_root}'
  53. def deps_ls(self) -> subprocess.CompletedProcess:
  54. return self.run(['deps', 'ls'])
  55. def deps_get(self) -> subprocess.CompletedProcess:
  56. return self.run([
  57. 'deps',
  58. 'get',
  59. self.repo_dir_arg,
  60. ])
  61. def deps_build(self, *,
  62. toolchain: str = None) -> subprocess.CompletedProcess:
  63. return self.run([
  64. 'deps',
  65. 'build',
  66. f'--toolchain={toolchain or self.default_builtin_toolchain}',
  67. self.repo_dir_arg,
  68. f'--deps-build-dir={self.deps_build_dir}',
  69. f'--lmi-path={self.lmi_path}',
  70. ])
  71. def build(self,
  72. *,
  73. toolchain: str = None,
  74. apps: bool = True,
  75. warnings: bool = True,
  76. tests: bool = True,
  77. export: bool = False) -> subprocess.CompletedProcess:
  78. return self.run([
  79. 'build',
  80. f'--out={self.build_dir}',
  81. ['--tests'] if tests else [],
  82. ['--apps'] if apps else [],
  83. ['--warnings'] if warnings else [],
  84. ['--export'] if export else [],
  85. f'--toolchain={toolchain or self.default_builtin_toolchain}',
  86. f'--lm-index={self.lmi_path}',
  87. self.project_dir_arg,
  88. ])
  89. def sdist_create(self) -> subprocess.CompletedProcess:
  90. return self.run([
  91. 'sdist',
  92. 'create',
  93. self.project_dir_arg,
  94. f'--out={self.build_dir / "created-sdist.sds"}',
  95. ])
  96. def sdist_export(self) -> subprocess.CompletedProcess:
  97. return self.run([
  98. 'sdist',
  99. 'export',
  100. self.project_dir_arg,
  101. self.repo_dir_arg,
  102. ])
  103. @property
  104. def default_builtin_toolchain(self) -> str:
  105. if os.name == 'posix':
  106. return ':gcc-8'
  107. elif os.name == 'nt':
  108. return ':msvc'
  109. else:
  110. raise RuntimeError(
  111. f'No default builtin toolchain defined for tests on platform "{os.name}"'
  112. )
  113. @property
  114. def exe_suffix(self) -> str:
  115. if os.name == 'posix':
  116. return ''
  117. elif os.name == 'nt':
  118. return '.exe'
  119. else:
  120. raise RuntimeError(
  121. f'We don\'t know the executable suffix for the platform "{os.name}"'
  122. )
  123. def set_contents(self, path: Union[str, Path],
  124. content: bytes) -> ContextManager[Path]:
  125. return fileutil.set_contents(self.source_root / path, content)
  126. @contextmanager
  127. def scoped_dds(test_dir: Path, project_dir: Path, name: str):
  128. dds_exe = Path(__file__).absolute().parent.parent / '_build/dds'
  129. if os.name == 'nt':
  130. dds_exe = dds_exe.with_suffix('.exe')
  131. with ExitStack() as scope:
  132. yield DDS(dds_exe, test_dir, project_dir, scope)
  133. class DDSFixtureParams(NamedTuple):
  134. ident: str
  135. subdir: Union[Path, str]
  136. def dds_fixture_conf(*argsets: DDSFixtureParams):
  137. args = list(argsets)
  138. return pytest.mark.parametrize(
  139. 'dds', args, indirect=True, ids=[p.ident for p in args])
  140. def dds_fixture_conf_1(subdir: Union[Path, str]):
  141. params = DDSFixtureParams(ident='only', subdir=subdir)
  142. return pytest.mark.parametrize('dds', [params], indirect=True, ids=['.'])