# 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 PigweedIdeSettings 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) def touch_temp_file( self, filename: Union[Path, str], content: str = '' ) -> None: """Create a temp file in the test case's temp dir, without context.""" with self.make_temp_file(filename, content): pass @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() def touch_temp_files( self, files_data: List[Tuple[Union[Path, str], str]] ) -> None: """Create several temp files in the temp dir, without context.""" with self.make_temp_files(files_data): pass @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, ) -> PigweedIdeSettings: """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 PigweedIdeSettings( False, False, False, default_config={ 'working_dir': str(working_dir_path), 'targets': targets, }, )