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.
Files changed (66) hide show
  1. provide/__init__.py +3 -0
  2. provide/testkit/__init__.py +248 -0
  3. provide/testkit/archive/__init__.py +24 -0
  4. provide/testkit/archive/fixtures.py +217 -0
  5. provide/testkit/cli.py +229 -0
  6. provide/testkit/common/__init__.py +32 -0
  7. provide/testkit/common/fixtures.py +234 -0
  8. provide/testkit/crypto.py +163 -0
  9. provide/testkit/environment.py +79 -0
  10. provide/testkit/file/__init__.py +40 -0
  11. provide/testkit/file/content_fixtures.py +275 -0
  12. provide/testkit/file/directory_fixtures.py +105 -0
  13. provide/testkit/file/fixtures.py +49 -0
  14. provide/testkit/file/special_fixtures.py +141 -0
  15. provide/testkit/fixtures.py +52 -0
  16. provide/testkit/harness.py +122 -0
  17. provide/testkit/hub.py +22 -0
  18. provide/testkit/logger/__init__.py +39 -0
  19. provide/testkit/logger/hooks.py +100 -0
  20. provide/testkit/logger/reset.py +230 -0
  21. provide/testkit/main.py +22 -0
  22. provide/testkit/mocking/__init__.py +46 -0
  23. provide/testkit/mocking/fixtures.py +340 -0
  24. provide/testkit/process/__init__.py +48 -0
  25. provide/testkit/process/async_fixtures.py +410 -0
  26. provide/testkit/process/fixtures.py +54 -0
  27. provide/testkit/process/subprocess_fixtures.py +208 -0
  28. provide/testkit/quality/__init__.py +101 -0
  29. provide/testkit/quality/artifacts.py +360 -0
  30. provide/testkit/quality/base.py +158 -0
  31. provide/testkit/quality/cli.py +394 -0
  32. provide/testkit/quality/complexity/__init__.py +30 -0
  33. provide/testkit/quality/complexity/analyzer.py +392 -0
  34. provide/testkit/quality/complexity/fixture.py +196 -0
  35. provide/testkit/quality/coverage/__init__.py +36 -0
  36. provide/testkit/quality/coverage/fixture.py +236 -0
  37. provide/testkit/quality/coverage/reporter.py +150 -0
  38. provide/testkit/quality/coverage/tracker.py +313 -0
  39. provide/testkit/quality/decorators.py +380 -0
  40. provide/testkit/quality/documentation/__init__.py +29 -0
  41. provide/testkit/quality/documentation/checker.py +361 -0
  42. provide/testkit/quality/documentation/fixture.py +187 -0
  43. provide/testkit/quality/profiling/__init__.py +30 -0
  44. provide/testkit/quality/profiling/fixture.py +332 -0
  45. provide/testkit/quality/profiling/profiler.py +428 -0
  46. provide/testkit/quality/report.py +266 -0
  47. provide/testkit/quality/runner.py +319 -0
  48. provide/testkit/quality/security/__init__.py +29 -0
  49. provide/testkit/quality/security/fixture.py +196 -0
  50. provide/testkit/quality/security/scanner.py +338 -0
  51. provide/testkit/streams.py +54 -0
  52. provide/testkit/threading/__init__.py +38 -0
  53. provide/testkit/threading/basic_fixtures.py +103 -0
  54. provide/testkit/threading/data_fixtures.py +101 -0
  55. provide/testkit/threading/execution_fixtures.py +268 -0
  56. provide/testkit/threading/fixtures.py +50 -0
  57. provide/testkit/threading/sync_fixtures.py +98 -0
  58. provide/testkit/time/__init__.py +32 -0
  59. provide/testkit/time/fixtures.py +416 -0
  60. provide/testkit/transport/__init__.py +30 -0
  61. provide/testkit/transport/fixtures.py +278 -0
  62. provide_testkit-0.0.0.dev0.dist-info/METADATA +145 -0
  63. provide_testkit-0.0.0.dev0.dist-info/RECORD +66 -0
  64. provide_testkit-0.0.0.dev0.dist-info/WHEEL +5 -0
  65. provide_testkit-0.0.0.dev0.dist-info/entry_points.txt +2 -0
  66. provide_testkit-0.0.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,268 @@
1
+ """
2
+ Thread execution and testing helper fixtures.
3
+
4
+ Advanced fixtures for concurrent execution, synchronization testing, deadlock detection,
5
+ and exception handling in threaded code.
6
+ """
7
+
8
+ from collections.abc import Callable
9
+ from concurrent.futures import ThreadPoolExecutor
10
+ import threading
11
+ import time
12
+ from typing import Any
13
+
14
+ import pytest
15
+
16
+
17
+ @pytest.fixture
18
+ def concurrent_executor():
19
+ """
20
+ Helper for executing functions concurrently in tests.
21
+
22
+ Returns:
23
+ Concurrent execution helper.
24
+ """
25
+
26
+ class ConcurrentExecutor:
27
+ def __init__(self):
28
+ self.results = []
29
+ self.exceptions = []
30
+
31
+ def run_concurrent(self, func: Callable, args_list: list[tuple], max_workers: int = 4) -> list[Any]:
32
+ """
33
+ Run function concurrently with different arguments.
34
+
35
+ Args:
36
+ func: Function to execute
37
+ args_list: List of argument tuples
38
+ max_workers: Maximum concurrent workers
39
+
40
+ Returns:
41
+ List of results in order
42
+ """
43
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
44
+ futures = []
45
+ for args in args_list:
46
+ if isinstance(args, tuple):
47
+ future = executor.submit(func, *args)
48
+ else:
49
+ future = executor.submit(func, args)
50
+ futures.append(future)
51
+
52
+ results = []
53
+ for future in futures:
54
+ try:
55
+ result = future.result(timeout=10)
56
+ results.append(result)
57
+ self.results.append(result)
58
+ except Exception as e:
59
+ self.exceptions.append(e)
60
+ results.append(None)
61
+
62
+ return results
63
+
64
+ def run_parallel(self, funcs: list[Callable], timeout: float = 10) -> list[Any]:
65
+ """
66
+ Run different functions in parallel.
67
+
68
+ Args:
69
+ funcs: List of functions to execute
70
+ timeout: Timeout for each function
71
+
72
+ Returns:
73
+ List of results
74
+ """
75
+ with ThreadPoolExecutor(max_workers=len(funcs)) as executor:
76
+ futures = [executor.submit(func) for func in funcs]
77
+ results = []
78
+
79
+ for future in futures:
80
+ try:
81
+ result = future.result(timeout=timeout)
82
+ results.append(result)
83
+ except Exception as e:
84
+ self.exceptions.append(e)
85
+ results.append(None)
86
+
87
+ return results
88
+
89
+ return ConcurrentExecutor()
90
+
91
+
92
+ @pytest.fixture
93
+ def thread_synchronizer():
94
+ """
95
+ Helper for synchronizing test threads.
96
+
97
+ Returns:
98
+ Thread synchronization helper.
99
+ """
100
+
101
+ class ThreadSynchronizer:
102
+ def __init__(self):
103
+ self.checkpoints = {}
104
+
105
+ def checkpoint(self, name: str, thread_id: int | None = None):
106
+ """
107
+ Record that a thread reached a checkpoint.
108
+
109
+ Args:
110
+ name: Checkpoint name
111
+ thread_id: Optional thread ID (uses current if None)
112
+ """
113
+ thread_id = thread_id or threading.get_ident()
114
+ if name not in self.checkpoints:
115
+ self.checkpoints[name] = []
116
+ self.checkpoints[name].append((thread_id, time.time()))
117
+
118
+ def wait_for_checkpoint(self, name: str, count: int, timeout: float = 5.0) -> bool:
119
+ """
120
+ Wait for N threads to reach a checkpoint.
121
+
122
+ Args:
123
+ name: Checkpoint name
124
+ count: Number of threads to wait for
125
+ timeout: Maximum wait time
126
+
127
+ Returns:
128
+ True if checkpoint reached, False if timeout
129
+ """
130
+ start = time.time()
131
+ while time.time() - start < timeout:
132
+ if name in self.checkpoints and len(self.checkpoints[name]) >= count:
133
+ return True
134
+ time.sleep(0.01)
135
+ return False
136
+
137
+ def get_order(self, checkpoint: str) -> list[int]:
138
+ """
139
+ Get order in which threads reached checkpoint.
140
+
141
+ Args:
142
+ checkpoint: Checkpoint name
143
+
144
+ Returns:
145
+ List of thread IDs in order
146
+ """
147
+ if checkpoint not in self.checkpoints:
148
+ return []
149
+ return [tid for tid, _ in sorted(self.checkpoints[checkpoint], key=lambda x: x[1])]
150
+
151
+ def clear(self):
152
+ """Clear all checkpoints."""
153
+ self.checkpoints.clear()
154
+
155
+ return ThreadSynchronizer()
156
+
157
+
158
+ @pytest.fixture
159
+ def deadlock_detector():
160
+ """
161
+ Helper for detecting potential deadlocks in tests.
162
+
163
+ Returns:
164
+ Deadlock detection helper.
165
+ """
166
+
167
+ class DeadlockDetector:
168
+ def __init__(self):
169
+ self.locks_held = {} # thread_id -> set of locks
170
+ self.lock = threading.Lock()
171
+
172
+ def acquire(self, lock_name: str, thread_id: int | None = None):
173
+ """Record lock acquisition."""
174
+ thread_id = thread_id or threading.get_ident()
175
+ with self.lock:
176
+ if thread_id not in self.locks_held:
177
+ self.locks_held[thread_id] = set()
178
+ self.locks_held[thread_id].add(lock_name)
179
+
180
+ def release(self, lock_name: str, thread_id: int | None = None):
181
+ """Record lock release."""
182
+ thread_id = thread_id or threading.get_ident()
183
+ with self.lock:
184
+ if thread_id in self.locks_held:
185
+ self.locks_held[thread_id].discard(lock_name)
186
+
187
+ def check_circular_wait(self) -> bool:
188
+ """
189
+ Check for potential circular wait conditions.
190
+
191
+ Returns:
192
+ True if potential deadlock detected
193
+ """
194
+ # Simplified check - in practice would need wait-for graph
195
+ with self.lock:
196
+ # Check if multiple threads hold multiple locks
197
+ multi_lock_threads = [tid for tid, locks in self.locks_held.items() if len(locks) > 1]
198
+ return len(multi_lock_threads) > 1
199
+
200
+ def get_held_locks(self) -> dict[int, set[str]]:
201
+ """Get current lock holdings."""
202
+ with self.lock:
203
+ return self.locks_held.copy()
204
+
205
+ return DeadlockDetector()
206
+
207
+
208
+ @pytest.fixture
209
+ def thread_exception_handler():
210
+ """
211
+ Capture exceptions from threads for testing.
212
+
213
+ Returns:
214
+ Exception handler for threads.
215
+ """
216
+
217
+ class ThreadExceptionHandler:
218
+ def __init__(self):
219
+ self.exceptions = []
220
+ self.lock = threading.Lock()
221
+
222
+ def handle(self, func: Callable) -> Callable:
223
+ """
224
+ Wrap function to capture exceptions.
225
+
226
+ Args:
227
+ func: Function to wrap
228
+
229
+ Returns:
230
+ Wrapped function
231
+ """
232
+
233
+ def wrapper(*args, **kwargs):
234
+ try:
235
+ return func(*args, **kwargs)
236
+ except Exception as e:
237
+ with self.lock:
238
+ self.exceptions.append(
239
+ {
240
+ "thread": threading.current_thread().name,
241
+ "exception": e,
242
+ "time": time.time(),
243
+ }
244
+ )
245
+ raise
246
+
247
+ return wrapper
248
+
249
+ def get_exceptions(self) -> list[dict]:
250
+ """Get all captured exceptions."""
251
+ with self.lock:
252
+ return self.exceptions.copy()
253
+
254
+ def assert_no_exceptions(self):
255
+ """Assert no exceptions were raised."""
256
+ with self.lock:
257
+ if self.exceptions:
258
+ raise AssertionError(f"Thread exceptions occurred: {self.exceptions}")
259
+
260
+ return ThreadExceptionHandler()
261
+
262
+
263
+ __all__ = [
264
+ "concurrent_executor",
265
+ "deadlock_detector",
266
+ "thread_exception_handler",
267
+ "thread_synchronizer",
268
+ ]
@@ -0,0 +1,50 @@
1
+ """
2
+ Threading Test Fixtures and Utilities.
3
+
4
+ Core threading fixtures with re-exports from specialized modules.
5
+ Fixtures for testing multi-threaded code, thread synchronization,
6
+ and concurrent operations across the provide-io ecosystem.
7
+ """
8
+
9
+ # Re-export all fixtures from specialized modules
10
+ from provide.testkit.threading.basic_fixtures import (
11
+ mock_thread,
12
+ test_thread,
13
+ thread_local_storage,
14
+ thread_pool,
15
+ )
16
+ from provide.testkit.threading.data_fixtures import (
17
+ thread_safe_counter,
18
+ thread_safe_list,
19
+ )
20
+ from provide.testkit.threading.execution_fixtures import (
21
+ concurrent_executor,
22
+ deadlock_detector,
23
+ thread_exception_handler,
24
+ thread_synchronizer,
25
+ )
26
+ from provide.testkit.threading.sync_fixtures import (
27
+ thread_barrier,
28
+ thread_condition,
29
+ thread_event,
30
+ )
31
+
32
+ __all__ = [
33
+ # Basic threading fixtures
34
+ "test_thread",
35
+ "thread_pool",
36
+ "mock_thread",
37
+ "thread_local_storage",
38
+ # Synchronization fixtures
39
+ "thread_barrier",
40
+ "thread_event",
41
+ "thread_condition",
42
+ # Thread-safe data structures
43
+ "thread_safe_list",
44
+ "thread_safe_counter",
45
+ # Execution and testing helpers
46
+ "concurrent_executor",
47
+ "thread_synchronizer",
48
+ "deadlock_detector",
49
+ "thread_exception_handler",
50
+ ]
@@ -0,0 +1,98 @@
1
+ """
2
+ Thread synchronization test fixtures.
3
+
4
+ Fixtures for thread barriers, events, conditions, and other synchronization primitives.
5
+ """
6
+
7
+ import threading
8
+
9
+ import pytest
10
+
11
+
12
+ @pytest.fixture
13
+ def thread_barrier():
14
+ """
15
+ Create a barrier for thread synchronization.
16
+
17
+ Returns:
18
+ Function to create barriers for N threads.
19
+ """
20
+ barriers = []
21
+
22
+ def _create_barrier(n_threads: int, timeout: float | None = None) -> threading.Barrier:
23
+ """
24
+ Create a barrier for synchronizing threads.
25
+
26
+ Args:
27
+ n_threads: Number of threads to synchronize
28
+ timeout: Optional timeout for barrier
29
+
30
+ Returns:
31
+ Barrier instance
32
+ """
33
+ barrier = threading.Barrier(n_threads, timeout=timeout)
34
+ barriers.append(barrier)
35
+ return barrier
36
+
37
+ yield _create_barrier
38
+
39
+ # Cleanup: abort all barriers
40
+ for barrier in barriers:
41
+ try:
42
+ barrier.abort()
43
+ except threading.BrokenBarrierError:
44
+ pass
45
+
46
+
47
+ @pytest.fixture
48
+ def thread_event():
49
+ """
50
+ Create thread events for signaling.
51
+
52
+ Returns:
53
+ Function to create thread events.
54
+ """
55
+ events = []
56
+
57
+ def _create_event() -> threading.Event:
58
+ """Create a thread event."""
59
+ event = threading.Event()
60
+ events.append(event)
61
+ return event
62
+
63
+ yield _create_event
64
+
65
+ # Cleanup: set all events to release waiting threads
66
+ for event in events:
67
+ event.set()
68
+
69
+
70
+ @pytest.fixture
71
+ def thread_condition():
72
+ """
73
+ Create condition variables for thread coordination.
74
+
75
+ Returns:
76
+ Function to create condition variables.
77
+ """
78
+
79
+ def _create_condition(lock: threading.Lock | None = None) -> threading.Condition:
80
+ """
81
+ Create a condition variable.
82
+
83
+ Args:
84
+ lock: Optional lock to use (creates new if None)
85
+
86
+ Returns:
87
+ Condition variable
88
+ """
89
+ return threading.Condition(lock)
90
+
91
+ return _create_condition
92
+
93
+
94
+ __all__ = [
95
+ "thread_barrier",
96
+ "thread_condition",
97
+ "thread_event",
98
+ ]
@@ -0,0 +1,32 @@
1
+ """
2
+ Time testing utilities for the provide-io ecosystem.
3
+
4
+ Fixtures and utilities for mocking time, freezing time, and testing
5
+ time-dependent code across any project that depends on provide.foundation.
6
+ """
7
+
8
+ from provide.testkit.time.fixtures import (
9
+ advance_time,
10
+ benchmark_timer,
11
+ freeze_time,
12
+ mock_datetime,
13
+ mock_sleep,
14
+ mock_sleep_with_callback,
15
+ rate_limiter_mock,
16
+ time_machine,
17
+ time_travel,
18
+ timer,
19
+ )
20
+
21
+ __all__ = [
22
+ "advance_time",
23
+ "benchmark_timer",
24
+ "freeze_time",
25
+ "mock_datetime",
26
+ "mock_sleep",
27
+ "mock_sleep_with_callback",
28
+ "rate_limiter_mock",
29
+ "time_machine",
30
+ "time_travel",
31
+ "timer",
32
+ ]