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