• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2022 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""pw_ide test classes."""
15
16from contextlib import contextmanager
17from io import TextIOWrapper
18from pathlib import Path
19import tempfile
20from typing import Generator, List, Optional, Tuple, Union
21import unittest
22
23from pw_ide.settings import PigweedIdeSettings
24
25
26class TempDirTestCase(unittest.TestCase):
27    """Run tests that need access to a temporary directory."""
28
29    def setUp(self) -> None:
30        self.temp_dir = tempfile.TemporaryDirectory()
31        self.temp_dir_path = Path(self.temp_dir.name)
32
33    def tearDown(self) -> None:
34        self.temp_dir.cleanup()
35        return super().tearDown()
36
37    @contextmanager
38    def make_temp_file(
39        self, filename: Union[Path, str], content: str = ''
40    ) -> Generator[Tuple[TextIOWrapper, Path], None, None]:
41        """Create a temp file in the test case's temp dir.
42
43        Returns a tuple containing the file reference and the file's path.
44        The file can be read immediately.
45        """
46        path = self.temp_dir_path / filename
47
48        with open(path, 'a+', encoding='utf-8') as file:
49            file.write(content)
50            file.flush()
51            file.seek(0)
52            yield (file, path)
53
54    def touch_temp_file(
55        self, filename: Union[Path, str], content: str = ''
56    ) -> None:
57        """Create a temp file in the test case's temp dir, without context."""
58        with self.make_temp_file(filename, content):
59            pass
60
61    @contextmanager
62    def open_temp_file(
63        self,
64        filename: Union[Path, str],
65    ) -> Generator[Tuple[TextIOWrapper, Path], None, None]:
66        """Open an existing temp file in the test case's temp dir.
67
68        Returns a tuple containing the file reference and the file's path.
69        """
70        path = self.temp_dir_path / filename
71
72        with open(path, 'r', encoding='utf-8') as file:
73            yield (file, path)
74
75    @contextmanager
76    def make_temp_files(
77        self, files_data: List[Tuple[Union[Path, str], str]]
78    ) -> Generator[List[TextIOWrapper], None, None]:
79        """Create several temp files in the test case's temp dir.
80
81        Provide a list of file name and content tuples. Saves you the trouble
82        of excessive `with self.make_temp_file, self.make_temp_file...`
83        nesting, and allows programmatic definition of multiple temp file
84        contexts. Files can be read immediately.
85        """
86        files: List[TextIOWrapper] = []
87
88        for filename, content in files_data:
89            file = open(self.path_in_temp_dir(filename), 'a+', encoding='utf-8')
90            file.write(content)
91            file.flush()
92            file.seek(0)
93            files.append(file)
94
95        yield files
96
97        for file in files:
98            file.close()
99
100    def touch_temp_files(
101        self, files_data: List[Tuple[Union[Path, str], str]]
102    ) -> None:
103        """Create several temp files in the temp dir, without context."""
104        with self.make_temp_files(files_data):
105            pass
106
107    @contextmanager
108    def open_temp_files(
109        self, files_data: List[Union[Path, str]]
110    ) -> Generator[List[TextIOWrapper], None, None]:
111        """Open several existing temp files in the test case's temp dir.
112
113        Provide a list of file names. Saves you the trouble of excessive
114        `with self.open_temp_file, self.open_temp_file...` nesting, and allows
115        programmatic definition of multiple temp file contexts.
116        """
117        files: List[TextIOWrapper] = []
118
119        for filename in files_data:
120            file = open(self.path_in_temp_dir(filename), 'r', encoding='utf-8')
121            files.append(file)
122
123        yield files
124
125        for file in files:
126            file.close()
127
128    def path_in_temp_dir(self, path: Union[Path, str]) -> Path:
129        """Place a path into the test case's temp dir.
130
131        This only works with a relative path; with an absolute path, this is a
132        no-op.
133        """
134        return self.temp_dir_path / path
135
136    def paths_in_temp_dir(self, *paths: Union[Path, str]) -> List[Path]:
137        """Place several paths into the test case's temp dir.
138
139        This only works with relative paths; with absolute paths, this is a
140        no-op.
141        """
142        return [self.path_in_temp_dir(path) for path in paths]
143
144
145class PwIdeTestCase(TempDirTestCase):
146    """A test case for testing `pw_ide`.
147
148    Provides a temp dir for testing file system actions and access to IDE
149    settings that wrap the temp dir.
150    """
151
152    def make_ide_settings(
153        self,
154        working_dir: Optional[Union[str, Path]] = None,
155        targets: Optional[List[str]] = None,
156    ) -> PigweedIdeSettings:
157        """Make settings that wrap provided paths in the temp path."""
158
159        if working_dir is not None:
160            working_dir_path = self.path_in_temp_dir(working_dir)
161        else:
162            working_dir_path = self.temp_dir_path
163
164        if targets is None:
165            targets = []
166
167        return PigweedIdeSettings(
168            False,
169            False,
170            False,
171            default_config={
172                'working_dir': str(working_dir_path),
173                'targets': targets,
174            },
175        )
176