Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import os
  2. import itertools
  3. from contextlib import contextmanager, ExitStack
  4. from pathlib import Path
  5. from typing import Union, NamedTuple, ContextManager, Optional, Iterator, TypeVar
  6. import shutil
  7. import pytest
  8. import _pytest
  9. from dds_ci import proc, toolchain as tc_mod
  10. from . import fileutil
  11. T = TypeVar('T')
  12. class DDS:
  13. def __init__(self, dds_exe: Path, test_dir: Path, project_dir: Path, 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 catalog_path(self) -> Path:
  25. return self.scratch_dir / 'catalog.db'
  26. @property
  27. def deps_build_dir(self) -> Path:
  28. return self.scratch_dir / 'deps-build'
  29. @property
  30. def build_dir(self) -> Path:
  31. return self.scratch_dir / 'build'
  32. @property
  33. def lmi_path(self) -> Path:
  34. return self.scratch_dir / 'INDEX.lmi'
  35. def cleanup(self) -> None:
  36. if self.scratch_dir.exists():
  37. shutil.rmtree(self.scratch_dir)
  38. def run_unchecked(self, cmd: proc.CommandLine, *, cwd: Optional[Path] = None) -> proc.ProcessResult:
  39. full_cmd = itertools.chain([self.dds_exe, '-ltrace'], cmd)
  40. return proc.run(full_cmd, cwd=cwd or self.source_root) # type: ignore
  41. def run(self, cmd: proc.CommandLine, *, cwd: Optional[Path] = None, check: bool = True) -> proc.ProcessResult:
  42. full_cmd = itertools.chain([self.dds_exe, '-ltrace'], cmd)
  43. return proc.run(full_cmd, cwd=cwd, check=check) # type: ignore
  44. @property
  45. def repo_dir_arg(self) -> str:
  46. return f'--repo-dir={self.repo_dir}'
  47. @property
  48. def project_dir_arg(self) -> str:
  49. return f'--project-dir={self.source_root}'
  50. @property
  51. def catalog_path_arg(self) -> str:
  52. return f'--catalog={self.catalog_path}'
  53. def build_deps(self, args: proc.CommandLine, *, toolchain: Optional[str] = None) -> proc.ProcessResult:
  54. return self.run([
  55. 'build-deps',
  56. f'--toolchain={toolchain or tc_mod.get_default_test_toolchain()}',
  57. self.catalog_path_arg,
  58. self.repo_dir_arg,
  59. f'--out={self.deps_build_dir}',
  60. f'--lmi-path={self.lmi_path}',
  61. args,
  62. ])
  63. def repo_add(self, url: str) -> None:
  64. self.run(['repo', 'add', url, '--update', self.catalog_path_arg])
  65. def build(self,
  66. *,
  67. toolchain: Optional[str] = None,
  68. apps: bool = True,
  69. warnings: bool = True,
  70. catalog_path: Optional[Path] = None,
  71. tests: bool = True,
  72. more_args: proc.CommandLine = (),
  73. check: bool = True) -> proc.ProcessResult:
  74. catalog_path = catalog_path or self.catalog_path
  75. return self.run(
  76. [
  77. 'build',
  78. f'--out={self.build_dir}',
  79. f'--toolchain={toolchain or tc_mod.get_default_test_toolchain()}',
  80. f'--catalog={catalog_path}',
  81. f'--repo-dir={self.repo_dir}',
  82. ['--no-tests'] if not tests else [],
  83. ['--no-apps'] if not apps else [],
  84. ['--no-warnings'] if not warnings else [],
  85. self.project_dir_arg,
  86. more_args,
  87. ],
  88. check=check,
  89. )
  90. def sdist_create(self) -> proc.ProcessResult:
  91. self.build_dir.mkdir(exist_ok=True, parents=True)
  92. return self.run(['sdist', 'create', self.project_dir_arg], cwd=self.build_dir)
  93. def sdist_export(self) -> proc.ProcessResult:
  94. return self.run([
  95. 'sdist',
  96. 'export',
  97. self.project_dir_arg,
  98. self.repo_dir_arg,
  99. ])
  100. def repo_import(self, sdist: Path) -> proc.ProcessResult:
  101. return self.run(['repo', self.repo_dir_arg, 'import', sdist])
  102. def catalog_create(self) -> proc.ProcessResult:
  103. self.scratch_dir.mkdir(parents=True, exist_ok=True)
  104. return self.run(['catalog', 'create', f'--catalog={self.catalog_path}'], cwd=self.test_dir)
  105. def catalog_get(self, req: str) -> proc.ProcessResult:
  106. return self.run([
  107. 'catalog',
  108. 'get',
  109. f'--catalog={self.catalog_path}',
  110. f'--out-dir={self.scratch_dir}',
  111. req,
  112. ])
  113. def set_contents(self, path: Union[str, Path], content: bytes) -> ContextManager[Path]:
  114. return fileutil.set_contents(self.source_root / path, content)
  115. @contextmanager
  116. def scoped_dds(dds_exe: Path, test_dir: Path, project_dir: Path) -> Iterator[DDS]:
  117. if os.name == 'nt':
  118. dds_exe = dds_exe.with_suffix('.exe')
  119. with ExitStack() as scope:
  120. yield DDS(dds_exe, test_dir, project_dir, scope)
  121. class DDSFixtureParams(NamedTuple):
  122. ident: str
  123. subdir: Union[Path, str]
  124. def dds_fixture_conf(*argsets: DDSFixtureParams) -> _pytest.mark.MarkDecorator:
  125. args = list(argsets)
  126. return pytest.mark.parametrize('dds', args, indirect=True, ids=[p.ident for p in args])
  127. def dds_fixture_conf_1(subdir: Union[Path, str]) -> _pytest.mark.MarkDecorator:
  128. params = DDSFixtureParams(ident='only', subdir=subdir)
  129. return pytest.mark.parametrize('dds', [params], indirect=True, ids=['.'])