| # Copyright 2022 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """pw_ide test classes.""" |
| |
| from contextlib import contextmanager |
| from io import TextIOWrapper |
| from pathlib import Path |
| import tempfile |
| from typing import Generator, List, Optional, Tuple, Union |
| import unittest |
| |
| from pw_ide.settings import IdeSettings |
| |
| |
| class TempDirTestCase(unittest.TestCase): |
| """Run tests that need access to a temporary directory.""" |
| def setUp(self) -> None: |
| self.temp_dir = tempfile.TemporaryDirectory() |
| self.temp_dir_path = Path(self.temp_dir.name) |
| |
| def tearDown(self) -> None: |
| self.temp_dir.cleanup() |
| return super().tearDown() |
| |
| @contextmanager |
| def make_temp_file( |
| self, |
| filename: Union[Path, str], |
| content: str = '' |
| ) -> Generator[Tuple[TextIOWrapper, Path], None, None]: |
| """Create a temp file in the test case's temp dir. |
| |
| Returns a tuple containing the file reference and the file's path. |
| The file can be read immediately. |
| """ |
| path = self.temp_dir_path / filename |
| |
| with open(path, 'a+', encoding='utf-8') as file: |
| file.write(content) |
| file.flush() |
| file.seek(0) |
| yield (file, path) |
| |
| @contextmanager |
| def open_temp_file( |
| self, |
| filename: Union[Path, str], |
| ) -> Generator[Tuple[TextIOWrapper, Path], None, None]: |
| """Open an existing temp file in the test case's temp dir. |
| |
| Returns a tuple containing the file reference and the file's path. |
| """ |
| path = self.temp_dir_path / filename |
| |
| with open(path, 'r', encoding='utf-8') as file: |
| yield (file, path) |
| |
| @contextmanager |
| def make_temp_files( |
| self, files_data: List[Tuple[Union[Path, str], str]] |
| ) -> Generator[List[TextIOWrapper], None, None]: |
| """Create several temp files in the test case's temp dir. |
| |
| Provide a list of file name and content tuples. Saves you the trouble |
| of excessive `with self.make_temp_file, self.make_temp_file...` |
| nesting, and allows programmatic definition of multiple temp file |
| contexts. Files can be read immediately. |
| """ |
| files: List[TextIOWrapper] = [] |
| |
| for filename, content in files_data: |
| file = open(self.path_in_temp_dir(filename), |
| 'a+', |
| encoding='utf-8') |
| file.write(content) |
| file.flush() |
| file.seek(0) |
| files.append(file) |
| |
| yield files |
| |
| for file in files: |
| file.close() |
| |
| @contextmanager |
| def open_temp_files( |
| self, files_data: List[Union[Path, str]] |
| ) -> Generator[List[TextIOWrapper], None, None]: |
| """Open several existing temp files in the test case's temp dir. |
| |
| Provide a list of file names. Saves you the trouble of excessive |
| `with self.open_temp_file, self.open_temp_file...` nesting, and allows |
| programmatic definition of multiple temp file contexts. |
| """ |
| files: List[TextIOWrapper] = [] |
| |
| for filename in files_data: |
| file = open(self.path_in_temp_dir(filename), 'r', encoding='utf-8') |
| files.append(file) |
| |
| yield files |
| |
| for file in files: |
| file.close() |
| |
| def path_in_temp_dir(self, path: Union[Path, str]) -> Path: |
| """Place a path into the test case's temp dir. |
| |
| This only works with a relative path; with an absolute path, this is a |
| no-op. |
| """ |
| return self.temp_dir_path / path |
| |
| def paths_in_temp_dir(self, *paths: Union[Path, str]) -> List[Path]: |
| """Place several paths into the test case's temp dir. |
| |
| This only works with relative paths; with absolute paths, this is a |
| no-op. |
| """ |
| return [self.path_in_temp_dir(path) for path in paths] |
| |
| |
| class PwIdeTestCase(TempDirTestCase): |
| """A test case for testing `pw_ide`. |
| |
| Provides a temp dir for testing file system actions and access to IDE |
| settings that wrap the temp dir. |
| """ |
| def make_ide_settings(self, |
| working_dir: Optional[Union[str, Path]] = None, |
| targets: Optional[List[str]] = None) -> IdeSettings: |
| """Make settings that wrap provided paths in the temp path.""" |
| |
| if working_dir is not None: |
| working_dir_path = self.path_in_temp_dir(working_dir) |
| else: |
| working_dir_path = self.temp_dir_path |
| |
| if targets is None: |
| targets = [] |
| |
| return IdeSettings(False, |
| False, |
| False, |
| default_config={ |
| 'working_dir': str(working_dir_path), |
| 'targets': targets, |
| }) |