spl2-testing-framework 1.0.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.
- spl2_testing_framework/__init__.py +15 -0
- spl2_testing_framework/conftest.py +217 -0
- spl2_testing_framework/launch_remotely_spl2.py +85 -0
- spl2_testing_framework/logger_manager.py +26 -0
- spl2_testing_framework/pytest.ini.sample +16 -0
- spl2_testing_framework/single_spl2_file_runner.py +52 -0
- spl2_testing_framework/spl2_test_config.json +9 -0
- spl2_testing_framework/spl2_utils/assertions.spl2 +157 -0
- spl2_testing_framework/spl2_utils/logs_to_metrics.spl2 +36 -0
- spl2_testing_framework/test_runner.py +59 -0
- spl2_testing_framework/tools/__init__.py +15 -0
- spl2_testing_framework/tools/jobs/__init__.py +15 -0
- spl2_testing_framework/tools/jobs/cli_job.py +72 -0
- spl2_testing_framework/tools/jobs/cloud_job.py +107 -0
- spl2_testing_framework/tools/jobs/job.py +65 -0
- spl2_testing_framework/tools/jobs/splunk_job.py +112 -0
- spl2_testing_framework/tools/performance.py +252 -0
- spl2_testing_framework/tools/results.py +135 -0
- spl2_testing_framework/tools/search_clients/__init__.py +28 -0
- spl2_testing_framework/tools/search_clients/cli_search_client.py +87 -0
- spl2_testing_framework/tools/search_clients/cloud_search_client.py +84 -0
- spl2_testing_framework/tools/search_clients/search_client.py +150 -0
- spl2_testing_framework/tools/search_clients/splunk_search_client.py +81 -0
- spl2_testing_framework/tools/spl2test_runner.py +143 -0
- spl2_testing_framework/tools/test_discovery.py +220 -0
- spl2_testing_framework/tools/test_types.py +84 -0
- spl2_testing_framework/tools/utils.py +52 -0
- spl2_testing_framework-1.0.0.dist-info/LICENSE +201 -0
- spl2_testing_framework-1.0.0.dist-info/METADATA +190 -0
- spl2_testing_framework-1.0.0.dist-info/RECORD +32 -0
- spl2_testing_framework-1.0.0.dist-info/WHEEL +4 -0
- spl2_testing_framework-1.0.0.dist-info/entry_points.txt +4 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Splunk Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
|
|
19
|
+
from tabulate import tabulate
|
|
20
|
+
|
|
21
|
+
from spl2_testing_framework.tools.performance import PerformanceCheck
|
|
22
|
+
from spl2_testing_framework.tools.test_types import UnitTest, BoxTest, SingleSPL2
|
|
23
|
+
from spl2_testing_framework.tools.utils import _make_functions_visible_for_testing
|
|
24
|
+
|
|
25
|
+
_LOGGER = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SPL2TestRunner:
|
|
29
|
+
"""Main class responsible for running tests"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, search_client):
|
|
32
|
+
self._search_client = search_client
|
|
33
|
+
|
|
34
|
+
def run_single_spl2_file(self, single_spl2_file: SingleSPL2) -> None:
|
|
35
|
+
"""Run a single spl2 file and print the output"""
|
|
36
|
+
code_to_test = _make_functions_visible_for_testing(
|
|
37
|
+
single_spl2_file.code_module_path
|
|
38
|
+
)
|
|
39
|
+
perf = PerformanceCheck(single_spl2_file)
|
|
40
|
+
code_to_test = perf.apply_code_transformations(code_to_test)
|
|
41
|
+
|
|
42
|
+
job = self._search_client.create_simple_job(
|
|
43
|
+
single_spl2_file.input,
|
|
44
|
+
single_spl2_file.code_module_path.stem,
|
|
45
|
+
code_to_test,
|
|
46
|
+
single_spl2_file.name,
|
|
47
|
+
)
|
|
48
|
+
self._search_client.run_job(job)
|
|
49
|
+
job_result = self._search_client.get_job_results(job)
|
|
50
|
+
output_result = job_result.get("destination", [])
|
|
51
|
+
|
|
52
|
+
perf.check_performance(output_result)
|
|
53
|
+
|
|
54
|
+
self.print_results(output_result, "RESULTS")
|
|
55
|
+
metrics_result = job_result.get("metrics_destination", [])
|
|
56
|
+
if len(metrics_result) > 0:
|
|
57
|
+
self.print_results(metrics_result, "METRICS RESULTS")
|
|
58
|
+
|
|
59
|
+
def print_results(self, results, type):
|
|
60
|
+
"""Prints the results in cli and log file"""
|
|
61
|
+
separator = "#"
|
|
62
|
+
_LOGGER.info(f"\n\n{separator * 50} {type} {separator * 50}\n\n")
|
|
63
|
+
for item in results:
|
|
64
|
+
table = tabulate(item.items(), headers=["Field", "Value"], tablefmt="plain")
|
|
65
|
+
_LOGGER.info("\n" + "-" * 20 + "\n")
|
|
66
|
+
_LOGGER.info("\n" + table + "\n")
|
|
67
|
+
_LOGGER.info("\n" + "-" * 20 + "\n")
|
|
68
|
+
|
|
69
|
+
def run_unit_test(self, template_test: UnitTest) -> None:
|
|
70
|
+
"""Run a unit test"""
|
|
71
|
+
code_to_test = _make_functions_visible_for_testing(
|
|
72
|
+
template_test.code_module_path
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
job = self._search_client.create_job(
|
|
76
|
+
template_test.content,
|
|
77
|
+
template_test.code_module_path.stem,
|
|
78
|
+
code_to_test,
|
|
79
|
+
template_test.name,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
self._search_client.run_job(job)
|
|
83
|
+
job_results = self._search_client.get_job_results(job)
|
|
84
|
+
|
|
85
|
+
errors = []
|
|
86
|
+
|
|
87
|
+
for result in job_results[template_test.name]:
|
|
88
|
+
for case_result in result["_testResults"]["results"]:
|
|
89
|
+
received, expected = (
|
|
90
|
+
case_result.get("received", None),
|
|
91
|
+
case_result.get("expected", None),
|
|
92
|
+
)
|
|
93
|
+
_LOGGER.debug("Check details: %s", case_result)
|
|
94
|
+
|
|
95
|
+
if expected == received:
|
|
96
|
+
_LOGGER.info(
|
|
97
|
+
f'Check passed: {template_test.file_name}/{template_test.name}/{case_result["test"]}'
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
errors.append(
|
|
101
|
+
{
|
|
102
|
+
"TEST_STATEMENT": case_result["test"],
|
|
103
|
+
"ACTUAL": received,
|
|
104
|
+
"EXPECTED": expected,
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
assert errors == [], f"Assertion errors: \n{errors}"
|
|
109
|
+
|
|
110
|
+
def run_box_test(self, box_test: BoxTest) -> None:
|
|
111
|
+
"""Run a box test"""
|
|
112
|
+
code_to_test = _make_functions_visible_for_testing(box_test.code_module_path)
|
|
113
|
+
|
|
114
|
+
perf = PerformanceCheck(box_test)
|
|
115
|
+
code_to_test = perf.apply_code_transformations(code_to_test)
|
|
116
|
+
|
|
117
|
+
job = self._search_client.create_simple_job(
|
|
118
|
+
box_test.input, box_test.code_module_path.stem, code_to_test, box_test.name
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
self._search_client.run_job(job)
|
|
122
|
+
job_result = self._search_client.get_job_results(job)
|
|
123
|
+
|
|
124
|
+
output_result = job_result.get("destination", [])
|
|
125
|
+
metrics_result = job_result.get("metrics_destination", [])
|
|
126
|
+
|
|
127
|
+
perf.check_performance(output_result)
|
|
128
|
+
|
|
129
|
+
if box_test.output:
|
|
130
|
+
assert sorted(output_result) == sorted(box_test.output)
|
|
131
|
+
_LOGGER.info(f"Output check passed: {box_test.name}")
|
|
132
|
+
_LOGGER.debug("Received: \n%s", str(output_result))
|
|
133
|
+
_LOGGER.debug("Expected: \n%s", str(box_test.output))
|
|
134
|
+
else:
|
|
135
|
+
_LOGGER.info(f"Output check skipped (expected empty): {box_test.name}")
|
|
136
|
+
|
|
137
|
+
if box_test.metrics:
|
|
138
|
+
assert sorted(metrics_result) == sorted(box_test.metrics)
|
|
139
|
+
_LOGGER.info(f"Metric check passed: {box_test.name}")
|
|
140
|
+
_LOGGER.debug("Received: \n%s", str(metrics_result))
|
|
141
|
+
_LOGGER.debug("Expected: \n%s", str(box_test.metrics))
|
|
142
|
+
else:
|
|
143
|
+
_LOGGER.info(f"Metric check skipped (expected empty): {box_test.name}")
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Splunk Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
import abc
|
|
18
|
+
import json
|
|
19
|
+
import logging
|
|
20
|
+
import os
|
|
21
|
+
import pathlib
|
|
22
|
+
import re
|
|
23
|
+
|
|
24
|
+
from spl2_testing_framework.tools.results import Metrics, Results
|
|
25
|
+
from spl2_testing_framework.tools.test_types import (
|
|
26
|
+
BoxTest,
|
|
27
|
+
SingleSPL2,
|
|
28
|
+
TestSuite,
|
|
29
|
+
UnitTest,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
_LOGGER = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class SamplesNotFoundException(Exception):
|
|
36
|
+
"""An Exception to be raised if samples are not found"""
|
|
37
|
+
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TestDiscovery(abc.ABC):
|
|
42
|
+
"""Base class for test discovery classes"""
|
|
43
|
+
|
|
44
|
+
def __init__(self, path: str, performance_check: str):
|
|
45
|
+
self.path = pathlib.Path(path)
|
|
46
|
+
self._performance_check = performance_check
|
|
47
|
+
self.tests = TestSuite()
|
|
48
|
+
|
|
49
|
+
def _read_tests(self, test_name: str) -> None:
|
|
50
|
+
tests_paths = self.path.rglob(test_name)
|
|
51
|
+
self._raw_tests = {
|
|
52
|
+
x: pathlib.Path(x).read_text(encoding="utf-8") for x in tests_paths
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class BoxTestDiscovery(TestDiscovery):
|
|
57
|
+
"""Discovers tests for box tests, looking into module.test.json files"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, path: str, performance_check: str):
|
|
60
|
+
super().__init__(path, performance_check)
|
|
61
|
+
|
|
62
|
+
def discover_tests(self) -> None:
|
|
63
|
+
self._read_tests("module.test.json")
|
|
64
|
+
self._parse_tests()
|
|
65
|
+
|
|
66
|
+
def _parse_tests(self) -> None:
|
|
67
|
+
self._parsed_modules = {
|
|
68
|
+
x: json.loads(content) for x, content in self._raw_tests.items()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for module_path, content in self._parsed_modules.items():
|
|
72
|
+
path = module_path.parent
|
|
73
|
+
for test in content:
|
|
74
|
+
name = test["filename"].replace(".spl2", "")
|
|
75
|
+
test_input = test["test"]["source"]
|
|
76
|
+
code_module_path = path / test["filename"]
|
|
77
|
+
output = [
|
|
78
|
+
Results(x)
|
|
79
|
+
for x in test["test"].get("expected_destination_result", [])
|
|
80
|
+
]
|
|
81
|
+
metrics = [
|
|
82
|
+
Metrics(x)
|
|
83
|
+
for x in test["test"].get("expected_metrics_destination_result", [])
|
|
84
|
+
]
|
|
85
|
+
test = BoxTest(
|
|
86
|
+
name=name,
|
|
87
|
+
input=test_input,
|
|
88
|
+
output=output,
|
|
89
|
+
metrics=metrics,
|
|
90
|
+
module_path=module_path,
|
|
91
|
+
code_module_path=code_module_path,
|
|
92
|
+
)
|
|
93
|
+
test.meta["performance"] = self._performance_check
|
|
94
|
+
self.tests.add(test)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class SingleSPL2Discovery(TestDiscovery):
|
|
98
|
+
"""Discovers the template file and its samples"""
|
|
99
|
+
|
|
100
|
+
def __init__(
|
|
101
|
+
self,
|
|
102
|
+
path: str,
|
|
103
|
+
template_file,
|
|
104
|
+
sample_file=None,
|
|
105
|
+
sample_delimiter="\n",
|
|
106
|
+
performance_check="no",
|
|
107
|
+
):
|
|
108
|
+
super().__init__(path, performance_check)
|
|
109
|
+
self.template_file = template_file
|
|
110
|
+
self.sample_file = sample_file
|
|
111
|
+
self.sample_delimiter = sample_delimiter
|
|
112
|
+
|
|
113
|
+
def discover_tests(self) -> None:
|
|
114
|
+
if not self.sample_file:
|
|
115
|
+
self._read_tests("module.json")
|
|
116
|
+
self._parse_tests()
|
|
117
|
+
|
|
118
|
+
def _parse_tests(self) -> None:
|
|
119
|
+
name = None
|
|
120
|
+
code_module_path = None
|
|
121
|
+
if self.sample_file:
|
|
122
|
+
_LOGGER.info("Reading samples from provided sample file.")
|
|
123
|
+
events = self._read_samples_from_file(delimiter=self.sample_delimiter)
|
|
124
|
+
name = self.template_file.replace(".spl2", "")
|
|
125
|
+
code_module_path = next(self.path.rglob(self.template_file), None)
|
|
126
|
+
else:
|
|
127
|
+
_LOGGER.info("Reading samples from corresponding module.json file")
|
|
128
|
+
self._parsed_modules = {
|
|
129
|
+
x: json.loads(content) for x, content in self._raw_tests.items()
|
|
130
|
+
}
|
|
131
|
+
events = None
|
|
132
|
+
for module_path, content in self._parsed_modules.items():
|
|
133
|
+
path = module_path.parent
|
|
134
|
+
break_outer = False
|
|
135
|
+
for test in content:
|
|
136
|
+
if test["filename"] == os.path.basename(self.template_file):
|
|
137
|
+
name = test["filename"].replace(".spl2", "")
|
|
138
|
+
code_module_path = path / test["filename"]
|
|
139
|
+
events = test["context"]["events"]
|
|
140
|
+
if isinstance(events, str):
|
|
141
|
+
events = events.split("\r\n")
|
|
142
|
+
elif isinstance(events, list):
|
|
143
|
+
events = [event["_raw"] for event in events]
|
|
144
|
+
else:
|
|
145
|
+
_LOGGER.error("Invalid events found.")
|
|
146
|
+
raise SamplesNotFoundException(
|
|
147
|
+
"No valid sample events found. Exiting.."
|
|
148
|
+
)
|
|
149
|
+
break_outer = True
|
|
150
|
+
break
|
|
151
|
+
if break_outer:
|
|
152
|
+
break
|
|
153
|
+
if not events:
|
|
154
|
+
_LOGGER.error("No events found.")
|
|
155
|
+
raise SamplesNotFoundException("No valid sample events found. Exiting..")
|
|
156
|
+
_LOGGER.debug(f"Events collected: {events}")
|
|
157
|
+
test_input = self._parse_and_append_events(events)
|
|
158
|
+
|
|
159
|
+
test = SingleSPL2(
|
|
160
|
+
name=name,
|
|
161
|
+
input=test_input,
|
|
162
|
+
code_module_path=code_module_path,
|
|
163
|
+
)
|
|
164
|
+
test.meta["performance"] = self._performance_check
|
|
165
|
+
|
|
166
|
+
self.tests.add(test)
|
|
167
|
+
|
|
168
|
+
def _read_samples_from_file(self, delimiter):
|
|
169
|
+
with open(self.sample_file) as file:
|
|
170
|
+
events = filter(lambda ev: len(ev) > 0, file.read().split(delimiter))
|
|
171
|
+
updated_events = []
|
|
172
|
+
for event in events:
|
|
173
|
+
event = "".join([delimiter, event])
|
|
174
|
+
updated_events.append(event)
|
|
175
|
+
final_events = [
|
|
176
|
+
event.encode().decode("unicode_escape") for event in updated_events
|
|
177
|
+
]
|
|
178
|
+
return final_events
|
|
179
|
+
|
|
180
|
+
def _parse_and_append_events(self, events):
|
|
181
|
+
formatted_events = ",".join(
|
|
182
|
+
[f"{{_raw: {json.dumps(event)} }}" for event in events]
|
|
183
|
+
)
|
|
184
|
+
template_module = f"[{formatted_events}]"
|
|
185
|
+
return template_module
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class UTDiscovery(TestDiscovery):
|
|
189
|
+
"""Discovers tests for unit tests, looking into .test.spl2 files"""
|
|
190
|
+
|
|
191
|
+
def __init__(self, path: str):
|
|
192
|
+
super().__init__(path, performance_check="no")
|
|
193
|
+
|
|
194
|
+
def discover_tests(self) -> None:
|
|
195
|
+
self._read_tests("*.test.spl2")
|
|
196
|
+
self._parse_tests()
|
|
197
|
+
|
|
198
|
+
def _parse_tests(self) -> None:
|
|
199
|
+
self.parsed = {
|
|
200
|
+
filename: re.findall(r"\$?(.*?__test)", test_file_content)
|
|
201
|
+
for filename, test_file_content in self._raw_tests.items()
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
for file, tests in self.parsed.items():
|
|
205
|
+
path = pathlib.Path(file)
|
|
206
|
+
file_name = path.name
|
|
207
|
+
content = path.read_text(encoding="utf-8")
|
|
208
|
+
code_module_name = file_name.replace(".test.", ".")
|
|
209
|
+
code_module_path = path.parent / code_module_name
|
|
210
|
+
|
|
211
|
+
for test in tests:
|
|
212
|
+
self.tests.add(
|
|
213
|
+
UnitTest(
|
|
214
|
+
name=test,
|
|
215
|
+
file_name=file_name,
|
|
216
|
+
file_path=path,
|
|
217
|
+
content=content,
|
|
218
|
+
code_module_path=code_module_path,
|
|
219
|
+
)
|
|
220
|
+
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Splunk Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
from collections import UserDict
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Dict, Iterator
|
|
22
|
+
|
|
23
|
+
_LOGGER = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class Test:
|
|
28
|
+
name: str
|
|
29
|
+
meta: dict = field(default_factory=dict, init=False)
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def test_id(self) -> str:
|
|
33
|
+
return self.name
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class SingleSPL2(Test):
|
|
38
|
+
input: str
|
|
39
|
+
code_module_path: Path
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class BoxTest(Test):
|
|
44
|
+
input: str
|
|
45
|
+
output: list
|
|
46
|
+
module_path: Path
|
|
47
|
+
code_module_path: Path
|
|
48
|
+
metrics: list = field(default_factory=list)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class UnitTest(Test):
|
|
53
|
+
file_path: Path
|
|
54
|
+
file_name: str
|
|
55
|
+
content: str
|
|
56
|
+
code_module_path: Path
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def test_id(self) -> str:
|
|
60
|
+
return self.file_name + "__" + self.name
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class TestSuite(UserDict):
|
|
65
|
+
tests: Dict[str, Test] = field(default_factory=dict)
|
|
66
|
+
|
|
67
|
+
def add(self, test: Test) -> None:
|
|
68
|
+
if test.test_id not in self.tests:
|
|
69
|
+
self.tests[test.test_id] = test
|
|
70
|
+
else:
|
|
71
|
+
_LOGGER.warning(
|
|
72
|
+
f"Test {test.test_id} already exists, name will be modified"
|
|
73
|
+
)
|
|
74
|
+
test_name = self.__calculate_new_name(test.test_id)
|
|
75
|
+
self.tests[test_name] = test
|
|
76
|
+
|
|
77
|
+
def __calculate_new_name(self, name: str) -> str:
|
|
78
|
+
return name + "__" + str(len(self.tests))
|
|
79
|
+
|
|
80
|
+
def __iter__(self):
|
|
81
|
+
return iter(self.tests.values())
|
|
82
|
+
|
|
83
|
+
def get_ids(self) -> Iterator[str]:
|
|
84
|
+
return iter(self.tests)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Splunk Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
import pathlib
|
|
18
|
+
import re
|
|
19
|
+
from functools import lru_cache
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_framework_root() -> pathlib.Path:
|
|
23
|
+
framework_root = pathlib.Path(__file__).parents[1]
|
|
24
|
+
return framework_root
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _make_functions_visible_for_testing(code_module_path):
|
|
28
|
+
code_module = code_module_path.read_text(encoding="utf-8")
|
|
29
|
+
functions = re.findall(r"^function\s+([^(]+)\(", code_module, flags=re.M)
|
|
30
|
+
for function in functions:
|
|
31
|
+
if not re.search(r"export\s" + function, code_module):
|
|
32
|
+
code_module = re.sub(
|
|
33
|
+
r"^function\s+" + function,
|
|
34
|
+
f"export function {function}",
|
|
35
|
+
code_module,
|
|
36
|
+
flags=re.M,
|
|
37
|
+
)
|
|
38
|
+
return code_module
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@lru_cache(None)
|
|
42
|
+
def read_assertions_module():
|
|
43
|
+
root_folder = get_framework_root()
|
|
44
|
+
assertions_file = root_folder / "spl2_utils" / "assertions.spl2"
|
|
45
|
+
return assertions_file.read_text(encoding="utf-8")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@lru_cache(None)
|
|
49
|
+
def read_commands_modules():
|
|
50
|
+
root_folder = get_framework_root()
|
|
51
|
+
commands_module = root_folder / "spl2_utils" / "logs_to_metrics.spl2"
|
|
52
|
+
return commands_module.read_text(encoding="utf-8")
|