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