spl2-testing-framework 1.2.2__tar.gz → 1.4.0__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.
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/PKG-INFO +4 -1
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/README.md +3 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/pyproject.toml +1 -1
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/conftest.py +13 -3
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/results.py +48 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/spl2test_runner.py +10 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/test_discovery.py +22 -6
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/LICENSE +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/__init__.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/launch_remotely_spl2.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/logger_manager.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/pytest.ini.sample +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/single_spl2_file_runner.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/spl2_test_config.json +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/spl2_utils/assertions.spl2 +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/spl2_utils/logs_to_metrics.spl2 +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/test_runner.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/__init__.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/create_diff_sheet.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/jobs/__init__.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/jobs/cli_job.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/jobs/cloud_job.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/jobs/job.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/jobs/splunk_job.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/jobs/test_cloud_job.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/performance.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/search_clients/__init__.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/search_clients/cli_search_client.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/search_clients/cloud_search_client.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/search_clients/search_client.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/search_clients/splunk_search_client.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/test_types.py +0 -0
- {spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: spl2-testing-framework
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: SPL2 Testing Framework
|
|
5
5
|
Home-page: https://github.com/splunk/spl2-testing-framework
|
|
6
6
|
Keywords: SPL2 testing framework
|
|
@@ -131,6 +131,7 @@ It is possible to pass more options to the command, which works also with pytest
|
|
|
131
131
|
Additionally, the following options are supported:
|
|
132
132
|
|
|
133
133
|
* `--ignore_empty_strings` - to ignore empty strings in the results
|
|
134
|
+
* `--ignore_additional_fields_in_actual` - to ignore fields present in actual results but not in expected results (useful when actual results contain extra fields that should not affect comparison)
|
|
134
135
|
* `--create_comparison_sheet` - to create a comparison sheet in `comparison_box_test` folder using actual and expected outputs (works only when running box tests)
|
|
135
136
|
|
|
136
137
|
Note: The `pytest.ini.sample` file allows you to define command parameters. Just update the configurations, rename the
|
|
@@ -180,6 +181,8 @@ It is possible to pass more options to the command, which works also with pytest
|
|
|
180
181
|
the samples in module.json file of the corresponding template file
|
|
181
182
|
* `--sample_delimiter` - Separator for separating the samples provided in the sample file. If not provided, it will use
|
|
182
183
|
newline as a default separator.
|
|
184
|
+
* `--limit_tests` - Limit number of tests to execute from the unit, sample file or module.json. If not provided, all will be executed.
|
|
185
|
+
This can be used for quick testing of spl2 files during development.
|
|
183
186
|
|
|
184
187
|
* ... and much more, whatever is supported by pytest
|
|
185
188
|
|
|
@@ -99,6 +99,7 @@ It is possible to pass more options to the command, which works also with pytest
|
|
|
99
99
|
Additionally, the following options are supported:
|
|
100
100
|
|
|
101
101
|
* `--ignore_empty_strings` - to ignore empty strings in the results
|
|
102
|
+
* `--ignore_additional_fields_in_actual` - to ignore fields present in actual results but not in expected results (useful when actual results contain extra fields that should not affect comparison)
|
|
102
103
|
* `--create_comparison_sheet` - to create a comparison sheet in `comparison_box_test` folder using actual and expected outputs (works only when running box tests)
|
|
103
104
|
|
|
104
105
|
Note: The `pytest.ini.sample` file allows you to define command parameters. Just update the configurations, rename the
|
|
@@ -148,6 +149,8 @@ It is possible to pass more options to the command, which works also with pytest
|
|
|
148
149
|
the samples in module.json file of the corresponding template file
|
|
149
150
|
* `--sample_delimiter` - Separator for separating the samples provided in the sample file. If not provided, it will use
|
|
150
151
|
newline as a default separator.
|
|
152
|
+
* `--limit_tests` - Limit number of tests to execute from the unit, sample file or module.json. If not provided, all will be executed.
|
|
153
|
+
This can be used for quick testing of spl2 files during development.
|
|
151
154
|
|
|
152
155
|
* ... and much more, whatever is supported by pytest
|
|
153
156
|
|
{spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/conftest.py
RENAMED
|
@@ -47,10 +47,12 @@ BOX_TEST = "box_test"
|
|
|
47
47
|
SINGLE_SPL2_FILE = "single_spl2_file"
|
|
48
48
|
|
|
49
49
|
IGNORE_EMPTY_STRINGS = "ignore_empty_strings"
|
|
50
|
+
IGNORE_ADDITIONAL_FIELDS_IN_ACTUAL = "ignore_additional_fields_in_actual"
|
|
50
51
|
CREATE_COMPARISON_SHEET = "create_comparison_sheet"
|
|
51
52
|
PERFORMANCE_CHECK = "performance_check"
|
|
52
53
|
TEST_TYPE = "type"
|
|
53
54
|
TEST_DIR = "test_dir"
|
|
55
|
+
LIMIT_TESTS = "limit_tests"
|
|
54
56
|
|
|
55
57
|
import logging
|
|
56
58
|
|
|
@@ -62,7 +64,7 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
62
64
|
|
|
63
65
|
|
|
64
66
|
def pytest_addoption(parser):
|
|
65
|
-
parser.addoption(f"--{TEST_TYPE}", action="store", default="
|
|
67
|
+
parser.addoption(f"--{TEST_TYPE}", action="store", default="cli")
|
|
66
68
|
parser.addoption(f"--{TEST_DIR}", action="store", default=".")
|
|
67
69
|
parser.addoption(
|
|
68
70
|
f"--{PERFORMANCE_CHECK}",
|
|
@@ -75,6 +77,8 @@ def pytest_addoption(parser):
|
|
|
75
77
|
parser.addoption("--sample_delimiter", action="store", default="\n")
|
|
76
78
|
parser.addoption(f"--{CREATE_COMPARISON_SHEET}", action="store_true", default=False)
|
|
77
79
|
parser.addoption(f"--{IGNORE_EMPTY_STRINGS}", action="store_true")
|
|
80
|
+
parser.addoption(f"--{IGNORE_ADDITIONAL_FIELDS_IN_ACTUAL}", action="store_true")
|
|
81
|
+
parser.addoption(f"--{LIMIT_TESTS}", action="store", type=int, default=None)
|
|
78
82
|
|
|
79
83
|
|
|
80
84
|
def pytest_generate_tests(metafunc):
|
|
@@ -82,11 +86,12 @@ def pytest_generate_tests(metafunc):
|
|
|
82
86
|
--test_dir option"""
|
|
83
87
|
test_dir = metafunc.config.getoption(TEST_DIR)
|
|
84
88
|
performance_check = metafunc.config.getoption(PERFORMANCE_CHECK)
|
|
89
|
+
limit_tests = metafunc.config.getoption(LIMIT_TESTS)
|
|
85
90
|
_set_results_options(metafunc)
|
|
86
91
|
|
|
87
92
|
if BOX_TEST in metafunc.fixturenames:
|
|
88
93
|
_LOGGER.info("Collecting Box Tests")
|
|
89
|
-
module_tests = BoxTestDiscovery(test_dir, performance_check)
|
|
94
|
+
module_tests = BoxTestDiscovery(test_dir, performance_check, limit_tests)
|
|
90
95
|
module_tests.discover_tests()
|
|
91
96
|
metafunc.parametrize(
|
|
92
97
|
"%s" % BOX_TEST, module_tests.tests, ids=module_tests.tests.get_ids()
|
|
@@ -105,6 +110,7 @@ def pytest_generate_tests(metafunc):
|
|
|
105
110
|
sample_file,
|
|
106
111
|
sample_delimiter,
|
|
107
112
|
performance_check=performance_check,
|
|
113
|
+
limit_tests=limit_tests,
|
|
108
114
|
)
|
|
109
115
|
try:
|
|
110
116
|
spl2_pipelines.discover_tests()
|
|
@@ -118,7 +124,7 @@ def pytest_generate_tests(metafunc):
|
|
|
118
124
|
|
|
119
125
|
if UNIT_TEST in metafunc.fixturenames:
|
|
120
126
|
_LOGGER.info("Collecting Unit Tests")
|
|
121
|
-
unit_tests = UTDiscovery(test_dir)
|
|
127
|
+
unit_tests = UTDiscovery(test_dir, limit_tests)
|
|
122
128
|
unit_tests.discover_tests()
|
|
123
129
|
metafunc.parametrize(
|
|
124
130
|
UNIT_TEST, unit_tests.tests, ids=unit_tests.tests.get_ids()
|
|
@@ -245,3 +251,7 @@ def bearer_token_authentication(config):
|
|
|
245
251
|
def _set_results_options(metafunc):
|
|
246
252
|
ignore_empty_strings = metafunc.config.getoption(IGNORE_EMPTY_STRINGS)
|
|
247
253
|
Results._ignore_empty_strings = ignore_empty_strings
|
|
254
|
+
ignore_fields_in_actual = metafunc.config.getoption(
|
|
255
|
+
IGNORE_ADDITIONAL_FIELDS_IN_ACTUAL
|
|
256
|
+
)
|
|
257
|
+
Results._ignore_additional_fields_in_actual = ignore_fields_in_actual
|
|
@@ -24,6 +24,7 @@ import pytest
|
|
|
24
24
|
|
|
25
25
|
class Results(UserDict):
|
|
26
26
|
_ignore_empty_strings = False
|
|
27
|
+
_ignore_additional_fields_in_actual = False
|
|
27
28
|
"""Class responsible for parsing and storing results from Splunk, IP and CLI.
|
|
28
29
|
It's necessary to parse nested jsons and other python types to strings in order to compare them
|
|
29
30
|
"""
|
|
@@ -121,6 +122,53 @@ class Results(UserDict):
|
|
|
121
122
|
if self._ignore_empty_strings:
|
|
122
123
|
self.data = self._remove_field_by_value(self.data, "")
|
|
123
124
|
|
|
125
|
+
@staticmethod
|
|
126
|
+
def _remove_extra_fields(actual_data, expected_data):
|
|
127
|
+
"""Remove fields from actual_data that are not present in expected_data.
|
|
128
|
+
This is useful when actual results may have additional fields that should be ignored in comparison.
|
|
129
|
+
Works recursively for nested dicts and lists.
|
|
130
|
+
"""
|
|
131
|
+
if isinstance(expected_data, dict) and isinstance(actual_data, dict):
|
|
132
|
+
# Get the keys that are in actual but not in expected
|
|
133
|
+
keys_to_remove = set(actual_data.keys()) - set(expected_data.keys())
|
|
134
|
+
for key in keys_to_remove:
|
|
135
|
+
actual_data.pop(key, None)
|
|
136
|
+
|
|
137
|
+
# Recursively process nested structures
|
|
138
|
+
for key in actual_data.keys():
|
|
139
|
+
if key in expected_data:
|
|
140
|
+
if isinstance(actual_data[key], dict) and isinstance(
|
|
141
|
+
expected_data[key], dict
|
|
142
|
+
):
|
|
143
|
+
Results._remove_extra_fields(
|
|
144
|
+
actual_data[key], expected_data[key]
|
|
145
|
+
)
|
|
146
|
+
elif isinstance(actual_data[key], list) and isinstance(
|
|
147
|
+
expected_data[key], list
|
|
148
|
+
):
|
|
149
|
+
# For lists, we need to handle them carefully
|
|
150
|
+
if len(actual_data[key]) > 0 and len(expected_data[key]) > 0:
|
|
151
|
+
if isinstance(actual_data[key][0], dict) and isinstance(
|
|
152
|
+
expected_data[key][0], dict
|
|
153
|
+
):
|
|
154
|
+
# If both are lists of dicts, process each element
|
|
155
|
+
for i in range(
|
|
156
|
+
min(len(actual_data[key]), len(expected_data[key]))
|
|
157
|
+
):
|
|
158
|
+
Results._remove_extra_fields(
|
|
159
|
+
actual_data[key][i], expected_data[key][i]
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
return actual_data
|
|
163
|
+
|
|
164
|
+
def remove_fields_not_in_expected(self, expected):
|
|
165
|
+
"""Remove fields from this Results object that are not present in the expected Results object.
|
|
166
|
+
This is the public method to be called when comparing results.
|
|
167
|
+
"""
|
|
168
|
+
if self._ignore_additional_fields_in_actual and isinstance(expected, Results):
|
|
169
|
+
self.data = self._remove_extra_fields(self.data, expected.data)
|
|
170
|
+
return self
|
|
171
|
+
|
|
124
172
|
def __lt__(self, other):
|
|
125
173
|
if "_raw" in self.data:
|
|
126
174
|
s = self.data.get("_raw")
|
|
@@ -131,6 +131,11 @@ class SPL2TestRunner:
|
|
|
131
131
|
perf.check_performance(output_result)
|
|
132
132
|
|
|
133
133
|
if box_test.output:
|
|
134
|
+
# Remove fields from actual results that are not in expected results if configured
|
|
135
|
+
for i, actual_result in enumerate(output_result):
|
|
136
|
+
if i < len(box_test.output):
|
|
137
|
+
actual_result.remove_fields_not_in_expected(box_test.output[i])
|
|
138
|
+
|
|
134
139
|
if self.create_comparison_sheet:
|
|
135
140
|
self.comp_obj.create_comparison_sheet(output_result, box_test)
|
|
136
141
|
assert sorted(output_result) == sorted(box_test.output)
|
|
@@ -141,6 +146,11 @@ class SPL2TestRunner:
|
|
|
141
146
|
_LOGGER.info(f"Output check skipped (expected empty): {box_test.name}")
|
|
142
147
|
|
|
143
148
|
if box_test.metrics:
|
|
149
|
+
# Remove fields from actual metrics that are not in expected metrics if configured
|
|
150
|
+
for i, actual_metric in enumerate(metrics_result):
|
|
151
|
+
if i < len(box_test.metrics):
|
|
152
|
+
actual_metric.remove_fields_not_in_expected(box_test.metrics[i])
|
|
153
|
+
|
|
144
154
|
assert sorted(metrics_result) == sorted(box_test.metrics)
|
|
145
155
|
_LOGGER.info(f"Metric check passed: {box_test.name}")
|
|
146
156
|
_LOGGER.debug("Received: \n%s", str(metrics_result))
|
|
@@ -41,9 +41,10 @@ class SamplesNotFoundException(Exception):
|
|
|
41
41
|
class TestDiscovery(abc.ABC):
|
|
42
42
|
"""Base class for test discovery classes"""
|
|
43
43
|
|
|
44
|
-
def __init__(self, path: str, performance_check: str):
|
|
44
|
+
def __init__(self, path: str, performance_check: str, limit_tests: int = None):
|
|
45
45
|
self.path = pathlib.Path(path)
|
|
46
46
|
self._performance_check = performance_check
|
|
47
|
+
self.limit_tests = limit_tests
|
|
47
48
|
self.tests = TestSuite()
|
|
48
49
|
|
|
49
50
|
def _read_tests(self, test_name: str) -> None:
|
|
@@ -56,8 +57,8 @@ class TestDiscovery(abc.ABC):
|
|
|
56
57
|
class BoxTestDiscovery(TestDiscovery):
|
|
57
58
|
"""Discovers tests for box tests, looking into module.test.json files"""
|
|
58
59
|
|
|
59
|
-
def __init__(self, path: str, performance_check: str):
|
|
60
|
-
super().__init__(path, performance_check)
|
|
60
|
+
def __init__(self, path: str, performance_check: str, limit_tests: int = None):
|
|
61
|
+
super().__init__(path, performance_check, limit_tests)
|
|
61
62
|
|
|
62
63
|
def discover_tests(self) -> None:
|
|
63
64
|
self._read_tests("module.test.json")
|
|
@@ -70,7 +71,13 @@ class BoxTestDiscovery(TestDiscovery):
|
|
|
70
71
|
|
|
71
72
|
for module_path, content in self._parsed_modules.items():
|
|
72
73
|
path = module_path.parent
|
|
74
|
+
tests_added = 0
|
|
73
75
|
for test in content:
|
|
76
|
+
if self.limit_tests is not None and tests_added >= self.limit_tests:
|
|
77
|
+
_LOGGER.info(
|
|
78
|
+
f"Limit of {self.limit_tests} tests reached for {module_path}. Stopping."
|
|
79
|
+
)
|
|
80
|
+
break
|
|
74
81
|
name = test["filename"].replace(".spl2", "")
|
|
75
82
|
test_input = test["test"]["source"]
|
|
76
83
|
code_module_path = path / test["filename"]
|
|
@@ -92,6 +99,7 @@ class BoxTestDiscovery(TestDiscovery):
|
|
|
92
99
|
)
|
|
93
100
|
test.meta["performance"] = self._performance_check
|
|
94
101
|
self.tests.add(test)
|
|
102
|
+
tests_added += 1
|
|
95
103
|
|
|
96
104
|
|
|
97
105
|
class SingleSPL2Discovery(TestDiscovery):
|
|
@@ -104,8 +112,9 @@ class SingleSPL2Discovery(TestDiscovery):
|
|
|
104
112
|
sample_file=None,
|
|
105
113
|
sample_delimiter="\n",
|
|
106
114
|
performance_check="no",
|
|
115
|
+
limit_tests: int = None,
|
|
107
116
|
):
|
|
108
|
-
super().__init__(path, performance_check)
|
|
117
|
+
super().__init__(path, performance_check, limit_tests)
|
|
109
118
|
self.template_file = template_file
|
|
110
119
|
self.sample_file = sample_file
|
|
111
120
|
self.sample_delimiter = sample_delimiter
|
|
@@ -188,8 +197,8 @@ class SingleSPL2Discovery(TestDiscovery):
|
|
|
188
197
|
class UTDiscovery(TestDiscovery):
|
|
189
198
|
"""Discovers tests for unit tests, looking into .test.spl2 files"""
|
|
190
199
|
|
|
191
|
-
def __init__(self, path: str):
|
|
192
|
-
super().__init__(path, performance_check="no")
|
|
200
|
+
def __init__(self, path: str, limit_tests: int = None):
|
|
201
|
+
super().__init__(path, performance_check="no", limit_tests=limit_tests)
|
|
193
202
|
|
|
194
203
|
def discover_tests(self) -> None:
|
|
195
204
|
self._read_tests("*.test.spl2")
|
|
@@ -208,7 +217,13 @@ class UTDiscovery(TestDiscovery):
|
|
|
208
217
|
code_module_name = file_name.replace(".test.", ".")
|
|
209
218
|
code_module_path = path.parent / code_module_name
|
|
210
219
|
|
|
220
|
+
tests_added = 0
|
|
211
221
|
for test in tests:
|
|
222
|
+
if self.limit_tests is not None and tests_added >= self.limit_tests:
|
|
223
|
+
_LOGGER.info(
|
|
224
|
+
f"Limit of {self.limit_tests} tests reached for {file}. Stopping."
|
|
225
|
+
)
|
|
226
|
+
break
|
|
212
227
|
self.tests.add(
|
|
213
228
|
UnitTest(
|
|
214
229
|
name=test,
|
|
@@ -218,3 +233,4 @@ class UTDiscovery(TestDiscovery):
|
|
|
218
233
|
code_module_path=code_module_path,
|
|
219
234
|
)
|
|
220
235
|
)
|
|
236
|
+
tests_added += 1
|
|
File without changes
|
{spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/test_runner.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spl2_testing_framework-1.2.2 → spl2_testing_framework-1.4.0}/spl2_testing_framework/tools/utils.py
RENAMED
|
File without changes
|