approval-utilities 15.3.2.dev3__tar.gz → 15.3.2.dev5__tar.gz
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.
Potentially problematic release.
This version of approval-utilities might be problematic. Click here for more details.
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/PKG-INFO +10 -5
- approval_utilities-15.3.2.dev5/approval_utilities/utilities/persistence/__init__.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities.egg-info/PKG-INFO +11 -6
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities.egg-info/SOURCES.txt +28 -1
- approval_utilities-15.3.2.dev5/tests/test_approvaltests_temp_dir.py +8 -0
- approval_utilities-15.3.2.dev5/tests/test_asserts.py +68 -0
- approval_utilities-15.3.2.dev5/tests/test_build.py +33 -0
- approval_utilities-15.3.2.dev5/tests/test_combinations.py +175 -0
- approval_utilities-15.3.2.dev5/tests/test_compare_file_lists.py +30 -0
- approval_utilities-15.3.2.dev5/tests/test_custom_printers.py +41 -0
- approval_utilities-15.3.2.dev5/tests/test_example_numpy.py +60 -0
- approval_utilities-15.3.2.dev5/tests/test_exception_utils.py +22 -0
- approval_utilities-15.3.2.dev5/tests/test_fileapprover.py +114 -0
- approval_utilities-15.3.2.dev5/tests/test_find_stale_approved_files.py +136 -0
- approval_utilities-15.3.2.dev5/tests/test_inline_approvals.py +263 -0
- approval_utilities-15.3.2.dev5/tests/test_list.py +40 -0
- approval_utilities-15.3.2.dev5/tests/test_multiline_string_utils.py +35 -0
- approval_utilities-15.3.2.dev5/tests/test_namer.py +70 -0
- approval_utilities-15.3.2.dev5/tests/test_options.py +118 -0
- approval_utilities-15.3.2.dev5/tests/test_pairwise_combinations.py +108 -0
- approval_utilities-15.3.2.dev5/tests/test_parse_inputs.py +147 -0
- approval_utilities-15.3.2.dev5/tests/test_python_patterns.py +20 -0
- approval_utilities-15.3.2.dev5/tests/test_scenarios.py +73 -0
- approval_utilities-15.3.2.dev5/tests/test_simple_logger.py +266 -0
- approval_utilities-15.3.2.dev5/tests/test_split_code.py +67 -0
- approval_utilities-15.3.2.dev5/tests/test_verify.py +341 -0
- approval_utilities-15.3.2.dev5/tests/test_verify_all.py +10 -0
- approval_utilities-15.3.2.dev5/tests/test_verify_argument_parser.py +14 -0
- approval_utilities-15.3.2.dev5/tests/test_workflow_matrix.py +40 -0
- approval_utilities-15.3.2.dev5/tests/test_writer.py +31 -0
- approval_utilities-15.3.2.dev5/version.py +1 -0
- approval_utilities-15.3.2.dev3/version.py +0 -1
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/LICENSE +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/MANIFEST.in +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/README.md +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/__init__.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/approvaltests/__init__.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/approvaltests/core/__init__.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/approvaltests/core/executable_command.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/approvaltests/core/verifiable.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/approvaltests/core/verify_parameters.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/list_utils.py +0 -0
- /approval_utilities-15.3.2.dev3/approval_utilities/utilities/__init__.py → /approval_utilities-15.3.2.dev5/approval_utilities/py.typed +0 -0
- {approval_utilities-15.3.2.dev3/approval_utilities/utilities/exceptions → approval_utilities-15.3.2.dev5/approval_utilities/utilities}/__init__.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/clipboard_utilities.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/deprecated.py +0 -0
- {approval_utilities-15.3.2.dev3/approval_utilities/utilities/logger → approval_utilities-15.3.2.dev5/approval_utilities/utilities/exceptions}/__init__.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/exceptions/exception_collector.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/exceptions/exception_utils.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/exceptions/multiple_exceptions.py +0 -0
- {approval_utilities-15.3.2.dev3/approval_utilities/utilities/persistence → approval_utilities-15.3.2.dev5/approval_utilities/utilities/logger}/__init__.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/logger/logging_instance.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/logger/simple_logger.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/map_reduce.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/markdown_table.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/multiline_string_utils.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/os_utilities.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/persistence/loader.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/persistence/saver.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/stack_frame_utilities.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/string_wrapper.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/time_utilities.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utilities/wrapper.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities/utils.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities.egg-info/dependency_links.txt +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/approval_utilities.egg-info/top_level.txt +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/requirements.prod.extras.txt +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/requirements.prod.required.txt +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/requirements.prod.txt +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/setup/setup_utils.py +0 -0
- {approval_utilities-15.3.2.dev3 → approval_utilities-15.3.2.dev5}/setup.cfg +0 -0
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
Metadata-Version:
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: approval_utilities
|
|
3
|
-
Version: 15.3.2.
|
|
3
|
+
Version: 15.3.2.dev5
|
|
4
4
|
Summary: Utilities for your production code that work well with approvaltests
|
|
5
5
|
Home-page: https://github.com/approvals/ApprovalTests.Python
|
|
6
6
|
Author: ApprovalTests Contributors
|
|
7
7
|
Author-email: llewellyn.falco@gmail.com
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Description: UNKNOWN
|
|
10
|
-
Platform: UNKNOWN
|
|
11
8
|
Classifier: Development Status :: 4 - Beta
|
|
12
9
|
Classifier: Intended Audience :: Developers
|
|
13
10
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -23,3 +20,11 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
23
20
|
Classifier: Topic :: Software Development :: Libraries
|
|
24
21
|
Classifier: Topic :: Utilities
|
|
25
22
|
Requires-Python: >=3.8
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: author
|
|
25
|
+
Dynamic: author-email
|
|
26
|
+
Dynamic: classifier
|
|
27
|
+
Dynamic: home-page
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
Dynamic: requires-python
|
|
30
|
+
Dynamic: summary
|
|
File without changes
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
Metadata-Version:
|
|
2
|
-
Name:
|
|
3
|
-
Version: 15.3.2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: approval_utilities
|
|
3
|
+
Version: 15.3.2.dev5
|
|
4
4
|
Summary: Utilities for your production code that work well with approvaltests
|
|
5
5
|
Home-page: https://github.com/approvals/ApprovalTests.Python
|
|
6
6
|
Author: ApprovalTests Contributors
|
|
7
7
|
Author-email: llewellyn.falco@gmail.com
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Description: UNKNOWN
|
|
10
|
-
Platform: UNKNOWN
|
|
11
8
|
Classifier: Development Status :: 4 - Beta
|
|
12
9
|
Classifier: Intended Audience :: Developers
|
|
13
10
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -23,3 +20,11 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
23
20
|
Classifier: Topic :: Software Development :: Libraries
|
|
24
21
|
Classifier: Topic :: Utilities
|
|
25
22
|
Requires-Python: >=3.8
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: author
|
|
25
|
+
Dynamic: author-email
|
|
26
|
+
Dynamic: classifier
|
|
27
|
+
Dynamic: home-page
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
Dynamic: requires-python
|
|
30
|
+
Dynamic: summary
|
|
@@ -7,6 +7,7 @@ requirements.prod.txt
|
|
|
7
7
|
version.py
|
|
8
8
|
approval_utilities/__init__.py
|
|
9
9
|
approval_utilities/list_utils.py
|
|
10
|
+
approval_utilities/py.typed
|
|
10
11
|
approval_utilities/utils.py
|
|
11
12
|
approval_utilities.egg-info/PKG-INFO
|
|
12
13
|
approval_utilities.egg-info/SOURCES.txt
|
|
@@ -38,4 +39,30 @@ approval_utilities/utilities/logger/simple_logger.py
|
|
|
38
39
|
approval_utilities/utilities/persistence/__init__.py
|
|
39
40
|
approval_utilities/utilities/persistence/loader.py
|
|
40
41
|
approval_utilities/utilities/persistence/saver.py
|
|
41
|
-
setup/setup_utils.py
|
|
42
|
+
setup/setup_utils.py
|
|
43
|
+
tests/test_approvaltests_temp_dir.py
|
|
44
|
+
tests/test_asserts.py
|
|
45
|
+
tests/test_build.py
|
|
46
|
+
tests/test_combinations.py
|
|
47
|
+
tests/test_compare_file_lists.py
|
|
48
|
+
tests/test_custom_printers.py
|
|
49
|
+
tests/test_example_numpy.py
|
|
50
|
+
tests/test_exception_utils.py
|
|
51
|
+
tests/test_fileapprover.py
|
|
52
|
+
tests/test_find_stale_approved_files.py
|
|
53
|
+
tests/test_inline_approvals.py
|
|
54
|
+
tests/test_list.py
|
|
55
|
+
tests/test_multiline_string_utils.py
|
|
56
|
+
tests/test_namer.py
|
|
57
|
+
tests/test_options.py
|
|
58
|
+
tests/test_pairwise_combinations.py
|
|
59
|
+
tests/test_parse_inputs.py
|
|
60
|
+
tests/test_python_patterns.py
|
|
61
|
+
tests/test_scenarios.py
|
|
62
|
+
tests/test_simple_logger.py
|
|
63
|
+
tests/test_split_code.py
|
|
64
|
+
tests/test_verify.py
|
|
65
|
+
tests/test_verify_all.py
|
|
66
|
+
tests/test_verify_argument_parser.py
|
|
67
|
+
tests/test_workflow_matrix.py
|
|
68
|
+
tests/test_writer.py
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from approvaltests.internals.logs.log_commons import APPROVAL_TESTS_TEMP_DIRECTORY
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_temp_directory_is_setup_automatically() -> None:
|
|
5
|
+
# setup happens automatically
|
|
6
|
+
gitignore_path = APPROVAL_TESTS_TEMP_DIRECTORY / ".gitignore"
|
|
7
|
+
contents = gitignore_path.read_text()
|
|
8
|
+
assert contents == "*"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
7
|
+
from approval_utilities.utils import get_adjacent_file
|
|
8
|
+
from approvaltests import Options, assert_against_file, assert_equal_with_reporter
|
|
9
|
+
from approvaltests.approval_exception import ApprovalException
|
|
10
|
+
from approvaltests.core import Reporter
|
|
11
|
+
from approvaltests.reporters.testing_reporter import ReporterForTesting
|
|
12
|
+
from approvaltests.scrubbers import scrub_all_guids
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TestAssertEqualWithReporter(unittest.TestCase):
|
|
16
|
+
def test_assert_with_scrubbing_and_options(self) -> None:
|
|
17
|
+
actuals = "2fd78d4a-ad49-447d-96a8-deda585a9aa5 and text"
|
|
18
|
+
expected = "<guid_0> and text"
|
|
19
|
+
assert_equal_with_reporter(
|
|
20
|
+
expected,
|
|
21
|
+
actuals,
|
|
22
|
+
options=Options()
|
|
23
|
+
.with_scrubber(scrub_all_guids)
|
|
24
|
+
.for_file.with_extension(".md"),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def test_text_reporter_called_on_failure(self) -> None:
|
|
28
|
+
class LocalReporter(Reporter):
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
self.received: Optional[str] = None
|
|
31
|
+
self.approved: Optional[str] = None
|
|
32
|
+
self.extention: Optional[str] = None
|
|
33
|
+
|
|
34
|
+
@override
|
|
35
|
+
def report(self, received_path: str, approved_path: str) -> bool:
|
|
36
|
+
self.received = Path(received_path).read_text(encoding="utf-8-sig")
|
|
37
|
+
self.approved = Path(approved_path).read_text(encoding="utf-8-sig")
|
|
38
|
+
self.extention = Path(received_path).suffix
|
|
39
|
+
|
|
40
|
+
reporter = LocalReporter()
|
|
41
|
+
try:
|
|
42
|
+
assert_equal_with_reporter(
|
|
43
|
+
"expected",
|
|
44
|
+
"actual",
|
|
45
|
+
options=Options()
|
|
46
|
+
.with_reporter(reporter)
|
|
47
|
+
.for_file.with_extension(".md"),
|
|
48
|
+
)
|
|
49
|
+
except AssertionError:
|
|
50
|
+
pass
|
|
51
|
+
self.assertEqual(reporter.received, "actual")
|
|
52
|
+
self.assertEqual(reporter.approved, "expected")
|
|
53
|
+
self.assertEqual(reporter.extention, ".md")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TestAsserts(unittest.TestCase):
|
|
57
|
+
def test_assert_against_file_works(self) -> None:
|
|
58
|
+
file_path = get_adjacent_file("manual_file.approved.txt")
|
|
59
|
+
assert_against_file("This text is in a file\n", file_path)
|
|
60
|
+
|
|
61
|
+
def test_assert_against_file_fails_with_reporter(self) -> None:
|
|
62
|
+
reporter = ReporterForTesting()
|
|
63
|
+
file_path = get_adjacent_file("manual_file.approved.txt")
|
|
64
|
+
try:
|
|
65
|
+
assert_against_file("This text is NOT in a file", file_path, reporter)
|
|
66
|
+
except ApprovalException:
|
|
67
|
+
pass
|
|
68
|
+
self.assertTrue(reporter.called)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_no_imports_from_build_directory() -> None:
|
|
6
|
+
# Make sure no file imports from build
|
|
7
|
+
root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|
8
|
+
for dirpath, dirnames, filenames in os.walk(root_dir):
|
|
9
|
+
# Exclude directories that start with '.'
|
|
10
|
+
dirnames[:] = [d for d in dirnames if not d.startswith(".") and d != "build"]
|
|
11
|
+
# also exclude venv
|
|
12
|
+
dirnames[:] = [d for d in dirnames if not d.startswith("venv")]
|
|
13
|
+
for filename in filenames:
|
|
14
|
+
# Skip files that start with '.' and non-Python files
|
|
15
|
+
if filename.startswith(".") or not filename.endswith(".py"):
|
|
16
|
+
continue
|
|
17
|
+
file_path = os.path.join(dirpath, filename)
|
|
18
|
+
with open(file_path, "r", encoding="utf-8") as file:
|
|
19
|
+
source = file.read()
|
|
20
|
+
try:
|
|
21
|
+
tree = ast.parse(source, filename=file_path)
|
|
22
|
+
except SyntaxError:
|
|
23
|
+
continue # Skip files with syntax errors
|
|
24
|
+
for node in ast.walk(tree):
|
|
25
|
+
if isinstance(node, ast.Import):
|
|
26
|
+
for alias in node.names:
|
|
27
|
+
if alias.name == "build" or alias.name.startswith("build."):
|
|
28
|
+
assert False, f"{file_path} imports 'build'"
|
|
29
|
+
elif isinstance(node, ast.ImportFrom):
|
|
30
|
+
if node.module == "build" or (
|
|
31
|
+
node.module and node.module.startswith("build.")
|
|
32
|
+
):
|
|
33
|
+
assert False, f"{file_path} imports from 'build'"
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from typing_extensions import override
|
|
4
|
+
|
|
5
|
+
from approvaltests.approval_exception import ApprovalException
|
|
6
|
+
from approvaltests.combination_approvals import (
|
|
7
|
+
verify_all_combinations,
|
|
8
|
+
verify_all_combinations_with_labeled_input,
|
|
9
|
+
verify_all_combinations_with_namer,
|
|
10
|
+
)
|
|
11
|
+
from approvaltests.reporters import CommandLineReporter
|
|
12
|
+
from approvaltests.reporters.testing_reporter import ReporterForTesting
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class VerifyAllCombinationsTests(unittest.TestCase):
|
|
16
|
+
@override
|
|
17
|
+
def setUp(self) -> None:
|
|
18
|
+
self.reporter = None
|
|
19
|
+
self.func = lambda *args: sum(args) + 1
|
|
20
|
+
|
|
21
|
+
def test_fails_for_mismatch_with_for_func_accepting_one_arg_and_combination_of_one_arg(
|
|
22
|
+
self,
|
|
23
|
+
) -> None:
|
|
24
|
+
arg1_combinations = (1,)
|
|
25
|
+
all_args_combinations = (arg1_combinations,)
|
|
26
|
+
with self.assertRaises(ApprovalException):
|
|
27
|
+
verify_all_combinations(
|
|
28
|
+
self.func, all_args_combinations, reporter=ReporterForTesting()
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def test_passes_for_func_accepting_one_arg_and_combination_of_one_arg(self) -> None:
|
|
32
|
+
arg1_combinations = (1,)
|
|
33
|
+
all_args_combinations = (arg1_combinations,)
|
|
34
|
+
verify_all_combinations(
|
|
35
|
+
self.func, all_args_combinations, reporter=self.reporter
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def test_fails_for_mismatch_with_for_func_accepting_one_arg_and_combination_of_two_args(
|
|
39
|
+
self,
|
|
40
|
+
) -> None:
|
|
41
|
+
arg1_combinations = (1, 2)
|
|
42
|
+
all_args_combinations = (arg1_combinations,)
|
|
43
|
+
with self.assertRaises(ApprovalException):
|
|
44
|
+
verify_all_combinations(
|
|
45
|
+
self.func, all_args_combinations, reporter=ReporterForTesting()
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def test_passes_for_func_accepting_one_arg_and_combination_of_two_args(
|
|
49
|
+
self,
|
|
50
|
+
) -> None:
|
|
51
|
+
arg1_combinations = (1, 2)
|
|
52
|
+
all_args_combinations = (arg1_combinations,)
|
|
53
|
+
verify_all_combinations(
|
|
54
|
+
self.func, all_args_combinations, reporter=self.reporter
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def test_fails_for_mismatch_with_for_func_accepting_two_args_and_combination_of_one_arg(
|
|
58
|
+
self,
|
|
59
|
+
) -> None:
|
|
60
|
+
arg1_combinations = (1,)
|
|
61
|
+
arg2_combinations = (2,)
|
|
62
|
+
arg_combinations = (arg1_combinations, arg2_combinations)
|
|
63
|
+
with self.assertRaises(ApprovalException):
|
|
64
|
+
verify_all_combinations(
|
|
65
|
+
self.func, arg_combinations, reporter=ReporterForTesting()
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def test_passes_for_func_accepting_two_args_and_combination_of_one_arg(
|
|
69
|
+
self,
|
|
70
|
+
) -> None:
|
|
71
|
+
arg1_combinations = (1,)
|
|
72
|
+
arg2_combinations = (2,)
|
|
73
|
+
arg_combinations = (arg1_combinations, arg2_combinations)
|
|
74
|
+
verify_all_combinations(self.func, arg_combinations, reporter=self.reporter)
|
|
75
|
+
|
|
76
|
+
def test_fails_for_mismatch_with_for_func_accepting_two_args_and_combination_of_two_args(
|
|
77
|
+
self,
|
|
78
|
+
) -> None:
|
|
79
|
+
arg1_combinations = (1, 3)
|
|
80
|
+
arg2_combinations = (2, 4)
|
|
81
|
+
arg_combinations = (arg1_combinations, arg2_combinations)
|
|
82
|
+
with self.assertRaises(ApprovalException):
|
|
83
|
+
verify_all_combinations(
|
|
84
|
+
self.func, arg_combinations, reporter=ReporterForTesting()
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def test_for_func_accepting_three_args_and_combination_of_three_args(self) -> None:
|
|
88
|
+
arg1_combinations = (1, 2, 3)
|
|
89
|
+
arg2_combinations = (2, 4, 6)
|
|
90
|
+
arg3_combinations = (10, 11, 12)
|
|
91
|
+
arg_combinations = [arg1_combinations, arg2_combinations, arg3_combinations]
|
|
92
|
+
verify_all_combinations(self.func, arg_combinations, reporter=self.reporter)
|
|
93
|
+
|
|
94
|
+
def test_when_arg_combinations_have_equal_lengths(self) -> None:
|
|
95
|
+
arg1_combinations = (1, 3, 5, 7)
|
|
96
|
+
arg2_combinations = (2, 4, 6)
|
|
97
|
+
arg_combinations = (arg1_combinations, arg2_combinations)
|
|
98
|
+
verify_all_combinations(self.func, arg_combinations, reporter=self.reporter)
|
|
99
|
+
|
|
100
|
+
def test_records_exception_message_when_function_under_test_throws_an_exception(
|
|
101
|
+
self,
|
|
102
|
+
) -> None:
|
|
103
|
+
def function_that_raises_exceptions(*args: object) -> None:
|
|
104
|
+
raise Exception(args)
|
|
105
|
+
|
|
106
|
+
arg1_combinations = (1, 3)
|
|
107
|
+
arg2_combinations = (2, 4)
|
|
108
|
+
arg_combinations = (arg1_combinations, arg2_combinations)
|
|
109
|
+
verify_all_combinations(
|
|
110
|
+
function_that_raises_exceptions, arg_combinations, reporter=self.reporter
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
def test_uses_user_specified_formatter_when_supplied(self) -> None:
|
|
114
|
+
arg1_combinations = (1, 3)
|
|
115
|
+
arg2_combinations = (2, 4)
|
|
116
|
+
arg_combinations = (arg1_combinations, arg2_combinations)
|
|
117
|
+
verify_all_combinations(
|
|
118
|
+
self.func,
|
|
119
|
+
arg_combinations,
|
|
120
|
+
formatter=lambda args, output: "inputs="
|
|
121
|
+
+ str(args)
|
|
122
|
+
+ ", outputs="
|
|
123
|
+
+ str(output)
|
|
124
|
+
+ "\n",
|
|
125
|
+
reporter=self.reporter,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
def test_with_labeled_input(self) -> None:
|
|
129
|
+
# begin-snippet: named_combinations
|
|
130
|
+
verify_all_combinations_with_labeled_input(
|
|
131
|
+
self.func,
|
|
132
|
+
arg1=(1, 3),
|
|
133
|
+
arg2=(2, 4),
|
|
134
|
+
)
|
|
135
|
+
# end-snippet
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class VerifyAllCombinationsWithNamerTests(unittest.TestCase):
|
|
139
|
+
@override
|
|
140
|
+
def setUp(self) -> None:
|
|
141
|
+
self.reporter = CommandLineReporter()
|
|
142
|
+
self.func = lambda *args: sum(args) + 1
|
|
143
|
+
|
|
144
|
+
def test_passes_for_func_accepting_one_arg_and_combination_of_one_arg(self) -> None:
|
|
145
|
+
arg1_combinations = (1,)
|
|
146
|
+
all_args_combinations = (arg1_combinations,)
|
|
147
|
+
verify_all_combinations_with_namer(
|
|
148
|
+
self.func, all_args_combinations, reporter=self.reporter
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def test_example_for_combinations() -> None:
|
|
153
|
+
# begin-snippet: combination_introduction
|
|
154
|
+
verify_all_combinations(is_awake, [["Monday", "Sunday"], ["7:00", "9:00", "11:00"]])
|
|
155
|
+
# end-snippet
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_starting_snippet() -> None:
|
|
159
|
+
# begin-snippet: combinations_starting_point
|
|
160
|
+
inputs1 = ["input1.value1", "input1.value2"]
|
|
161
|
+
inputs2 = ["input2.value1", "input2.value2", "input2.value3"]
|
|
162
|
+
verify_all_combinations(lambda a, b: "placeholder", [inputs1, inputs2])
|
|
163
|
+
# end-snippet
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def is_awake(day: str, time) -> str:
|
|
167
|
+
weekdays = ["Monday"]
|
|
168
|
+
is_weekday = day in weekdays
|
|
169
|
+
time = int(time.replace(":00", ""))
|
|
170
|
+
if is_weekday and time > 8:
|
|
171
|
+
return "Yes"
|
|
172
|
+
elif not is_weekday and time < 10:
|
|
173
|
+
return "No"
|
|
174
|
+
else:
|
|
175
|
+
return "Maybe"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def compare_log_and_file_system(
|
|
5
|
+
log_file_names: List[str], fs_file_names: List[str]
|
|
6
|
+
) -> List[str]:
|
|
7
|
+
return [file for file in fs_file_names if file not in log_file_names]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_returns_empty_list_for_two_empty_lists() -> None:
|
|
11
|
+
log_file_names: List[str] = []
|
|
12
|
+
fs_file_names: List[str] = []
|
|
13
|
+
assert compare_log_and_file_system(log_file_names, fs_file_names) == []
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_returns_empty_list_for_identical_lists() -> None:
|
|
17
|
+
log_file_names = ["a", "b", "c"]
|
|
18
|
+
fs_file_names = ["a", "b", "c"]
|
|
19
|
+
assert compare_log_and_file_system(log_file_names, fs_file_names) == []
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_returns_items_only_in_fs() -> None:
|
|
23
|
+
log_file_names = ["a", "b", "c"]
|
|
24
|
+
fs_file_names = ["a", "b", "c", "d", "e"]
|
|
25
|
+
assert compare_log_and_file_system(log_file_names, fs_file_names) == ["d", "e"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Future tests
|
|
29
|
+
# 1 - fs:[a,b,c], log:[a,b,c] => []
|
|
30
|
+
# 2 - fs:[a,b,c,d], log:[a,b,c] => [d]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from typing_extensions import override
|
|
5
|
+
|
|
6
|
+
from approvaltests import (
|
|
7
|
+
approvals,
|
|
8
|
+
find_formatter_for_specified_class,
|
|
9
|
+
register_formatter,
|
|
10
|
+
verify,
|
|
11
|
+
)
|
|
12
|
+
from approvaltests.core.format_wrapper import FormatWrapper
|
|
13
|
+
from approvaltests.verifiable_objects.formatter_of_argparse_namespace import (
|
|
14
|
+
FormatterOfArgparseNamespace,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_argparse_namespace() -> None:
|
|
19
|
+
approvals.settings().allow_multiple_verify_calls_for_this_method()
|
|
20
|
+
args = argparse.ArgumentParser()
|
|
21
|
+
args.add_argument("foo")
|
|
22
|
+
args.add_argument("--foo2")
|
|
23
|
+
result = args.parse_args(["bar", "--foo2=bar2"])
|
|
24
|
+
verify(FormatterOfArgparseNamespace(result))
|
|
25
|
+
verify(result)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_register_formatter() -> None:
|
|
29
|
+
class ExampleFormatterWrapper(FormatWrapper):
|
|
30
|
+
@override
|
|
31
|
+
def is_match(self, data: typing.Any) -> bool:
|
|
32
|
+
return True
|
|
33
|
+
|
|
34
|
+
@override
|
|
35
|
+
def wrap(self, data: typing.Any) -> typing.Any:
|
|
36
|
+
return 42
|
|
37
|
+
|
|
38
|
+
with register_formatter(ExampleFormatterWrapper()):
|
|
39
|
+
assert 42 == find_formatter_for_specified_class("Some Result")
|
|
40
|
+
verify("Some Result")
|
|
41
|
+
assert "Some Result" == find_formatter_for_specified_class("Some Result")
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import io
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
7
|
+
from approvaltests import Options, verify_binary
|
|
8
|
+
from approvaltests.core import Reporter
|
|
9
|
+
from approvaltests.reporters import get_command_text
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def serialize_ndarray(a: np.ndarray) -> bytes:
|
|
13
|
+
bs = io.BytesIO()
|
|
14
|
+
np.save(bs, a)
|
|
15
|
+
bs.seek(0)
|
|
16
|
+
return bs.read()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# begin-snippet: verify_numpy_array
|
|
20
|
+
def test_simulator_produces_correct_output() -> None:
|
|
21
|
+
np_array = np.full(shape=(32, 16), fill_value=42, dtype=np.int64)
|
|
22
|
+
verify_binary(
|
|
23
|
+
serialize_ndarray(np_array),
|
|
24
|
+
".npy",
|
|
25
|
+
options=Options().with_reporter(NDArrayDiffReporter()),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# end-snippet
|
|
30
|
+
|
|
31
|
+
# Use a custom reporter to show the assertion message from numpy
|
|
32
|
+
# Also the default python console reporter will try to interpret the numpy data as text causing an error
|
|
33
|
+
|
|
34
|
+
# begin-snippet: numpy_custom_reporter
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load_ndarray(path: str):
|
|
38
|
+
with open(path, mode="rb") as f:
|
|
39
|
+
return np.load(f)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class NDArrayDiffReporter(Reporter):
|
|
43
|
+
@override
|
|
44
|
+
def report(self, received_path: str, approved_path: str) -> bool:
|
|
45
|
+
if not Path(approved_path).is_file():
|
|
46
|
+
self._create_empty_array(approved_path)
|
|
47
|
+
received = load_ndarray(received_path)
|
|
48
|
+
approved = load_ndarray(approved_path)
|
|
49
|
+
to_approve_msg = (
|
|
50
|
+
f"To approve run:\n {get_command_text(received_path, approved_path)}"
|
|
51
|
+
)
|
|
52
|
+
print(np.testing.build_err_msg([received, approved], err_msg=to_approve_msg))
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
# end-snippet
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def _create_empty_array(path: str) -> None:
|
|
59
|
+
with open(path, mode="wb") as f:
|
|
60
|
+
f.write(serialize_ndarray(np.zeros((0,))))
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from approval_utilities.utilities.exceptions.exception_collector import (
|
|
2
|
+
ExceptionCollector,
|
|
3
|
+
gather_all_exceptions,
|
|
4
|
+
)
|
|
5
|
+
from approvaltests import verify_exception
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def is_odd(integer: int) -> None:
|
|
9
|
+
if integer % 2 != 0:
|
|
10
|
+
raise ValueError(f"{integer} is not odd!")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_gather_all_exceptions() -> None:
|
|
14
|
+
collector = ExceptionCollector()
|
|
15
|
+
for i in range(1, 6):
|
|
16
|
+
collector.gather(lambda: is_odd(i))
|
|
17
|
+
|
|
18
|
+
verify_exception(lambda: collector.release())
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_gather_all_exceptions_from_list() -> None:
|
|
22
|
+
verify_exception(lambda: gather_all_exceptions(range(1, 6), is_odd).release())
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
from approvaltests import Options, approvals, get_default_namer, verify, verify_file
|
|
5
|
+
from approvaltests.file_approver import FileApprover
|
|
6
|
+
from approvaltests.internals.logs.approved_file_log import ApprovedFilesLog
|
|
7
|
+
from approvaltests.internals.logs.failed_comparison_log import FailedComparisonLog
|
|
8
|
+
from approvaltests.reporters.generic_diff_reporter_factory import (
|
|
9
|
+
GenericDiffReporterFactory,
|
|
10
|
+
)
|
|
11
|
+
from approvaltests.reporters.report_quietly import ReportQuietly
|
|
12
|
+
from approvaltests.reporters.testing_reporter import ReporterForTesting
|
|
13
|
+
from approvaltests.string_writer import StringWriter
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FileApproverTests(unittest.TestCase):
|
|
17
|
+
@staticmethod
|
|
18
|
+
def test_compare_same_files() -> None:
|
|
19
|
+
writer = StringWriter("a")
|
|
20
|
+
writer.write_received_file("a.txt")
|
|
21
|
+
shutil.copy("a.txt", "a_same.txt")
|
|
22
|
+
FileApprover.verify_files("a.txt", "a_same.txt", None, Options().comparator)
|
|
23
|
+
|
|
24
|
+
def test_compare_different_files(self) -> None:
|
|
25
|
+
reporter = ReporterForTesting()
|
|
26
|
+
FileApprover.verify_files("a.txt", "b.txt", reporter, Options().comparator)
|
|
27
|
+
self.assertTrue(reporter.called)
|
|
28
|
+
|
|
29
|
+
def test_scrubbed_files(self) -> None:
|
|
30
|
+
verify_file("a.txt", options=Options().with_scrubber(lambda t: "<scrubbed>"))
|
|
31
|
+
|
|
32
|
+
def test_full(self) -> None:
|
|
33
|
+
namer = get_default_namer()
|
|
34
|
+
writer = StringWriter("b")
|
|
35
|
+
reporter = ReporterForTesting()
|
|
36
|
+
FileApprover.verify(namer, writer, reporter, Options().comparator)
|
|
37
|
+
self.assertTrue(reporter.called)
|
|
38
|
+
|
|
39
|
+
def test_returns_error_when_files_are_different(self) -> None:
|
|
40
|
+
approvals.settings().allow_multiple_verify_calls_for_this_method()
|
|
41
|
+
namer = get_default_namer()
|
|
42
|
+
writer = StringWriter("b")
|
|
43
|
+
reporter = ReporterForTesting()
|
|
44
|
+
error = FileApprover.verify(namer, writer, reporter, Options().comparator)
|
|
45
|
+
import re
|
|
46
|
+
|
|
47
|
+
replaced = re.sub("ved: .*approved_files.", "ved: <rootdir>/", error)
|
|
48
|
+
|
|
49
|
+
verify(replaced)
|
|
50
|
+
|
|
51
|
+
def test_returns_none_when_files_are_same_files(self) -> None:
|
|
52
|
+
namer = get_default_namer()
|
|
53
|
+
writer = StringWriter("b")
|
|
54
|
+
reporter = GenericDiffReporterFactory().get_first_working()
|
|
55
|
+
error = FileApprover.verify(namer, writer, reporter, Options().comparator)
|
|
56
|
+
self.assertEqual(None, error)
|
|
57
|
+
|
|
58
|
+
def test_approved_file_is_logged(self) -> None:
|
|
59
|
+
name = approvals.get_default_namer().get_approved_filename()
|
|
60
|
+
name1 = name.replace(".txt", ".txt1")
|
|
61
|
+
name2 = name.replace(".txt", ".txt2")
|
|
62
|
+
|
|
63
|
+
log = ApprovedFilesLog.get_approved_files_log()
|
|
64
|
+
log_lines = log.read_text().split("\n")
|
|
65
|
+
|
|
66
|
+
# check log is cleared
|
|
67
|
+
self.assertNotIn(name1, log_lines)
|
|
68
|
+
self.assertNotIn(name2, log_lines)
|
|
69
|
+
|
|
70
|
+
# touch approved file
|
|
71
|
+
verify("a", options=Options().for_file.with_extension(".txt1"))
|
|
72
|
+
verify("a", options=Options().for_file.with_extension(".txt2"))
|
|
73
|
+
|
|
74
|
+
# assert that the approved file is logged
|
|
75
|
+
log_lines = log.read_text().split("\n")
|
|
76
|
+
self.assertIn(name1, log_lines)
|
|
77
|
+
self.assertIn(name2, log_lines)
|
|
78
|
+
|
|
79
|
+
def test_failed_comparison_is_logged(self) -> None:
|
|
80
|
+
approved_name = approvals.get_default_namer().get_approved_filename()
|
|
81
|
+
received_name = approvals.get_default_namer().get_received_filename()
|
|
82
|
+
expected_line = f"{received_name} -> {approved_name}"
|
|
83
|
+
|
|
84
|
+
name1 = expected_line.replace(".txt", ".txt1")
|
|
85
|
+
name2 = expected_line.replace(".txt", ".txt2")
|
|
86
|
+
|
|
87
|
+
log = FailedComparisonLog.get_failed_comparison_log()
|
|
88
|
+
|
|
89
|
+
log_lines = log.read_text().split("\n")
|
|
90
|
+
|
|
91
|
+
# check log is cleared
|
|
92
|
+
self.assertNotIn(name1, log_lines)
|
|
93
|
+
self.assertNotIn(name2, log_lines)
|
|
94
|
+
|
|
95
|
+
# fail a verify and log it
|
|
96
|
+
self.run_a_failing_test(".txt1")
|
|
97
|
+
self.run_a_failing_test(".txt2")
|
|
98
|
+
|
|
99
|
+
# assert that the approved file is logged
|
|
100
|
+
log_lines = log.read_text().split("\n")
|
|
101
|
+
self.assertIn(name1, log_lines)
|
|
102
|
+
self.assertIn(name2, log_lines)
|
|
103
|
+
|
|
104
|
+
def run_a_failing_test(self, extension: str) -> None:
|
|
105
|
+
try:
|
|
106
|
+
verify(
|
|
107
|
+
"a",
|
|
108
|
+
options=Options()
|
|
109
|
+
.for_file.with_extension(extension)
|
|
110
|
+
.with_reporter(ReportQuietly()),
|
|
111
|
+
)
|
|
112
|
+
self.fail("expected to fail")
|
|
113
|
+
except:
|
|
114
|
+
pass
|