hunterMakesPy 0.1.1__py3-none-any.whl → 0.2.0__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.
- hunterMakesPy/__init__.py +8 -5
- hunterMakesPy/_theSSOT.py +3 -39
- hunterMakesPy/coping.py +82 -6
- hunterMakesPy/dataStructures.py +129 -126
- hunterMakesPy/parseParameters.py +29 -29
- hunterMakesPy/pytestForYourUse.py +5 -322
- hunterMakesPy/tests/__init__.py +5 -0
- {tests → hunterMakesPy/tests}/conftest.py +1 -2
- hunterMakesPy/tests/test_coping.py +216 -0
- {tests → hunterMakesPy/tests}/test_dataStructures.py +124 -118
- {tests → hunterMakesPy/tests}/test_filesystemToolkit.py +5 -2
- hunterMakesPy/tests/test_parseParameters.py +339 -0
- {huntermakespy-0.1.1.dist-info → huntermakespy-0.2.0.dist-info}/METADATA +12 -12
- huntermakespy-0.2.0.dist-info/RECORD +20 -0
- {huntermakespy-0.1.1.dist-info → huntermakespy-0.2.0.dist-info}/top_level.txt +0 -1
- huntermakespy-0.1.1.dist-info/RECORD +0 -20
- tests/__init__.py +0 -0
- tests/test_coping.py +0 -56
- tests/test_parseParameters.py +0 -21
- {huntermakespy-0.1.1.dist-info → huntermakespy-0.2.0.dist-info}/WHEEL +0 -0
- {huntermakespy-0.1.1.dist-info → huntermakespy-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,327 +1,10 @@
|
|
|
1
1
|
"""Pytest tests you can use in your package to test some hunterMakesPy functions.
|
|
2
2
|
|
|
3
3
|
Each function in this module returns a list of test functions that can be used with `pytest.parametrize`.
|
|
4
|
-
"""
|
|
5
|
-
from collections.abc import Callable, Iterable, Iterator
|
|
6
|
-
from hunterMakesPy import defineConcurrencyLimit, intInnit, oopsieKwargsie
|
|
7
|
-
from typing import Any, NoReturn
|
|
8
|
-
from unittest.mock import Mock, patch
|
|
9
|
-
import pytest
|
|
10
|
-
|
|
11
|
-
def PytestFor_defineConcurrencyLimit(callableToTest: Callable[..., int] = defineConcurrencyLimit, cpuCount: int = 8) -> list[tuple[str, Callable[[], None]]]: # noqa: C901
|
|
12
|
-
"""Return a list of test functions to validate concurrency limit behavior.
|
|
13
|
-
|
|
14
|
-
This function provides a comprehensive test suite for validating concurrency limit parsing
|
|
15
|
-
and computation, checking both valid and invalid input scenarios.
|
|
16
|
-
|
|
17
|
-
Parameters
|
|
18
|
-
----------
|
|
19
|
-
callableToTest : Callable[[bool | float | int | None, int], int] = defineConcurrencyLimit
|
|
20
|
-
The function to test, which should accept various input types and return an integer
|
|
21
|
-
representing the concurrency limit.
|
|
22
|
-
cpuCount : int = 8
|
|
23
|
-
The number of CPUs to simulate in the test environment.
|
|
24
|
-
|
|
25
|
-
Returns
|
|
26
|
-
-------
|
|
27
|
-
listOfTestFunctions : list[tuple[str, Callable[[], None]]]
|
|
28
|
-
A list of tuples, each containing a string describing the test case and a callable
|
|
29
|
-
test function that implements the test case.
|
|
30
|
-
|
|
31
|
-
Examples
|
|
32
|
-
--------
|
|
33
|
-
Run each test on `hunterMakesPy.defineConcurrencyLimit`:
|
|
34
|
-
```python
|
|
35
|
-
from hunterMakesPy.pytestForYourUse import PytestFor_defineConcurrencyLimit
|
|
36
|
-
|
|
37
|
-
listOfTests = PytestFor_defineConcurrencyLimit()
|
|
38
|
-
for nameOfTest, callablePytest in listOfTests:
|
|
39
|
-
callablePytest()
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Or, run each test on your function that has a compatible signature:
|
|
43
|
-
```python
|
|
44
|
-
from hunterMakesPy.pytestForYourUse import PytestFor_defineConcurrencyLimit
|
|
45
|
-
from packageLocal import functionLocal
|
|
46
|
-
|
|
47
|
-
@pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_defineConcurrencyLimit(callableToTest=functionLocal))
|
|
48
|
-
def test_functionLocal(nameOfTest, callablePytest):
|
|
49
|
-
callablePytest()
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
"""
|
|
53
|
-
@patch('multiprocessing.cpu_count', return_value=cpuCount)
|
|
54
|
-
def testDefaults(_mockCpu: Mock) -> None:
|
|
55
|
-
listOfParameters: list[bool | int | None] = [None, False, 0]
|
|
56
|
-
for limitParameter in listOfParameters:
|
|
57
|
-
assert callableToTest(limit=limitParameter, cpuTotal=cpuCount) == cpuCount
|
|
58
|
-
|
|
59
|
-
@patch('multiprocessing.cpu_count', return_value=cpuCount)
|
|
60
|
-
def testDirectIntegers(_mockCpu: Mock) -> None:
|
|
61
|
-
for limitParameter in [1, 4, 16]:
|
|
62
|
-
assert callableToTest(limit=limitParameter, cpuTotal=cpuCount) == limitParameter
|
|
63
|
-
|
|
64
|
-
@patch('multiprocessing.cpu_count', return_value=cpuCount)
|
|
65
|
-
def testFractionalFloats(_mockCpu: Mock) -> None:
|
|
66
|
-
testCases: dict[float, int] = {
|
|
67
|
-
0.5: cpuCount // 2,
|
|
68
|
-
0.25: cpuCount // 4,
|
|
69
|
-
0.75: int(cpuCount * 0.75)
|
|
70
|
-
}
|
|
71
|
-
for limit, expected in testCases.items():
|
|
72
|
-
assert callableToTest(limit=limit, cpuTotal=cpuCount) == expected
|
|
73
|
-
|
|
74
|
-
@patch('multiprocessing.cpu_count', return_value=cpuCount)
|
|
75
|
-
def testMinimumOne(_mockCpu: Mock) -> None:
|
|
76
|
-
listOfParameters: list[float | int] = [-10, -0.99, 0.1]
|
|
77
|
-
for limitParameter in listOfParameters:
|
|
78
|
-
assert callableToTest(limit=limitParameter, cpuTotal=cpuCount) >= 1
|
|
79
|
-
|
|
80
|
-
@patch('multiprocessing.cpu_count', return_value=cpuCount)
|
|
81
|
-
def testBooleanTrue(_mockCpu: Mock) -> None:
|
|
82
|
-
assert callableToTest(limit=True, cpuTotal=cpuCount) == 1
|
|
83
|
-
assert callableToTest(limit='True', cpuTotal=cpuCount) == 1 # pyright: ignore[reportArgumentType]
|
|
84
|
-
assert callableToTest(limit='TRUE', cpuTotal=cpuCount) == 1 # pyright: ignore[reportArgumentType]
|
|
85
|
-
assert callableToTest(limit=' true ', cpuTotal=cpuCount) == 1 # pyright: ignore[reportArgumentType]
|
|
86
|
-
|
|
87
|
-
@patch('multiprocessing.cpu_count', return_value=cpuCount)
|
|
88
|
-
def testInvalidStrings(_mockCpu: Mock) -> None:
|
|
89
|
-
for stringInput in ["invalid", "True but not quite", "None of the above"]:
|
|
90
|
-
with pytest.raises(ValueError, match="must be a number, `True`, `False`, or `None`"):
|
|
91
|
-
callableToTest(limit=stringInput, cpuTotal=cpuCount) # pyright: ignore[reportArgumentType]
|
|
92
|
-
|
|
93
|
-
@patch('multiprocessing.cpu_count', return_value=cpuCount)
|
|
94
|
-
def testStringNumbers(_mockCpu: Mock) -> None:
|
|
95
|
-
testCases: list[tuple[str, int]] = [
|
|
96
|
-
("1.51", 2),
|
|
97
|
-
("-2.51", 5),
|
|
98
|
-
("4", 4),
|
|
99
|
-
("0.5", 4),
|
|
100
|
-
("-0.25", 6),
|
|
101
|
-
]
|
|
102
|
-
for stringNumber, expectedLimit in testCases:
|
|
103
|
-
assert callableToTest(limit=stringNumber, cpuTotal=cpuCount) == expectedLimit # pyright: ignore[reportArgumentType]
|
|
104
|
-
|
|
105
|
-
return [
|
|
106
|
-
('testDefaults', testDefaults),
|
|
107
|
-
('testDirectIntegers', testDirectIntegers),
|
|
108
|
-
('testFractionalFloats', testFractionalFloats),
|
|
109
|
-
('testMinimumOne', testMinimumOne),
|
|
110
|
-
('testBooleanTrue', testBooleanTrue),
|
|
111
|
-
('testInvalidStrings', testInvalidStrings),
|
|
112
|
-
('testStringNumbers', testStringNumbers)
|
|
113
|
-
]
|
|
114
|
-
|
|
115
|
-
def PytestFor_intInnit(callableToTest: Callable[[Iterable[int], str | None, type[Any] | None], list[int]] = intInnit) -> list[tuple[str, Callable[[], None]]]: # noqa: C901
|
|
116
|
-
"""Return a list of test functions to validate integer initialization behavior.
|
|
117
|
-
|
|
118
|
-
This function provides a comprehensive test suite for validating integer parsing
|
|
119
|
-
and initialization, checking both valid and invalid input scenarios.
|
|
120
|
-
|
|
121
|
-
Parameters
|
|
122
|
-
----------
|
|
123
|
-
callableToTest : Callable[[Iterable[int], str | None, type[Any] | None], list[int]] = intInnit
|
|
124
|
-
The function to test. Should accept a sequence of integer-compatible values,
|
|
125
|
-
an optional parameter name string, and an optional parameter type.
|
|
126
|
-
Returns a list of validated integers.
|
|
127
|
-
|
|
128
|
-
Returns
|
|
129
|
-
-------
|
|
130
|
-
listOfTestFunctions : list[tuple[str, Callable[[], None]]]
|
|
131
|
-
A list of tuples containing a string describing the test case and a callable
|
|
132
|
-
test function implementing the test case.
|
|
133
|
-
|
|
134
|
-
Examples
|
|
135
|
-
--------
|
|
136
|
-
Run tests on `hunterMakesPy.intInnit`:
|
|
137
|
-
```python
|
|
138
|
-
from hunterMakesPy.pytestForYourUse import PytestFor_intInnit
|
|
139
|
-
|
|
140
|
-
listOfTests = PytestFor_intInnit()
|
|
141
|
-
for nameOfTest, callablePytest in listOfTests:
|
|
142
|
-
callablePytest()
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
Run tests on your compatible function:
|
|
146
|
-
```python
|
|
147
|
-
from hunterMakesPy.pytestForYourUse import PytestFor_intInnit
|
|
148
|
-
from packageLocal import functionLocal
|
|
149
|
-
|
|
150
|
-
@pytest.mark.parametrize("nameOfTest,callablePytest",
|
|
151
|
-
PytestFor_intInnit(callableToTest=functionLocal))
|
|
152
|
-
def test_functionLocal(nameOfTest, callablePytest):
|
|
153
|
-
callablePytest()
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
"""
|
|
157
|
-
def testHandlesValidIntegers() -> None:
|
|
158
|
-
assert callableToTest([2, 3, 5, 8], 'test', None) == [2, 3, 5, 8]
|
|
159
|
-
assert callableToTest([13.0, 21.0, 34.0], 'test', None) == [13, 21, 34] # pyright: ignore[reportArgumentType]
|
|
160
|
-
assert callableToTest(['55', '89', '144'], 'test', None) == [55, 89, 144] # pyright: ignore[reportArgumentType]
|
|
161
|
-
assert callableToTest([' 233 ', '377', '-610'], 'test', None) == [233, 377, -610] # pyright: ignore[reportArgumentType]
|
|
162
|
-
|
|
163
|
-
def testRejectsNonWholeNumbers() -> None:
|
|
164
|
-
listInvalidNumbers: list[float] = [13.7, 21.5, 34.8, -55.9]
|
|
165
|
-
for invalidNumber in listInvalidNumbers:
|
|
166
|
-
with pytest.raises(ValueError):
|
|
167
|
-
callableToTest([invalidNumber], 'test', None) # pyright: ignore[reportArgumentType]
|
|
168
|
-
|
|
169
|
-
def testRejectsBooleans() -> None:
|
|
170
|
-
with pytest.raises(TypeError):
|
|
171
|
-
callableToTest([True, False], 'test', None)
|
|
172
4
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
with pytest.raises(ValueError):
|
|
176
|
-
callableToTest([invalidString], 'test', None) # pyright: ignore[reportArgumentType]
|
|
177
|
-
|
|
178
|
-
def testRejectsEmptyList() -> None:
|
|
179
|
-
with pytest.raises(ValueError):
|
|
180
|
-
callableToTest([], 'test', None)
|
|
181
|
-
|
|
182
|
-
def testHandlesMixedValidTypes() -> None:
|
|
183
|
-
assert callableToTest([13, '21', 34.0], 'test', None) == [13, 21, 34] # pyright: ignore[reportArgumentType]
|
|
184
|
-
|
|
185
|
-
def testHandlesBytes() -> None:
|
|
186
|
-
validCases: list[tuple[list[bytes], str, list[int]]] = [
|
|
187
|
-
([b'123'], '123', [123]),
|
|
188
|
-
]
|
|
189
|
-
for inputData, testName, expected in validCases:
|
|
190
|
-
assert callableToTest(inputData, testName, None) == expected # pyright: ignore[reportArgumentType]
|
|
191
|
-
|
|
192
|
-
extendedCases: list[tuple[list[bytes], str, list[int]]] = [
|
|
193
|
-
([b'123456789'], '123456789', [123456789]),
|
|
194
|
-
]
|
|
195
|
-
for inputData, testName, expected in extendedCases:
|
|
196
|
-
assert callableToTest(inputData, testName, None) == expected # pyright: ignore[reportArgumentType]
|
|
197
|
-
|
|
198
|
-
invalidCases: list[list[bytes]] = [[b'\x00']]
|
|
199
|
-
for inputData in invalidCases:
|
|
200
|
-
with pytest.raises(ValueError):
|
|
201
|
-
callableToTest(inputData, 'test', None) # pyright: ignore[reportArgumentType]
|
|
202
|
-
|
|
203
|
-
def testHandlesMemoryview() -> None:
|
|
204
|
-
validCases: list[tuple[list[memoryview], str, list[int]]] = [
|
|
205
|
-
([memoryview(b'123')], '123', [123]),
|
|
206
|
-
]
|
|
207
|
-
for inputData, testName, expected in validCases:
|
|
208
|
-
assert callableToTest(inputData, testName, None) == expected # pyright: ignore[reportArgumentType]
|
|
209
|
-
|
|
210
|
-
largeMemoryviewCase: list[memoryview] = [memoryview(b'9999999999')]
|
|
211
|
-
assert callableToTest(largeMemoryviewCase, 'test', None) == [9999999999] # pyright: ignore[reportArgumentType]
|
|
212
|
-
|
|
213
|
-
invalidMemoryviewCases: list[list[memoryview]] = [[memoryview(b'\x00')]]
|
|
214
|
-
for inputData in invalidMemoryviewCases:
|
|
215
|
-
with pytest.raises(ValueError):
|
|
216
|
-
callableToTest(inputData, 'test', None) # pyright: ignore[reportArgumentType]
|
|
217
|
-
|
|
218
|
-
def testRejectsMutableSequence() -> None:
|
|
219
|
-
class MutableList(list[int]):
|
|
220
|
-
def __iter__(self) -> Iterator[int]:
|
|
221
|
-
self.append(89)
|
|
222
|
-
return super().__iter__()
|
|
223
|
-
with pytest.raises(RuntimeError, match=".*modified during iteration.*"):
|
|
224
|
-
callableToTest(MutableList([13, 21, 34]), 'test', None)
|
|
225
|
-
|
|
226
|
-
def testHandlesComplexIntegers() -> None:
|
|
227
|
-
testCases: list[tuple[list[complex], list[int]]] = [
|
|
228
|
-
([13+0j], [13]),
|
|
229
|
-
([21+0j, 34+0j], [21, 34])
|
|
230
|
-
]
|
|
231
|
-
for inputData, expected in testCases:
|
|
232
|
-
assert callableToTest(inputData, 'test', None) == expected # pyright: ignore[reportArgumentType]
|
|
233
|
-
|
|
234
|
-
def testRejectsInvalidComplex() -> None:
|
|
235
|
-
for invalidComplex in [13+1j, 21+0.5j, 34.5+0j]:
|
|
236
|
-
with pytest.raises(ValueError):
|
|
237
|
-
callableToTest([invalidComplex], 'test', None) # pyright: ignore[reportArgumentType]
|
|
238
|
-
|
|
239
|
-
return [
|
|
240
|
-
('testHandlesValidIntegers', testHandlesValidIntegers),
|
|
241
|
-
('testRejectsNonWholeNumbers', testRejectsNonWholeNumbers),
|
|
242
|
-
('testRejectsBooleans', testRejectsBooleans),
|
|
243
|
-
('testRejectsInvalidStrings', testRejectsInvalidStrings),
|
|
244
|
-
('testRejectsEmptyList', testRejectsEmptyList),
|
|
245
|
-
('testHandlesMixedValidTypes', testHandlesMixedValidTypes),
|
|
246
|
-
('testHandlesBytes', testHandlesBytes),
|
|
247
|
-
('testHandlesMemoryview', testHandlesMemoryview),
|
|
248
|
-
('testRejectsMutableSequence', testRejectsMutableSequence),
|
|
249
|
-
('testHandlesComplexIntegers', testHandlesComplexIntegers),
|
|
250
|
-
('testRejectsInvalidComplex', testRejectsInvalidComplex)
|
|
251
|
-
]
|
|
252
|
-
|
|
253
|
-
def PytestFor_oopsieKwargsie(callableToTest: Callable[[str], bool | None | str] = oopsieKwargsie) -> list[tuple[str, Callable[[], None]]]: # noqa: C901
|
|
254
|
-
"""Return a list of test functions to validate string-to-boolean/None conversion behavior.
|
|
255
|
-
|
|
256
|
-
This function provides a comprehensive test suite for validating string parsing and conversion
|
|
257
|
-
to boolean or None values, with fallback to the original string when appropriate.
|
|
258
|
-
|
|
259
|
-
Parameters
|
|
260
|
-
----------
|
|
261
|
-
callableToTest : Callable[[str], bool | None | str] = oopsieKwargsie
|
|
262
|
-
The function to test, which should accept a string and return either a boolean, None,
|
|
263
|
-
or the original input.
|
|
264
|
-
|
|
265
|
-
Returns
|
|
266
|
-
-------
|
|
267
|
-
listOfTestFunctions : list[tuple[str, Callable[[], None]]]
|
|
268
|
-
A list of tuples, each containing a string describing the test case and a callable
|
|
269
|
-
test function that implements the test case.
|
|
270
|
-
|
|
271
|
-
Examples
|
|
272
|
-
--------
|
|
273
|
-
Run each test on `hunterMakesPy.oopsieKwargsie`:
|
|
274
|
-
```python
|
|
275
|
-
from hunterMakesPy.pytestForYourUse import PytestFor_oopsieKwargsie
|
|
276
|
-
|
|
277
|
-
listOfTests = PytestFor_oopsieKwargsie()
|
|
278
|
-
for nameOfTest, callablePytest in listOfTests:
|
|
279
|
-
callablePytest()
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
Or, run each test on your function that has a compatible signature:
|
|
283
|
-
```python
|
|
284
|
-
from hunterMakesPy.pytestForYourUse import PytestFor_oopsieKwargsie
|
|
285
|
-
from packageLocal import functionLocal
|
|
286
|
-
|
|
287
|
-
@pytest.mark.parametrize("nameOfTest,callablePytest", PytestFor_oopsieKwargsie(callableToTest=functionLocal))
|
|
288
|
-
def test_functionLocal(nameOfTest, callablePytest):
|
|
289
|
-
callablePytest()
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
"""
|
|
293
|
-
def testHandlesTrueVariants() -> None:
|
|
294
|
-
for variantTrue in ['True', 'TRUE', ' true ', 'TrUe']:
|
|
295
|
-
assert callableToTest(variantTrue) is True
|
|
296
|
-
|
|
297
|
-
def testHandlesFalseVariants() -> None:
|
|
298
|
-
for variantFalse in ['False', 'FALSE', ' false ', 'FaLsE']:
|
|
299
|
-
assert callableToTest(variantFalse) is False
|
|
300
|
-
|
|
301
|
-
def testHandlesNoneVariants() -> None:
|
|
302
|
-
for variantNone in ['None', 'NONE', ' none ', 'NoNe']:
|
|
303
|
-
assert callableToTest(variantNone) is None
|
|
304
|
-
|
|
305
|
-
def testReturnsOriginalString() -> None:
|
|
306
|
-
for stringInput in ['hello', '123', 'True story', 'False alarm']:
|
|
307
|
-
assert callableToTest(stringInput) == stringInput
|
|
308
|
-
|
|
309
|
-
def testHandlesNonStringObjects() -> None:
|
|
310
|
-
class NeverGonnaStringIt:
|
|
311
|
-
def __str__(self) -> NoReturn:
|
|
312
|
-
message = "Cannot be stringified"
|
|
313
|
-
raise TypeError(message)
|
|
314
|
-
|
|
315
|
-
assert callableToTest(123) == "123" # pyright: ignore[reportArgumentType]
|
|
316
|
-
|
|
317
|
-
neverGonnaStringIt = NeverGonnaStringIt()
|
|
318
|
-
result = callableToTest(neverGonnaStringIt) # pyright: ignore[reportArgumentType]
|
|
319
|
-
assert result is neverGonnaStringIt
|
|
5
|
+
Note: These test functions are now in `hunterMakesPy.tests` with all other tests.
|
|
6
|
+
"""
|
|
320
7
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
('testHandlesNoneVariants', testHandlesNoneVariants),
|
|
325
|
-
('testReturnsOriginalString', testReturnsOriginalString),
|
|
326
|
-
('testHandlesNonStringObjects', testHandlesNonStringObjects)
|
|
327
|
-
]
|
|
8
|
+
from hunterMakesPy.tests.test_parseParameters import (
|
|
9
|
+
PytestFor_defineConcurrencyLimit as PytestFor_defineConcurrencyLimit, PytestFor_intInnit as PytestFor_intInnit,
|
|
10
|
+
PytestFor_oopsieKwargsie as PytestFor_oopsieKwargsie)
|
|
@@ -5,8 +5,7 @@ import pathlib
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
7
|
# SSOT for test data paths and filenames
|
|
8
|
-
|
|
9
|
-
pathDataSamples = pathlib.Path("tests/dataSamples")
|
|
8
|
+
pathDataSamples = pathlib.Path("hunterMakesPy/tests/dataSamples")
|
|
10
9
|
|
|
11
10
|
# Fixture to provide a temporary directory for filesystem tests
|
|
12
11
|
@pytest.fixture
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
from hunterMakesPy import PackageSettings, raiseIfNone
|
|
2
|
+
from hunterMakesPy.coping import getIdentifierPackagePACKAGING, getPathPackageINSTALLING
|
|
3
|
+
from hunterMakesPy.tests.conftest import uniformTestFailureMessage
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
@pytest.mark.parametrize(
|
|
8
|
+
"returnTarget, expected",
|
|
9
|
+
[
|
|
10
|
+
(13, 13),
|
|
11
|
+
(17, 17),
|
|
12
|
+
("fibonacci", "fibonacci"),
|
|
13
|
+
("prime", "prime"),
|
|
14
|
+
([], []),
|
|
15
|
+
({}, {}),
|
|
16
|
+
(False, False),
|
|
17
|
+
(0, 0),
|
|
18
|
+
]
|
|
19
|
+
)
|
|
20
|
+
def testRaiseIfNoneReturnsNonNoneValues(returnTarget: object, expected: object) -> None:
|
|
21
|
+
actual = raiseIfNone(returnTarget)
|
|
22
|
+
assert actual == expected, uniformTestFailureMessage(expected, actual, "testRaiseIfNoneReturnsNonNoneValues", returnTarget)
|
|
23
|
+
assert actual is returnTarget, uniformTestFailureMessage(returnTarget, actual, "testRaiseIfNoneReturnsNonNoneValues identity check", returnTarget)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def testRaiseIfNoneRaisesValueErrorWhenGivenNone() -> None:
|
|
27
|
+
with pytest.raises(ValueError, match="A function unexpectedly returned `None`. Hint: look at the traceback immediately before `raiseIfNone`."):
|
|
28
|
+
raiseIfNone(None)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.parametrize(
|
|
32
|
+
"customMessage",
|
|
33
|
+
[
|
|
34
|
+
"Configuration must include 'host' setting",
|
|
35
|
+
"Database connection failed",
|
|
36
|
+
"User input is required",
|
|
37
|
+
"Network request returned empty response",
|
|
38
|
+
]
|
|
39
|
+
)
|
|
40
|
+
def testRaiseIfNoneRaisesValueErrorWithCustomMessage(customMessage: str) -> None:
|
|
41
|
+
with pytest.raises(ValueError, match=customMessage):
|
|
42
|
+
raiseIfNone(None, customMessage)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def testRaiseIfNoneWithEmptyStringMessage() -> None:
|
|
46
|
+
with pytest.raises(ValueError, match="A function unexpectedly returned `None`. Hint: look at the traceback immediately before `raiseIfNone`."):
|
|
47
|
+
raiseIfNone(None, "")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def testRaiseIfNonePreservesTypeAnnotations() -> None:
|
|
51
|
+
integerValue: int = raiseIfNone(23)
|
|
52
|
+
assert isinstance(integerValue, int), uniformTestFailureMessage(int, type(integerValue), "testRaiseIfNonePreservesTypeAnnotations", integerValue)
|
|
53
|
+
|
|
54
|
+
stringValue: str = raiseIfNone("cardinal")
|
|
55
|
+
assert isinstance(stringValue, str), uniformTestFailureMessage(str, type(stringValue), "testRaiseIfNonePreservesTypeAnnotations", stringValue)
|
|
56
|
+
|
|
57
|
+
listValue: list[int] = raiseIfNone([29, 31])
|
|
58
|
+
assert isinstance(listValue, list), uniformTestFailureMessage(list, type(listValue), "testRaiseIfNonePreservesTypeAnnotations", listValue)
|
|
59
|
+
|
|
60
|
+
# Tests for PackageSettings dataclass
|
|
61
|
+
@pytest.mark.parametrize(
|
|
62
|
+
"identifierPackageFALLBACK, expectedIdentifierPackage",
|
|
63
|
+
[
|
|
64
|
+
("astToolFactory", "hunterMakesPy"), # Should read from pyproject.toml
|
|
65
|
+
("nonExistentPackage", "hunterMakesPy"), # Should read from pyproject.toml
|
|
66
|
+
("customPackage", "hunterMakesPy"), # Should read from pyproject.toml
|
|
67
|
+
]
|
|
68
|
+
)
|
|
69
|
+
def testPackageSettingsWithFallbackUsesProjectToml(identifierPackageFALLBACK: str, expectedIdentifierPackage: str) -> None:
|
|
70
|
+
"""Test that PackageSettings reads package name from pyproject.toml when using fallback."""
|
|
71
|
+
packageSettings = PackageSettings(identifierPackageFALLBACK)
|
|
72
|
+
assert packageSettings.identifierPackage == expectedIdentifierPackage, uniformTestFailureMessage(
|
|
73
|
+
expectedIdentifierPackage, packageSettings.identifierPackage, "PackageSettings fallback", identifierPackageFALLBACK
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
@pytest.mark.parametrize(
|
|
77
|
+
"explicitIdentifierPackage, expectedIdentifierPackage",
|
|
78
|
+
[
|
|
79
|
+
("customPackageName", "customPackageName"),
|
|
80
|
+
("fibonacci", "fibonacci"),
|
|
81
|
+
("prime", "prime"),
|
|
82
|
+
("astToolFactory", "astToolFactory"),
|
|
83
|
+
]
|
|
84
|
+
)
|
|
85
|
+
def testPackageSettingsWithExplicitIdentifierPackage(explicitIdentifierPackage: str, expectedIdentifierPackage: str) -> None:
|
|
86
|
+
"""Test that PackageSettings respects explicitly provided identifierPackage."""
|
|
87
|
+
packageSettings = PackageSettings(identifierPackage=explicitIdentifierPackage)
|
|
88
|
+
assert packageSettings.identifierPackage == expectedIdentifierPackage, uniformTestFailureMessage(
|
|
89
|
+
expectedIdentifierPackage, packageSettings.identifierPackage, "PackageSettings explicit identifierPackage", explicitIdentifierPackage
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
@pytest.mark.parametrize(
|
|
93
|
+
"explicitPathPackage, expectedPathPackage",
|
|
94
|
+
[
|
|
95
|
+
(Path("C:/fibonacci/path"), Path("C:/fibonacci/path")),
|
|
96
|
+
(Path("C:/prime/directory"), Path("C:/prime/directory")),
|
|
97
|
+
(Path("/usr/local/lib/package"), Path("/usr/local/lib/package")),
|
|
98
|
+
(Path("relative/path"), Path("relative/path")),
|
|
99
|
+
]
|
|
100
|
+
)
|
|
101
|
+
def testPackageSettingsWithExplicitPathPackage(explicitPathPackage: Path, expectedPathPackage: Path) -> None:
|
|
102
|
+
"""Test that PackageSettings respects explicitly provided pathPackage."""
|
|
103
|
+
packageSettings = PackageSettings(pathPackage=explicitPathPackage)
|
|
104
|
+
assert packageSettings.pathPackage == expectedPathPackage, uniformTestFailureMessage(
|
|
105
|
+
expectedPathPackage, packageSettings.pathPackage, "PackageSettings explicit pathPackage", explicitPathPackage
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
@pytest.mark.parametrize(
|
|
109
|
+
"fileExtension, expectedFileExtension",
|
|
110
|
+
[
|
|
111
|
+
(".fibonacci", ".fibonacci"),
|
|
112
|
+
(".prime", ".prime"),
|
|
113
|
+
(".txt", ".txt"),
|
|
114
|
+
(".md", ".md"),
|
|
115
|
+
(".json", ".json"),
|
|
116
|
+
]
|
|
117
|
+
)
|
|
118
|
+
def testPackageSettingsWithCustomFileExtension(fileExtension: str, expectedFileExtension: str) -> None:
|
|
119
|
+
"""Test that PackageSettings respects custom file extensions."""
|
|
120
|
+
packageSettings = PackageSettings(fileExtension=fileExtension)
|
|
121
|
+
assert packageSettings.fileExtension == expectedFileExtension, uniformTestFailureMessage(
|
|
122
|
+
expectedFileExtension, packageSettings.fileExtension, "PackageSettings custom fileExtension", fileExtension
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def testPackageSettingsDefaultValues() -> None:
|
|
126
|
+
"""Test that PackageSettings has correct default values when no arguments provided."""
|
|
127
|
+
packageSettings = PackageSettings()
|
|
128
|
+
|
|
129
|
+
# Should have default file extension
|
|
130
|
+
assert packageSettings.fileExtension == '.py', uniformTestFailureMessage(
|
|
131
|
+
'.py', packageSettings.fileExtension, "PackageSettings default fileExtension"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# identifierPackage should be empty when no fallback provided
|
|
135
|
+
assert packageSettings.identifierPackage == '', uniformTestFailureMessage(
|
|
136
|
+
'', packageSettings.identifierPackage, "PackageSettings default identifierPackage"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# pathPackage should remain as Path() when identifierPackage is empty
|
|
140
|
+
expectedPath = Path()
|
|
141
|
+
assert packageSettings.pathPackage == expectedPath, uniformTestFailureMessage(
|
|
142
|
+
expectedPath, packageSettings.pathPackage, "PackageSettings default pathPackage"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
@pytest.mark.parametrize(
|
|
146
|
+
"identifierPackageFALLBACK, identifierPackage, pathPackage, fileExtension",
|
|
147
|
+
[
|
|
148
|
+
("fallback", "custom", Path("C:/custom/path"), ".txt"),
|
|
149
|
+
("fibonacci", "prime", Path("C:/fibonacci/lib"), ".md"),
|
|
150
|
+
("defaultFallback", "overridePackage", Path("/usr/local/override"), ".json"),
|
|
151
|
+
]
|
|
152
|
+
)
|
|
153
|
+
def testPackageSettingsAllParametersCombined(
|
|
154
|
+
identifierPackageFALLBACK: str,
|
|
155
|
+
identifierPackage: str,
|
|
156
|
+
pathPackage: Path,
|
|
157
|
+
fileExtension: str
|
|
158
|
+
) -> None:
|
|
159
|
+
"""Test PackageSettings with all parameters provided."""
|
|
160
|
+
packageSettings = PackageSettings(
|
|
161
|
+
identifierPackageFALLBACK,
|
|
162
|
+
identifierPackage=identifierPackage,
|
|
163
|
+
pathPackage=pathPackage,
|
|
164
|
+
fileExtension=fileExtension
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
assert packageSettings.identifierPackage == identifierPackage, uniformTestFailureMessage(
|
|
168
|
+
identifierPackage, packageSettings.identifierPackage, "PackageSettings combined identifierPackage"
|
|
169
|
+
)
|
|
170
|
+
assert packageSettings.pathPackage == pathPackage, uniformTestFailureMessage(
|
|
171
|
+
pathPackage, packageSettings.pathPackage, "PackageSettings combined pathPackage"
|
|
172
|
+
)
|
|
173
|
+
assert packageSettings.fileExtension == fileExtension, uniformTestFailureMessage(
|
|
174
|
+
fileExtension, packageSettings.fileExtension, "PackageSettings combined fileExtension"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def testPackageSettingsFallbackIgnoredWhenExplicitIdentifierProvided() -> None:
|
|
178
|
+
"""Test that fallback is ignored when explicit identifierPackage is provided."""
|
|
179
|
+
packageSettings = PackageSettings("shouldBeIgnored", identifierPackage="explicit")
|
|
180
|
+
assert packageSettings.identifierPackage == "explicit", uniformTestFailureMessage(
|
|
181
|
+
"explicit", packageSettings.identifierPackage, "PackageSettings fallback ignored"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Tests for helper functions
|
|
185
|
+
@pytest.mark.parametrize(
|
|
186
|
+
"identifierPackageFALLBACK, expectedResult",
|
|
187
|
+
[
|
|
188
|
+
("fibonacci", "hunterMakesPy"), # Should read from pyproject.toml
|
|
189
|
+
("prime", "hunterMakesPy"), # Should read from pyproject.toml
|
|
190
|
+
("nonExistentPackage", "hunterMakesPy"), # Should read from pyproject.toml
|
|
191
|
+
]
|
|
192
|
+
)
|
|
193
|
+
def testGetIdentifierPackagePACKAGING(identifierPackageFALLBACK: str, expectedResult: str) -> None:
|
|
194
|
+
"""Test that getIdentifierPackagePACKAGING reads from pyproject.toml correctly."""
|
|
195
|
+
actual = getIdentifierPackagePACKAGING(identifierPackageFALLBACK)
|
|
196
|
+
assert actual == expectedResult, uniformTestFailureMessage(
|
|
197
|
+
expectedResult, actual, "getIdentifierPackagePACKAGING", identifierPackageFALLBACK
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
@pytest.mark.parametrize(
|
|
201
|
+
"identifierPackage",
|
|
202
|
+
[
|
|
203
|
+
"hunterMakesPy", # This package exists
|
|
204
|
+
"fibonacci", # Non-existent package should fallback to cwd
|
|
205
|
+
"prime", # Non-existent package should fallback to cwd
|
|
206
|
+
]
|
|
207
|
+
)
|
|
208
|
+
def testGetPathPackageINSTALLING(identifierPackage: str) -> None:
|
|
209
|
+
"""Test that getPathPackageINSTALLING returns valid Path objects."""
|
|
210
|
+
actual = getPathPackageINSTALLING(identifierPackage)
|
|
211
|
+
assert isinstance(actual, Path), uniformTestFailureMessage(
|
|
212
|
+
Path, type(actual), "getPathPackageINSTALLING type", identifierPackage
|
|
213
|
+
)
|
|
214
|
+
assert actual.exists() or actual == Path.cwd(), uniformTestFailureMessage(
|
|
215
|
+
"existing path or cwd", actual, "getPathPackageINSTALLING existence", identifierPackage
|
|
216
|
+
)
|