edq-utils 0.0.4__py3-none-any.whl → 0.0.6__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.
Potentially problematic release.
This version of edq-utils might be problematic. Click here for more details.
- edq/__init__.py +1 -1
- edq/cli/config/__init__.py +3 -0
- edq/cli/config/list.py +69 -0
- edq/cli/http/__init__.py +3 -0
- edq/cli/http/exchange-server.py +71 -0
- edq/cli/http/send-exchange.py +45 -0
- edq/cli/http/verify-exchanges.py +38 -0
- edq/cli/testing/__init__.py +3 -0
- edq/cli/testing/cli-test.py +12 -5
- edq/cli/version.py +2 -1
- edq/core/argparser.py +28 -3
- edq/core/config.py +268 -0
- edq/core/config_test.py +1038 -0
- edq/procedure/verify_exchanges.py +85 -0
- edq/testing/asserts.py +0 -1
- edq/testing/cli.py +107 -29
- edq/testing/cli_test.py +8 -1
- edq/testing/httpserver.py +553 -0
- edq/testing/httpserver_test.py +424 -0
- edq/testing/run.py +40 -10
- edq/testing/testdata/cli/data/configs/empty/edq-config.json +1 -0
- edq/testing/testdata/cli/data/configs/simple-1/edq-config.json +4 -0
- edq/testing/testdata/cli/data/configs/simple-2/edq-config.json +3 -0
- edq/testing/testdata/cli/data/configs/value-number/edq-config.json +3 -0
- edq/testing/testdata/cli/tests/config/list/config_list_base.txt +16 -0
- edq/testing/testdata/cli/tests/config/list/config_list_config_value_number.txt +10 -0
- edq/testing/testdata/cli/tests/config/list/config_list_ignore_config.txt +14 -0
- edq/testing/testdata/cli/tests/config/list/config_list_no_config.txt +8 -0
- edq/testing/testdata/cli/tests/config/list/config_list_show_origin.txt +13 -0
- edq/testing/testdata/cli/tests/config/list/config_list_skip_header.txt +10 -0
- edq/testing/testdata/cli/tests/platform_skip.txt +5 -0
- edq/testing/testdata/http/exchanges/simple.httpex.json +5 -0
- edq/testing/testdata/http/exchanges/simple_anchor.httpex.json +5 -0
- edq/testing/testdata/http/exchanges/simple_file.httpex.json +10 -0
- edq/testing/testdata/http/exchanges/simple_file_binary.httpex.json +10 -0
- edq/testing/testdata/http/exchanges/simple_file_get_params.httpex.json +14 -0
- edq/testing/testdata/http/exchanges/simple_file_multiple.httpex.json +13 -0
- edq/testing/testdata/http/exchanges/simple_file_name.httpex.json +11 -0
- edq/testing/testdata/http/exchanges/simple_file_post_multiple.httpex.json +13 -0
- edq/testing/testdata/http/exchanges/simple_file_post_params.httpex.json +14 -0
- edq/testing/testdata/http/exchanges/simple_headers.httpex.json +8 -0
- edq/testing/testdata/http/exchanges/simple_jsonresponse_dict.httpex.json +7 -0
- edq/testing/testdata/http/exchanges/simple_jsonresponse_list.httpex.json +9 -0
- edq/testing/testdata/http/exchanges/simple_params.httpex.json +9 -0
- edq/testing/testdata/http/exchanges/simple_post.httpex.json +5 -0
- edq/testing/testdata/http/exchanges/simple_post_params.httpex.json +9 -0
- edq/testing/testdata/http/exchanges/simple_post_urlparams.httpex.json +5 -0
- edq/testing/testdata/http/exchanges/simple_urlparams.httpex.json +5 -0
- edq/testing/testdata/http/exchanges/specialcase_listparams_explicit.httpex.json +8 -0
- edq/testing/testdata/http/exchanges/specialcase_listparams_url.httpex.json +5 -0
- edq/testing/testdata/http/files/tiny.png +0 -0
- edq/testing/unittest.py +26 -6
- edq/util/dirent.py +2 -0
- edq/util/dirent_test.py +43 -32
- edq/util/json.py +21 -4
- edq/util/net.py +894 -0
- edq_utils-0.0.6.dist-info/METADATA +156 -0
- edq_utils-0.0.6.dist-info/RECORD +78 -0
- edq/util/testdata/dirent-operations/dir_1/b.txt +0 -1
- edq/util/testdata/dirent-operations/dir_1/dir_2/c.txt +0 -1
- edq/util/testdata/dirent-operations/symlink_a.txt +0 -1
- edq/util/testdata/dirent-operations/symlink_dir_1/b.txt +0 -1
- edq/util/testdata/dirent-operations/symlink_dir_1/dir_2/c.txt +0 -1
- edq/util/testdata/dirent-operations/symlink_file_empty +0 -0
- edq_utils-0.0.4.dist-info/METADATA +0 -63
- edq_utils-0.0.4.dist-info/RECORD +0 -41
- /edq/{util/testdata/dirent-operations/file_empty → procedure/__init__.py} +0 -0
- /edq/{util/testdata/dirent-operations → testing/testdata/http/files}/a.txt +0 -0
- {edq_utils-0.0.4.dist-info → edq_utils-0.0.6.dist-info}/WHEEL +0 -0
- {edq_utils-0.0.4.dist-info → edq_utils-0.0.6.dist-info}/licenses/LICENSE +0 -0
- {edq_utils-0.0.4.dist-info → edq_utils-0.0.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Verify that exchanges sent to a given server have the same response.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import glob
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
import typing
|
|
9
|
+
import unittest
|
|
10
|
+
|
|
11
|
+
import edq.testing.unittest
|
|
12
|
+
import edq.util.net
|
|
13
|
+
|
|
14
|
+
class ExchangeVerification(edq.testing.unittest.BaseTest):
|
|
15
|
+
""" Verify that exchanges match their content. """
|
|
16
|
+
|
|
17
|
+
def run(paths: typing.List[str], server: str) -> int:
|
|
18
|
+
""" Run exchange verification. """
|
|
19
|
+
|
|
20
|
+
exchange_paths = _collect_exchange_paths(paths)
|
|
21
|
+
|
|
22
|
+
_attach_tests(exchange_paths, server)
|
|
23
|
+
|
|
24
|
+
runner = unittest.TextTestRunner(verbosity = 2)
|
|
25
|
+
tests = unittest.defaultTestLoader.loadTestsFromTestCase(ExchangeVerification)
|
|
26
|
+
results = runner.run(tests)
|
|
27
|
+
|
|
28
|
+
return len(results.errors) + len(results.failures)
|
|
29
|
+
|
|
30
|
+
def _attach_tests(
|
|
31
|
+
paths: typing.List[str],
|
|
32
|
+
server: str,
|
|
33
|
+
extension: str = edq.util.net.DEFAULT_HTTP_EXCHANGE_EXTENSION,
|
|
34
|
+
) -> None:
|
|
35
|
+
""" Create tests for each path and attach them to the ExchangeVerification class. """
|
|
36
|
+
|
|
37
|
+
common_prefix = os.path.commonprefix(paths)
|
|
38
|
+
|
|
39
|
+
for path in paths:
|
|
40
|
+
name = path.replace(common_prefix, '').replace(extension, '')
|
|
41
|
+
test_name = f"test_verify_exchange__{name}"
|
|
42
|
+
|
|
43
|
+
setattr(ExchangeVerification, test_name, _get_test_method(path, server))
|
|
44
|
+
|
|
45
|
+
def _get_test_method(path: str, server: str,
|
|
46
|
+
match_options: typing.Union[typing.Dict[str, typing.Any], None] = None,
|
|
47
|
+
) -> typing.Callable:
|
|
48
|
+
""" Create a test method for the given path. """
|
|
49
|
+
|
|
50
|
+
if (match_options is None):
|
|
51
|
+
match_options = {}
|
|
52
|
+
|
|
53
|
+
def __method(self: edq.testing.unittest.BaseTest) -> None:
|
|
54
|
+
exchange = edq.util.net.HTTPExchange.from_path(path)
|
|
55
|
+
response, body = exchange.make_request(server, raise_for_status = False, **match_options)
|
|
56
|
+
|
|
57
|
+
match, hint = exchange.match_response(response, override_body = body, **match_options)
|
|
58
|
+
if (not match):
|
|
59
|
+
raise AssertionError(f"Exchange does not match: '{hint}'.")
|
|
60
|
+
|
|
61
|
+
return __method
|
|
62
|
+
|
|
63
|
+
def _collect_exchange_paths(
|
|
64
|
+
paths: typing.List[str],
|
|
65
|
+
extension: str = edq.util.net.DEFAULT_HTTP_EXCHANGE_EXTENSION,
|
|
66
|
+
) -> typing.List[str]:
|
|
67
|
+
""" Collect exchange files by matching extensions and descending dirs. """
|
|
68
|
+
|
|
69
|
+
final_paths = []
|
|
70
|
+
|
|
71
|
+
for path in paths:
|
|
72
|
+
path = os.path.abspath(path)
|
|
73
|
+
|
|
74
|
+
if (os.path.isfile(path)):
|
|
75
|
+
if (path.endswith(extension)):
|
|
76
|
+
final_paths.append(path)
|
|
77
|
+
else:
|
|
78
|
+
logging.warning("Path does not look like an exchange file: '%s'.", path)
|
|
79
|
+
else:
|
|
80
|
+
dirent_paths = glob.glob(os.path.join(path, "**", f"*{extension}"), recursive = True)
|
|
81
|
+
for dirent_path in dirent_paths:
|
|
82
|
+
final_paths.append(dirent_path)
|
|
83
|
+
|
|
84
|
+
final_paths.sort()
|
|
85
|
+
return final_paths
|
edq/testing/asserts.py
CHANGED
|
@@ -37,7 +37,6 @@ class StringComparisonAssertion(typing.Protocol):
|
|
|
37
37
|
Perform an assertion between expected and actual data.
|
|
38
38
|
"""
|
|
39
39
|
|
|
40
|
-
|
|
41
40
|
def content_equals_raw(test: edq.testing.unittest.BaseTest, expected: str, actual: str, **kwargs: typing.Any) -> None:
|
|
42
41
|
""" Check for equality using a simple string comparison. """
|
|
43
42
|
|
edq/testing/cli.py
CHANGED
|
@@ -2,13 +2,19 @@
|
|
|
2
2
|
Infrastructure for testing CLI tools using a JSON file which describes a test case,
|
|
3
3
|
which is essentially an invocation of a CLI tool and the expected output.
|
|
4
4
|
|
|
5
|
-
The test case file must be a `.txt` file that live in
|
|
5
|
+
The test case file must be a `.txt` file that live in the test cases dir.
|
|
6
6
|
The file contains two parts (separated by a line with just TEST_CASE_SEP):
|
|
7
7
|
the first part which is a JSON object (see below for available keys),
|
|
8
8
|
and a second part which is the expected text output (stdout).
|
|
9
9
|
For the keys of the JSON section, see the defaulted arguments to CLITestInfo.
|
|
10
10
|
The options JSON will be splatted into CLITestInfo's constructor.
|
|
11
11
|
|
|
12
|
+
If a test class implements a method with the signature `modify_cli_test_info(self, test_info: CLITestInfo) -> None`,
|
|
13
|
+
then this method will be called with the test info right after the test info is read from disk.
|
|
14
|
+
|
|
15
|
+
If a test class implements a class method with the signature `get_test_basename(cls, path: str) -> str`,
|
|
16
|
+
then this method will be called to create the base name for the test case at the given path.
|
|
17
|
+
|
|
12
18
|
The expected output or any argument can reference the test's current temp or data dirs with `__TEMP_DIR__()` or `__DATA_DIR__()`, respectively.
|
|
13
19
|
An optional slash-separated path can be used as an argument to reference a path within those base directories.
|
|
14
20
|
For example, `__DATA_DIR__(foo/bar.txt)` references `bar.txt` inside the `foo` directory inside the data directory.
|
|
@@ -28,15 +34,11 @@ import edq.util.dirent
|
|
|
28
34
|
import edq.util.json
|
|
29
35
|
import edq.util.pyimport
|
|
30
36
|
|
|
31
|
-
THIS_DIR: str = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
|
|
32
|
-
BASE_TESTDATA_DIR: str = os.path.join(THIS_DIR, "testdata", "cli")
|
|
33
|
-
TEST_CASES_DIR: str = os.path.join(BASE_TESTDATA_DIR, "tests")
|
|
34
|
-
DATA_DIR: str = os.path.join(BASE_TESTDATA_DIR, "data")
|
|
35
|
-
|
|
36
37
|
TEST_CASE_SEP: str = '---'
|
|
37
38
|
DATA_DIR_ID: str = '__DATA_DIR__'
|
|
39
|
+
ABS_DATA_DIR_ID: str = '__ABS_DATA_DIR__'
|
|
38
40
|
TEMP_DIR_ID: str = '__TEMP_DIR__'
|
|
39
|
-
|
|
41
|
+
BASE_DIR_ID: str = '__BASE_DIR__'
|
|
40
42
|
|
|
41
43
|
DEFAULT_ASSERTION_FUNC_NAME: str = 'edq.testing.asserts.content_equals_normalize'
|
|
42
44
|
|
|
@@ -48,6 +50,7 @@ class CLITestInfo:
|
|
|
48
50
|
def __init__(self,
|
|
49
51
|
test_name: str,
|
|
50
52
|
base_dir: str,
|
|
53
|
+
data_dir: str,
|
|
51
54
|
temp_dir: str,
|
|
52
55
|
cli: typing.Union[str, None] = None,
|
|
53
56
|
arguments: typing.Union[typing.List[str], None] = None,
|
|
@@ -57,16 +60,45 @@ class CLITestInfo:
|
|
|
57
60
|
stderr_assertion_func: typing.Union[str, None] = None,
|
|
58
61
|
expected_stdout: str = '',
|
|
59
62
|
expected_stderr: str = '',
|
|
63
|
+
split_stdout_stderr: bool = False,
|
|
60
64
|
strip_error_output: bool = True,
|
|
65
|
+
extra_options: typing.Union[typing.Dict[str, typing.Any], None] = None,
|
|
61
66
|
**kwargs: typing.Any) -> None:
|
|
67
|
+
self.skip_reasons: typing.List[str] = []
|
|
68
|
+
"""
|
|
69
|
+
Reasons that this test will be skipped.
|
|
70
|
+
Any entries in this list indicate that the test should be skipped.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
self.platform_skip_pattern: typing.Union[str, None] = platform_skip
|
|
74
|
+
"""
|
|
75
|
+
A pattern to check if the test should be skipped on the current platform.
|
|
76
|
+
Will be used in `re.search()` against `sys.platform`.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
if ((platform_skip is not None) and re.search(platform_skip, sys.platform)):
|
|
80
|
+
self.skip_reasons.append(f"not available on platform: '{sys.platform}'")
|
|
81
|
+
|
|
62
82
|
self.test_name: str = test_name
|
|
63
83
|
""" The name of this test. """
|
|
64
84
|
|
|
65
85
|
self.base_dir: str = base_dir
|
|
66
|
-
"""
|
|
86
|
+
"""
|
|
87
|
+
The base directory for this test (usually the dir the CLI test file lives.
|
|
88
|
+
This is the expansion for `__BASE_DIR__` paths.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
self.data_dir: str = data_dir
|
|
92
|
+
"""
|
|
93
|
+
A directory that additional testing data lives in.
|
|
94
|
+
This is the expansion for `__DATA_DIR__` paths.
|
|
95
|
+
"""
|
|
67
96
|
|
|
68
97
|
self.temp_dir: str = temp_dir
|
|
69
|
-
"""
|
|
98
|
+
"""
|
|
99
|
+
A temp directory that this test has access to.
|
|
100
|
+
This is the expansion for `__TEMP_DIR__` paths.
|
|
101
|
+
"""
|
|
70
102
|
|
|
71
103
|
edq.util.dirent.mkdir(temp_dir)
|
|
72
104
|
|
|
@@ -76,9 +108,12 @@ class CLITestInfo:
|
|
|
76
108
|
self.module_name: str = cli
|
|
77
109
|
""" The name of the module to invoke. """
|
|
78
110
|
|
|
79
|
-
self.module: typing.Any =
|
|
111
|
+
self.module: typing.Any = None
|
|
80
112
|
""" The module to invoke. """
|
|
81
113
|
|
|
114
|
+
if (not self.should_skip()):
|
|
115
|
+
self.module = edq.util.pyimport.import_name(self.module_name)
|
|
116
|
+
|
|
82
117
|
if (arguments is None):
|
|
83
118
|
arguments = []
|
|
84
119
|
|
|
@@ -88,19 +123,16 @@ class CLITestInfo:
|
|
|
88
123
|
self.error: bool = error
|
|
89
124
|
""" Whether or not this test is expected to be an error (raise an exception). """
|
|
90
125
|
|
|
91
|
-
self.platform_skip: typing.Union[str, None] = platform_skip
|
|
92
|
-
""" If the current platform matches this regular expression, then the test will be skipped. """
|
|
93
|
-
|
|
94
126
|
self.stdout_assertion_func: typing.Union[edq.testing.asserts.StringComparisonAssertion, None] = None
|
|
95
127
|
""" The assertion func to compare the expected and actual stdout of the CLI. """
|
|
96
128
|
|
|
97
|
-
if (stdout_assertion_func is not None):
|
|
129
|
+
if ((stdout_assertion_func is not None) and (not self.should_skip())):
|
|
98
130
|
self.stdout_assertion_func = edq.util.pyimport.fetch(stdout_assertion_func)
|
|
99
131
|
|
|
100
132
|
self.stderr_assertion_func: typing.Union[edq.testing.asserts.StringComparisonAssertion, None] = None
|
|
101
133
|
""" The assertion func to compare the expected and actual stderr of the CLI. """
|
|
102
134
|
|
|
103
|
-
if (stderr_assertion_func is not None):
|
|
135
|
+
if ((stderr_assertion_func is not None) and (not self.should_skip())):
|
|
104
136
|
self.stderr_assertion_func = edq.util.pyimport.fetch(stderr_assertion_func)
|
|
105
137
|
|
|
106
138
|
self.expected_stdout: str = expected_stdout
|
|
@@ -113,12 +145,33 @@ class CLITestInfo:
|
|
|
113
145
|
self.expected_stdout = self.expected_stdout.strip()
|
|
114
146
|
self.expected_stderr = self.expected_stderr.strip()
|
|
115
147
|
|
|
148
|
+
self.split_stdout_stderr: bool = split_stdout_stderr
|
|
149
|
+
"""
|
|
150
|
+
Split stdout and stderr into different strings for testing.
|
|
151
|
+
By default, these two will be combined.
|
|
152
|
+
If both are non-empty, then they will be joined like: f"{stdout}\n{TEST_CASE_SEP}\n{stderr}".
|
|
153
|
+
Otherwise, only the non-empty one will be present with no separator.
|
|
154
|
+
Any stdout assertions will be applied to the combined text.
|
|
155
|
+
"""
|
|
156
|
+
|
|
116
157
|
# Make any path normalizations over the arguments and expected output.
|
|
117
158
|
self.expected_stdout = self._expand_paths(self.expected_stdout)
|
|
118
159
|
self.expected_stderr = self._expand_paths(self.expected_stderr)
|
|
119
160
|
for (i, argument) in enumerate(self.arguments):
|
|
120
161
|
self.arguments[i] = self._expand_paths(argument)
|
|
121
162
|
|
|
163
|
+
if (extra_options is None):
|
|
164
|
+
extra_options = {}
|
|
165
|
+
|
|
166
|
+
self.extra_options: typing.Union[typing.Dict[str, typing.Any], None] = extra_options
|
|
167
|
+
"""
|
|
168
|
+
A place to store additional options.
|
|
169
|
+
Extra top-level options will cause tests to error.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
if (len(kwargs) > 0):
|
|
173
|
+
raise ValueError(f"Found unknown CLI test options: '{kwargs}'.")
|
|
174
|
+
|
|
122
175
|
def _expand_paths(self, text: str) -> str:
|
|
123
176
|
"""
|
|
124
177
|
Expand path replacements in testing text.
|
|
@@ -126,9 +179,10 @@ class CLITestInfo:
|
|
|
126
179
|
"""
|
|
127
180
|
|
|
128
181
|
replacements = [
|
|
129
|
-
(DATA_DIR_ID,
|
|
182
|
+
(DATA_DIR_ID, self.data_dir),
|
|
130
183
|
(TEMP_DIR_ID, self.temp_dir),
|
|
131
|
-
(
|
|
184
|
+
(BASE_DIR_ID, self.base_dir),
|
|
185
|
+
(ABS_DATA_DIR_ID, os.path.abspath(self.data_dir)),
|
|
132
186
|
]
|
|
133
187
|
|
|
134
188
|
for (key, target_dir) in replacements:
|
|
@@ -136,8 +190,18 @@ class CLITestInfo:
|
|
|
136
190
|
|
|
137
191
|
return text
|
|
138
192
|
|
|
193
|
+
def should_skip(self) -> bool:
|
|
194
|
+
""" Check if this test should be skipped. """
|
|
195
|
+
|
|
196
|
+
return (len(self.skip_reasons) > 0)
|
|
197
|
+
|
|
198
|
+
def skip_message(self) -> str:
|
|
199
|
+
""" Get a message displaying the reasons this test should be skipped. """
|
|
200
|
+
|
|
201
|
+
return f"This test has been skipped because of the following: {self.skip_reasons}."
|
|
202
|
+
|
|
139
203
|
@staticmethod
|
|
140
|
-
def load_path(path: str, test_name: str, base_temp_dir: str) -> 'CLITestInfo':
|
|
204
|
+
def load_path(path: str, test_name: str, base_temp_dir: str, data_dir: str) -> 'CLITestInfo':
|
|
141
205
|
""" Load a CLI test file and extract the test info. """
|
|
142
206
|
|
|
143
207
|
options, expected_stdout = read_test_file(path)
|
|
@@ -147,7 +211,7 @@ class CLITestInfo:
|
|
|
147
211
|
base_dir = os.path.dirname(os.path.abspath(path))
|
|
148
212
|
temp_dir = os.path.join(base_temp_dir, test_name)
|
|
149
213
|
|
|
150
|
-
return CLITestInfo(test_name, base_dir, temp_dir, **options)
|
|
214
|
+
return CLITestInfo(test_name, base_dir, data_dir, temp_dir, **options)
|
|
151
215
|
|
|
152
216
|
def read_test_file(path: str) -> typing.Tuple[typing.Dict[str, typing.Any], str]:
|
|
153
217
|
""" Read a test case file and split the output into JSON data and text. """
|
|
@@ -189,14 +253,18 @@ def replace_path_pattern(text: str, key: str, target_dir: str) -> str:
|
|
|
189
253
|
|
|
190
254
|
return text
|
|
191
255
|
|
|
192
|
-
def _get_test_method(test_name: str, path: str) -> typing.Callable:
|
|
256
|
+
def _get_test_method(test_name: str, path: str, data_dir: str) -> typing.Callable:
|
|
193
257
|
""" Get a test method that represents the test case at the given path. """
|
|
194
258
|
|
|
195
259
|
def __method(self: edq.testing.unittest.BaseTest) -> None:
|
|
196
|
-
test_info = CLITestInfo.load_path(path, test_name, getattr(self, BASE_TEMP_DIR_ATTR))
|
|
260
|
+
test_info = CLITestInfo.load_path(path, test_name, getattr(self, BASE_TEMP_DIR_ATTR), data_dir)
|
|
261
|
+
|
|
262
|
+
# Allow the test class a chance to modify the test info before the test runs.
|
|
263
|
+
if (hasattr(self, 'modify_cli_test_info')):
|
|
264
|
+
self.modify_cli_test_info(test_info)
|
|
197
265
|
|
|
198
|
-
if (
|
|
199
|
-
self.skipTest(
|
|
266
|
+
if (test_info.should_skip()):
|
|
267
|
+
self.skipTest(test_info.skip_message())
|
|
200
268
|
|
|
201
269
|
old_args = sys.argv
|
|
202
270
|
sys.argv = [test_info.module.__file__] + test_info.arguments
|
|
@@ -223,6 +291,12 @@ def _get_test_method(test_name: str, path: str) -> typing.Callable:
|
|
|
223
291
|
finally:
|
|
224
292
|
sys.argv = old_args
|
|
225
293
|
|
|
294
|
+
if (not test_info.split_stdout_stderr):
|
|
295
|
+
if ((len(stdout_text) > 0) and (len(stderr_text) > 0)):
|
|
296
|
+
stdout_text = f"{stdout_text}\n{TEST_CASE_SEP}\n{stderr_text}"
|
|
297
|
+
elif (len(stderr_text) > 0):
|
|
298
|
+
stdout_text = stderr_text
|
|
299
|
+
|
|
226
300
|
if (test_info.stdout_assertion_func is not None):
|
|
227
301
|
test_info.stdout_assertion_func(self, test_info.expected_stdout, stdout_text)
|
|
228
302
|
|
|
@@ -231,7 +305,7 @@ def _get_test_method(test_name: str, path: str) -> typing.Callable:
|
|
|
231
305
|
|
|
232
306
|
return __method
|
|
233
307
|
|
|
234
|
-
def add_test_paths(target_class: type, paths: typing.List[str]) -> None:
|
|
308
|
+
def add_test_paths(target_class: type, data_dir: str, paths: typing.List[str]) -> None:
|
|
235
309
|
""" Add tests from the given test files. """
|
|
236
310
|
|
|
237
311
|
# Attach a temp directory to the testing class so all tests can share a common base temp dir.
|
|
@@ -239,15 +313,19 @@ def add_test_paths(target_class: type, paths: typing.List[str]) -> None:
|
|
|
239
313
|
setattr(target_class, BASE_TEMP_DIR_ATTR, edq.util.dirent.get_temp_path('edq_cli_test_'))
|
|
240
314
|
|
|
241
315
|
for path in sorted(paths):
|
|
242
|
-
|
|
316
|
+
basename = os.path.splitext(os.path.basename(path))[0]
|
|
317
|
+
if (hasattr(target_class, 'get_test_basename')):
|
|
318
|
+
basename = getattr(target_class, 'get_test_basename')(path)
|
|
319
|
+
|
|
320
|
+
test_name = 'test_cli__' + basename
|
|
243
321
|
|
|
244
322
|
try:
|
|
245
|
-
setattr(target_class, test_name, _get_test_method(test_name, path))
|
|
323
|
+
setattr(target_class, test_name, _get_test_method(test_name, path, data_dir))
|
|
246
324
|
except Exception as ex:
|
|
247
325
|
raise ValueError(f"Failed to parse test case '{path}'.") from ex
|
|
248
326
|
|
|
249
|
-
def discover_test_cases(target_class: type) -> None:
|
|
327
|
+
def discover_test_cases(target_class: type, test_cases_dir: str, data_dir: str) -> None:
|
|
250
328
|
""" Look in the text cases directory for any test cases and add them as test methods to the test class. """
|
|
251
329
|
|
|
252
|
-
paths = list(sorted(glob.glob(os.path.join(
|
|
253
|
-
add_test_paths(target_class, paths)
|
|
330
|
+
paths = list(sorted(glob.glob(os.path.join(test_cases_dir, "**", "*.txt"), recursive = True)))
|
|
331
|
+
add_test_paths(target_class, data_dir, paths)
|
edq/testing/cli_test.py
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
1
3
|
import edq.testing.cli
|
|
2
4
|
import edq.testing.unittest
|
|
3
5
|
|
|
6
|
+
THIS_DIR: str = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
|
|
7
|
+
BASE_TESTDATA_DIR: str = os.path.join(THIS_DIR, "testdata", "cli")
|
|
8
|
+
TEST_CASES_DIR: str = os.path.join(BASE_TESTDATA_DIR, "tests")
|
|
9
|
+
DATA_DIR: str = os.path.join(BASE_TESTDATA_DIR, "data")
|
|
10
|
+
|
|
4
11
|
class CLITest(edq.testing.unittest.BaseTest):
|
|
5
12
|
""" Test CLI invocations. """
|
|
6
13
|
|
|
7
14
|
# Populate CLITest with all the test methods.
|
|
8
|
-
edq.testing.cli.discover_test_cases(CLITest)
|
|
15
|
+
edq.testing.cli.discover_test_cases(CLITest, TEST_CASES_DIR, DATA_DIR)
|