provide-testkit 0.0.0.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- provide/__init__.py +3 -0
- provide/testkit/__init__.py +248 -0
- provide/testkit/archive/__init__.py +24 -0
- provide/testkit/archive/fixtures.py +217 -0
- provide/testkit/cli.py +229 -0
- provide/testkit/common/__init__.py +32 -0
- provide/testkit/common/fixtures.py +234 -0
- provide/testkit/crypto.py +163 -0
- provide/testkit/environment.py +79 -0
- provide/testkit/file/__init__.py +40 -0
- provide/testkit/file/content_fixtures.py +275 -0
- provide/testkit/file/directory_fixtures.py +105 -0
- provide/testkit/file/fixtures.py +49 -0
- provide/testkit/file/special_fixtures.py +141 -0
- provide/testkit/fixtures.py +52 -0
- provide/testkit/harness.py +122 -0
- provide/testkit/hub.py +22 -0
- provide/testkit/logger/__init__.py +39 -0
- provide/testkit/logger/hooks.py +100 -0
- provide/testkit/logger/reset.py +230 -0
- provide/testkit/main.py +22 -0
- provide/testkit/mocking/__init__.py +46 -0
- provide/testkit/mocking/fixtures.py +340 -0
- provide/testkit/process/__init__.py +48 -0
- provide/testkit/process/async_fixtures.py +410 -0
- provide/testkit/process/fixtures.py +54 -0
- provide/testkit/process/subprocess_fixtures.py +208 -0
- provide/testkit/quality/__init__.py +101 -0
- provide/testkit/quality/artifacts.py +360 -0
- provide/testkit/quality/base.py +158 -0
- provide/testkit/quality/cli.py +394 -0
- provide/testkit/quality/complexity/__init__.py +30 -0
- provide/testkit/quality/complexity/analyzer.py +392 -0
- provide/testkit/quality/complexity/fixture.py +196 -0
- provide/testkit/quality/coverage/__init__.py +36 -0
- provide/testkit/quality/coverage/fixture.py +236 -0
- provide/testkit/quality/coverage/reporter.py +150 -0
- provide/testkit/quality/coverage/tracker.py +313 -0
- provide/testkit/quality/decorators.py +380 -0
- provide/testkit/quality/documentation/__init__.py +29 -0
- provide/testkit/quality/documentation/checker.py +361 -0
- provide/testkit/quality/documentation/fixture.py +187 -0
- provide/testkit/quality/profiling/__init__.py +30 -0
- provide/testkit/quality/profiling/fixture.py +332 -0
- provide/testkit/quality/profiling/profiler.py +428 -0
- provide/testkit/quality/report.py +266 -0
- provide/testkit/quality/runner.py +319 -0
- provide/testkit/quality/security/__init__.py +29 -0
- provide/testkit/quality/security/fixture.py +196 -0
- provide/testkit/quality/security/scanner.py +338 -0
- provide/testkit/streams.py +54 -0
- provide/testkit/threading/__init__.py +38 -0
- provide/testkit/threading/basic_fixtures.py +103 -0
- provide/testkit/threading/data_fixtures.py +101 -0
- provide/testkit/threading/execution_fixtures.py +268 -0
- provide/testkit/threading/fixtures.py +50 -0
- provide/testkit/threading/sync_fixtures.py +98 -0
- provide/testkit/time/__init__.py +32 -0
- provide/testkit/time/fixtures.py +416 -0
- provide/testkit/transport/__init__.py +30 -0
- provide/testkit/transport/fixtures.py +278 -0
- provide_testkit-0.0.0.dev0.dist-info/METADATA +145 -0
- provide_testkit-0.0.0.dev0.dist-info/RECORD +66 -0
- provide_testkit-0.0.0.dev0.dist-info/WHEEL +5 -0
- provide_testkit-0.0.0.dev0.dist-info/entry_points.txt +2 -0
- provide_testkit-0.0.0.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Environment management utilities for testing.
|
|
3
|
+
|
|
4
|
+
Provides context managers and utilities for managing test environments,
|
|
5
|
+
environment variables, and foundation setup/cleanup.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from provide.testkit.logger import reset_foundation_setup_for_testing
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TestEnvironment:
|
|
16
|
+
"""Context manager for test environment setup with proper cleanup."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, env_vars: dict[str, str] | None = None):
|
|
19
|
+
"""
|
|
20
|
+
Initialize test environment manager.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
env_vars: Dictionary of environment variables to set during the test
|
|
24
|
+
"""
|
|
25
|
+
self.env_vars = env_vars or {}
|
|
26
|
+
self.original_env: dict[str, str | None] = {}
|
|
27
|
+
|
|
28
|
+
def __enter__(self) -> "TestEnvironment":
|
|
29
|
+
"""Enter the test environment context."""
|
|
30
|
+
# Save original environment variables
|
|
31
|
+
for key in self.env_vars:
|
|
32
|
+
self.original_env[key] = os.environ.get(key)
|
|
33
|
+
os.environ[key] = self.env_vars[key]
|
|
34
|
+
|
|
35
|
+
# Reset foundation setup
|
|
36
|
+
reset_foundation_setup_for_testing()
|
|
37
|
+
return self
|
|
38
|
+
|
|
39
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
40
|
+
"""Exit the test environment context and restore original state."""
|
|
41
|
+
# Restore original environment variables
|
|
42
|
+
for key, original_value in self.original_env.items():
|
|
43
|
+
if original_value is None:
|
|
44
|
+
os.environ.pop(key, None)
|
|
45
|
+
else:
|
|
46
|
+
os.environ[key] = original_value
|
|
47
|
+
|
|
48
|
+
# Reset foundation setup again for cleanup
|
|
49
|
+
reset_foundation_setup_for_testing()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_example_dir() -> Path:
|
|
53
|
+
"""Get the examples directory path consistently across examples."""
|
|
54
|
+
# Note: This assumes foundation project structure
|
|
55
|
+
# In testkit context, this should point to foundation's examples
|
|
56
|
+
current_file = Path(__file__).resolve()
|
|
57
|
+
# Go up from testkit/src/provide/testkit/environment.py to find foundation
|
|
58
|
+
testkit_root = current_file.parent.parent.parent.parent
|
|
59
|
+
foundation_root = testkit_root.parent / "provide-foundation"
|
|
60
|
+
return foundation_root / "examples"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def add_src_to_path() -> Path:
|
|
64
|
+
"""Add src directory to Python path for examples. Returns project root path."""
|
|
65
|
+
import sys
|
|
66
|
+
|
|
67
|
+
example_dir = get_example_dir()
|
|
68
|
+
project_root = example_dir.parent
|
|
69
|
+
src_path = project_root / "src"
|
|
70
|
+
|
|
71
|
+
if src_path.exists() and str(src_path) not in sys.path:
|
|
72
|
+
sys.path.insert(0, str(src_path))
|
|
73
|
+
|
|
74
|
+
return project_root
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def reset_test_environment() -> None:
|
|
78
|
+
"""Reset the test environment to a clean state."""
|
|
79
|
+
reset_foundation_setup_for_testing()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File testing fixtures for the provide-io ecosystem.
|
|
3
|
+
|
|
4
|
+
Standard fixtures for file and directory operations that can be used
|
|
5
|
+
across any project that depends on provide.foundation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from provide.testkit.file.fixtures import (
|
|
9
|
+
binary_file,
|
|
10
|
+
empty_directory,
|
|
11
|
+
nested_directory_structure,
|
|
12
|
+
readonly_file,
|
|
13
|
+
temp_binary_file,
|
|
14
|
+
temp_csv_file,
|
|
15
|
+
temp_directory,
|
|
16
|
+
temp_executable_file,
|
|
17
|
+
temp_file,
|
|
18
|
+
temp_file_with_content,
|
|
19
|
+
temp_json_file,
|
|
20
|
+
temp_named_file,
|
|
21
|
+
temp_symlink,
|
|
22
|
+
test_files_structure,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"binary_file",
|
|
27
|
+
"empty_directory",
|
|
28
|
+
"nested_directory_structure",
|
|
29
|
+
"readonly_file",
|
|
30
|
+
"temp_binary_file",
|
|
31
|
+
"temp_csv_file",
|
|
32
|
+
"temp_directory",
|
|
33
|
+
"temp_executable_file",
|
|
34
|
+
"temp_file",
|
|
35
|
+
"temp_file_with_content",
|
|
36
|
+
"temp_json_file",
|
|
37
|
+
"temp_named_file",
|
|
38
|
+
"temp_symlink",
|
|
39
|
+
"test_files_structure",
|
|
40
|
+
]
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Content-based file test fixtures.
|
|
3
|
+
|
|
4
|
+
Fixtures for creating files with specific content types like text, binary,
|
|
5
|
+
CSV, JSON, and other structured data.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import csv
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
import random
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
from provide.foundation.file import temp_file as foundation_temp_file
|
|
16
|
+
from provide.foundation.file.safe import safe_delete
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def temp_file():
|
|
21
|
+
"""
|
|
22
|
+
Create a temporary file factory with optional content.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
A function that creates temporary files with specified content and suffix.
|
|
26
|
+
"""
|
|
27
|
+
created_files = []
|
|
28
|
+
|
|
29
|
+
def _make_temp_file(content: str = "test content", suffix: str = ".txt") -> Path:
|
|
30
|
+
"""
|
|
31
|
+
Create a temporary file.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
content: Content to write to the file
|
|
35
|
+
suffix: File suffix/extension
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Path to the created temporary file
|
|
39
|
+
"""
|
|
40
|
+
with foundation_temp_file(suffix=suffix, text=True, cleanup=False) as path:
|
|
41
|
+
path.write_text(content)
|
|
42
|
+
created_files.append(path)
|
|
43
|
+
return path
|
|
44
|
+
|
|
45
|
+
yield _make_temp_file
|
|
46
|
+
|
|
47
|
+
# Cleanup all created files
|
|
48
|
+
for path in created_files:
|
|
49
|
+
safe_delete(path, missing_ok=True)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.fixture
|
|
53
|
+
def temp_named_file():
|
|
54
|
+
"""
|
|
55
|
+
Create a named temporary file factory.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Function that creates named temporary files.
|
|
59
|
+
"""
|
|
60
|
+
created_files = []
|
|
61
|
+
|
|
62
|
+
def _make_named_file(
|
|
63
|
+
content: bytes | str = None,
|
|
64
|
+
suffix: str = "",
|
|
65
|
+
prefix: str = "tmp",
|
|
66
|
+
dir: Path | str = None,
|
|
67
|
+
mode: str = "w+b",
|
|
68
|
+
delete: bool = False,
|
|
69
|
+
) -> Path:
|
|
70
|
+
"""
|
|
71
|
+
Create a named temporary file.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
content: Optional content to write
|
|
75
|
+
suffix: File suffix
|
|
76
|
+
prefix: File prefix
|
|
77
|
+
dir: Directory for the file
|
|
78
|
+
mode: File mode
|
|
79
|
+
delete: Whether to delete on close
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Path to the created file
|
|
83
|
+
"""
|
|
84
|
+
if isinstance(dir, Path):
|
|
85
|
+
dir = str(dir)
|
|
86
|
+
|
|
87
|
+
# Use Foundation's temp_file with cleanup=False since we manage cleanup
|
|
88
|
+
with foundation_temp_file(
|
|
89
|
+
suffix=suffix, prefix=prefix, dir=dir, text="b" not in mode, cleanup=False
|
|
90
|
+
) as path:
|
|
91
|
+
if content is not None:
|
|
92
|
+
if isinstance(content, str):
|
|
93
|
+
if "b" in mode:
|
|
94
|
+
path.write_bytes(content.encode())
|
|
95
|
+
else:
|
|
96
|
+
path.write_text(content)
|
|
97
|
+
else:
|
|
98
|
+
path.write_bytes(content)
|
|
99
|
+
|
|
100
|
+
if not delete:
|
|
101
|
+
created_files.append(path)
|
|
102
|
+
|
|
103
|
+
return path
|
|
104
|
+
|
|
105
|
+
yield _make_named_file
|
|
106
|
+
|
|
107
|
+
# Cleanup
|
|
108
|
+
for path in created_files:
|
|
109
|
+
safe_delete(path, missing_ok=True)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@pytest.fixture
|
|
113
|
+
def temp_file_with_content():
|
|
114
|
+
"""
|
|
115
|
+
Create temporary files with specific content.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Function that creates files with content.
|
|
119
|
+
"""
|
|
120
|
+
created_files = []
|
|
121
|
+
|
|
122
|
+
def _make_file(content: str | bytes, suffix: str = ".txt", encoding: str = "utf-8") -> Path:
|
|
123
|
+
"""
|
|
124
|
+
Create a temporary file with content.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
content: Content to write
|
|
128
|
+
suffix: File suffix
|
|
129
|
+
encoding: Text encoding (for str content)
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Path to created file
|
|
133
|
+
"""
|
|
134
|
+
# Use Foundation's temp_file
|
|
135
|
+
with foundation_temp_file(suffix=suffix, text=not isinstance(content, bytes), cleanup=False) as path:
|
|
136
|
+
if isinstance(content, bytes):
|
|
137
|
+
path.write_bytes(content)
|
|
138
|
+
else:
|
|
139
|
+
path.write_text(content, encoding=encoding)
|
|
140
|
+
|
|
141
|
+
created_files.append(path)
|
|
142
|
+
return path
|
|
143
|
+
|
|
144
|
+
yield _make_file
|
|
145
|
+
|
|
146
|
+
# Cleanup
|
|
147
|
+
for path in created_files:
|
|
148
|
+
safe_delete(path, missing_ok=True)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@pytest.fixture
|
|
152
|
+
def temp_binary_file():
|
|
153
|
+
"""
|
|
154
|
+
Create temporary binary files.
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Function that creates binary files.
|
|
158
|
+
"""
|
|
159
|
+
created_files = []
|
|
160
|
+
|
|
161
|
+
def _make_binary(size: int = 1024, pattern: bytes = None, suffix: str = ".bin") -> Path:
|
|
162
|
+
"""
|
|
163
|
+
Create a temporary binary file.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
size: File size in bytes
|
|
167
|
+
pattern: Optional byte pattern to repeat
|
|
168
|
+
suffix: File suffix
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
Path to created binary file
|
|
172
|
+
"""
|
|
173
|
+
if pattern is None:
|
|
174
|
+
# Create pseudo-random binary data
|
|
175
|
+
content = bytes(random.randint(0, 255) for _ in range(size))
|
|
176
|
+
else:
|
|
177
|
+
# Repeat pattern to reach size
|
|
178
|
+
repetitions = size // len(pattern) + 1
|
|
179
|
+
content = (pattern * repetitions)[:size]
|
|
180
|
+
|
|
181
|
+
with foundation_temp_file(suffix=suffix, text=False, cleanup=False) as path:
|
|
182
|
+
path.write_bytes(content)
|
|
183
|
+
|
|
184
|
+
created_files.append(path)
|
|
185
|
+
return path
|
|
186
|
+
|
|
187
|
+
yield _make_binary
|
|
188
|
+
|
|
189
|
+
# Cleanup
|
|
190
|
+
for path in created_files:
|
|
191
|
+
safe_delete(path, missing_ok=True)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@pytest.fixture
|
|
195
|
+
def temp_csv_file():
|
|
196
|
+
"""
|
|
197
|
+
Create temporary CSV files for testing.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Function that creates CSV files.
|
|
201
|
+
"""
|
|
202
|
+
created_files = []
|
|
203
|
+
|
|
204
|
+
def _make_csv(headers: list[str], rows: list[list], suffix: str = ".csv") -> Path:
|
|
205
|
+
"""
|
|
206
|
+
Create a temporary CSV file.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
headers: Column headers
|
|
210
|
+
rows: Data rows
|
|
211
|
+
suffix: File suffix
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Path to created CSV file
|
|
215
|
+
"""
|
|
216
|
+
with foundation_temp_file(suffix=suffix, text=True, cleanup=False) as path:
|
|
217
|
+
with open(path, "w", newline="") as f:
|
|
218
|
+
writer = csv.writer(f)
|
|
219
|
+
writer.writerow(headers)
|
|
220
|
+
writer.writerows(rows)
|
|
221
|
+
|
|
222
|
+
created_files.append(path)
|
|
223
|
+
return path
|
|
224
|
+
|
|
225
|
+
yield _make_csv
|
|
226
|
+
|
|
227
|
+
# Cleanup
|
|
228
|
+
for path in created_files:
|
|
229
|
+
safe_delete(path, missing_ok=True)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@pytest.fixture
|
|
233
|
+
def temp_json_file():
|
|
234
|
+
"""
|
|
235
|
+
Create temporary JSON files for testing.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
Function that creates JSON files.
|
|
239
|
+
"""
|
|
240
|
+
created_files = []
|
|
241
|
+
|
|
242
|
+
def _make_json(data: dict | list, suffix: str = ".json", indent: int = 2) -> Path:
|
|
243
|
+
"""
|
|
244
|
+
Create a temporary JSON file.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
data: JSON data to write
|
|
248
|
+
suffix: File suffix
|
|
249
|
+
indent: JSON indentation
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Path to created JSON file
|
|
253
|
+
"""
|
|
254
|
+
with foundation_temp_file(suffix=suffix, text=True, cleanup=False) as path:
|
|
255
|
+
with open(path, "w") as f:
|
|
256
|
+
json.dump(data, f, indent=indent)
|
|
257
|
+
|
|
258
|
+
created_files.append(path)
|
|
259
|
+
return path
|
|
260
|
+
|
|
261
|
+
yield _make_json
|
|
262
|
+
|
|
263
|
+
# Cleanup
|
|
264
|
+
for path in created_files:
|
|
265
|
+
safe_delete(path, missing_ok=True)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
__all__ = [
|
|
269
|
+
"temp_binary_file",
|
|
270
|
+
"temp_csv_file",
|
|
271
|
+
"temp_file",
|
|
272
|
+
"temp_file_with_content",
|
|
273
|
+
"temp_json_file",
|
|
274
|
+
"temp_named_file",
|
|
275
|
+
]
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Directory-specific test fixtures.
|
|
3
|
+
|
|
4
|
+
Fixtures for creating temporary directories, nested structures,
|
|
5
|
+
and standard test directory layouts.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from collections.abc import Generator
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
from provide.foundation.file import temp_dir as foundation_temp_dir
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def temp_directory() -> Generator[Path, None, None]:
|
|
18
|
+
"""
|
|
19
|
+
Create a temporary directory that's cleaned up after test.
|
|
20
|
+
|
|
21
|
+
Yields:
|
|
22
|
+
Path to the temporary directory.
|
|
23
|
+
"""
|
|
24
|
+
with foundation_temp_dir() as temp_dir:
|
|
25
|
+
yield temp_dir
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.fixture
|
|
29
|
+
def test_files_structure() -> Generator[tuple[Path, Path], None, None]:
|
|
30
|
+
"""
|
|
31
|
+
Create standard test file structure with files and subdirectories.
|
|
32
|
+
|
|
33
|
+
Creates:
|
|
34
|
+
- source/
|
|
35
|
+
- file1.txt (contains "Content 1")
|
|
36
|
+
- file2.txt (contains "Content 2")
|
|
37
|
+
- subdir/
|
|
38
|
+
- file3.txt (contains "Content 3")
|
|
39
|
+
|
|
40
|
+
Yields:
|
|
41
|
+
Tuple of (temp_path, source_path)
|
|
42
|
+
"""
|
|
43
|
+
with foundation_temp_dir() as path:
|
|
44
|
+
source = path / "source"
|
|
45
|
+
source.mkdir()
|
|
46
|
+
|
|
47
|
+
# Create test files
|
|
48
|
+
(source / "file1.txt").write_text("Content 1")
|
|
49
|
+
(source / "file2.txt").write_text("Content 2")
|
|
50
|
+
|
|
51
|
+
# Create subdirectory with files
|
|
52
|
+
subdir = source / "subdir"
|
|
53
|
+
subdir.mkdir()
|
|
54
|
+
(subdir / "file3.txt").write_text("Content 3")
|
|
55
|
+
|
|
56
|
+
yield path, source
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.fixture
|
|
60
|
+
def nested_directory_structure() -> Generator[Path, None, None]:
|
|
61
|
+
"""
|
|
62
|
+
Create a deeply nested directory structure for testing.
|
|
63
|
+
|
|
64
|
+
Creates:
|
|
65
|
+
- level1/
|
|
66
|
+
- level2/
|
|
67
|
+
- level3/
|
|
68
|
+
- deep_file.txt
|
|
69
|
+
- file_l2.txt
|
|
70
|
+
- file_l1.txt
|
|
71
|
+
|
|
72
|
+
Yields:
|
|
73
|
+
Path to the root of the structure.
|
|
74
|
+
"""
|
|
75
|
+
with foundation_temp_dir() as root:
|
|
76
|
+
# Create nested structure
|
|
77
|
+
deep_dir = root / "level1" / "level2" / "level3"
|
|
78
|
+
deep_dir.mkdir(parents=True)
|
|
79
|
+
|
|
80
|
+
# Add files at different levels
|
|
81
|
+
(root / "file_l1.txt").write_text("Level 1 file")
|
|
82
|
+
(root / "level1" / "file_l2.txt").write_text("Level 2 file")
|
|
83
|
+
(deep_dir / "deep_file.txt").write_text("Deep file")
|
|
84
|
+
|
|
85
|
+
yield root
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@pytest.fixture
|
|
89
|
+
def empty_directory() -> Generator[Path, None, None]:
|
|
90
|
+
"""
|
|
91
|
+
Create an empty temporary directory.
|
|
92
|
+
|
|
93
|
+
Yields:
|
|
94
|
+
Path to an empty directory.
|
|
95
|
+
"""
|
|
96
|
+
with foundation_temp_dir() as temp_dir:
|
|
97
|
+
yield temp_dir
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
__all__ = [
|
|
101
|
+
"empty_directory",
|
|
102
|
+
"nested_directory_structure",
|
|
103
|
+
"temp_directory",
|
|
104
|
+
"test_files_structure",
|
|
105
|
+
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File and Directory Test Fixtures.
|
|
3
|
+
|
|
4
|
+
Core file testing fixtures with re-exports from specialized modules.
|
|
5
|
+
Common fixtures for testing file operations, creating temporary directories,
|
|
6
|
+
and standard test file structures used across the provide-io ecosystem.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Re-export all fixtures from specialized modules
|
|
10
|
+
from provide.testkit.file.content_fixtures import (
|
|
11
|
+
temp_binary_file,
|
|
12
|
+
temp_csv_file,
|
|
13
|
+
temp_file,
|
|
14
|
+
temp_file_with_content,
|
|
15
|
+
temp_json_file,
|
|
16
|
+
temp_named_file,
|
|
17
|
+
)
|
|
18
|
+
from provide.testkit.file.directory_fixtures import (
|
|
19
|
+
empty_directory,
|
|
20
|
+
nested_directory_structure,
|
|
21
|
+
temp_directory,
|
|
22
|
+
test_files_structure,
|
|
23
|
+
)
|
|
24
|
+
from provide.testkit.file.special_fixtures import (
|
|
25
|
+
binary_file,
|
|
26
|
+
readonly_file,
|
|
27
|
+
temp_executable_file,
|
|
28
|
+
temp_symlink,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
# Directory fixtures
|
|
33
|
+
"temp_directory",
|
|
34
|
+
"test_files_structure",
|
|
35
|
+
"nested_directory_structure",
|
|
36
|
+
"empty_directory",
|
|
37
|
+
# Special file fixtures
|
|
38
|
+
"binary_file",
|
|
39
|
+
"readonly_file",
|
|
40
|
+
"temp_symlink",
|
|
41
|
+
"temp_executable_file",
|
|
42
|
+
# Content-based fixtures
|
|
43
|
+
"temp_file",
|
|
44
|
+
"temp_named_file",
|
|
45
|
+
"temp_file_with_content",
|
|
46
|
+
"temp_binary_file",
|
|
47
|
+
"temp_csv_file",
|
|
48
|
+
"temp_json_file",
|
|
49
|
+
]
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Special file test fixtures.
|
|
3
|
+
|
|
4
|
+
Fixtures for creating specialized files like binary files, read-only files,
|
|
5
|
+
symbolic links, and executable files.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from collections.abc import Generator
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import stat
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
from provide.foundation.file import temp_file as foundation_temp_file
|
|
15
|
+
from provide.foundation.file.safe import safe_delete
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.fixture
|
|
19
|
+
def binary_file() -> Generator[Path, None, None]:
|
|
20
|
+
"""
|
|
21
|
+
Create a temporary binary file for testing.
|
|
22
|
+
|
|
23
|
+
Yields:
|
|
24
|
+
Path to a binary file containing sample binary data.
|
|
25
|
+
"""
|
|
26
|
+
with foundation_temp_file(suffix=".bin", text=False, cleanup=False) as path:
|
|
27
|
+
# Write some binary data
|
|
28
|
+
path.write_bytes(
|
|
29
|
+
b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09" + b"\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8\xf7\xf6"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
yield path
|
|
33
|
+
safe_delete(path, missing_ok=True)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.fixture
|
|
37
|
+
def readonly_file() -> Generator[Path, None, None]:
|
|
38
|
+
"""
|
|
39
|
+
Create a read-only file for permission testing.
|
|
40
|
+
|
|
41
|
+
Yields:
|
|
42
|
+
Path to a read-only file.
|
|
43
|
+
"""
|
|
44
|
+
with foundation_temp_file(suffix=".txt", text=True, cleanup=False) as path:
|
|
45
|
+
path.write_text("Read-only content")
|
|
46
|
+
|
|
47
|
+
# Make file read-only
|
|
48
|
+
path.chmod(0o444)
|
|
49
|
+
|
|
50
|
+
yield path
|
|
51
|
+
|
|
52
|
+
# Restore write permission for cleanup
|
|
53
|
+
path.chmod(0o644)
|
|
54
|
+
safe_delete(path, missing_ok=True)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@pytest.fixture
|
|
58
|
+
def temp_symlink():
|
|
59
|
+
"""
|
|
60
|
+
Create temporary symbolic links for testing.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Function that creates symbolic links.
|
|
64
|
+
"""
|
|
65
|
+
created_links = []
|
|
66
|
+
|
|
67
|
+
def _make_symlink(target: Path | str, link_name: Path | str = None) -> Path:
|
|
68
|
+
"""
|
|
69
|
+
Create a temporary symbolic link.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
target: Target path for the symlink
|
|
73
|
+
link_name: Optional link name (auto-generated if None)
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Path to created symlink
|
|
77
|
+
"""
|
|
78
|
+
target = Path(target)
|
|
79
|
+
|
|
80
|
+
if link_name is None:
|
|
81
|
+
with foundation_temp_file(cleanup=True) as temp_path:
|
|
82
|
+
link_name = Path(str(temp_path) + "_link")
|
|
83
|
+
else:
|
|
84
|
+
link_name = Path(link_name)
|
|
85
|
+
|
|
86
|
+
link_name.symlink_to(target)
|
|
87
|
+
created_links.append(link_name)
|
|
88
|
+
|
|
89
|
+
return link_name
|
|
90
|
+
|
|
91
|
+
yield _make_symlink
|
|
92
|
+
|
|
93
|
+
# Cleanup
|
|
94
|
+
for link in created_links:
|
|
95
|
+
safe_delete(link, missing_ok=True)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@pytest.fixture
|
|
99
|
+
def temp_executable_file():
|
|
100
|
+
"""
|
|
101
|
+
Create temporary executable files for testing.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Function that creates executable files.
|
|
105
|
+
"""
|
|
106
|
+
created_files = []
|
|
107
|
+
|
|
108
|
+
def _make_executable(content: str = "#!/bin/sh\necho 'test'\n", suffix: str = ".sh") -> Path:
|
|
109
|
+
"""
|
|
110
|
+
Create a temporary executable file.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
content: Script content
|
|
114
|
+
suffix: File suffix
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Path to created executable file
|
|
118
|
+
"""
|
|
119
|
+
with foundation_temp_file(suffix=suffix, text=True, cleanup=False) as path:
|
|
120
|
+
path.write_text(content)
|
|
121
|
+
|
|
122
|
+
# Make executable
|
|
123
|
+
current = path.stat().st_mode
|
|
124
|
+
path.chmod(current | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
|
|
125
|
+
|
|
126
|
+
created_files.append(path)
|
|
127
|
+
return path
|
|
128
|
+
|
|
129
|
+
yield _make_executable
|
|
130
|
+
|
|
131
|
+
# Cleanup
|
|
132
|
+
for path in created_files:
|
|
133
|
+
safe_delete(path, missing_ok=True)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
__all__ = [
|
|
137
|
+
"binary_file",
|
|
138
|
+
"readonly_file",
|
|
139
|
+
"temp_executable_file",
|
|
140
|
+
"temp_symlink",
|
|
141
|
+
]
|