spl2-testing-framework 1.5.0__tar.gz → 1.5.1__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.5.0 → spl2_testing_framework-1.5.1}/PKG-INFO +3 -2
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/README.md +2 -2
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/pyproject.toml +1 -1
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/performance.py +93 -12
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/spl2test_runner.py +1 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/LICENSE +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/__init__.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/conftest.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/launch_remotely_spl2.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/logger_manager.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/pytest.ini.sample +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/single_spl2_file_runner.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/spl2_test_config.json +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/spl2_utils/assertions.spl2 +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/spl2_utils/logs_to_metrics.spl2 +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/test_runner.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/__init__.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/create_diff_sheet.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/jobs/__init__.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/jobs/cli_job.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/jobs/cloud_job.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/jobs/job.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/jobs/splunk_job.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/jobs/test_cloud_job.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/results.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/search_clients/__init__.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/search_clients/cli_search_client.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/search_clients/cloud_search_client.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/search_clients/search_client.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/search_clients/splunk_search_client.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/test_discovery.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/test_types.py +0 -0
- {spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/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.5.
|
|
3
|
+
Version: 1.5.1
|
|
4
4
|
Summary: SPL2 Testing Framework
|
|
5
5
|
Home-page: https://github.com/splunk/spl2-testing-framework
|
|
6
6
|
Keywords: SPL2 testing framework
|
|
@@ -207,8 +207,9 @@ This checks can be applied only to box tests, as assertions which are used in un
|
|
|
207
207
|
performance.
|
|
208
208
|
|
|
209
209
|
## Format all files
|
|
210
|
-
To format all files
|
|
210
|
+
To format all files run:
|
|
211
211
|
|
|
212
212
|
```bash
|
|
213
213
|
black spl2_testing_framework tests
|
|
214
214
|
```
|
|
215
|
+
|
|
@@ -175,8 +175,8 @@ This checks can be applied only to box tests, as assertions which are used in un
|
|
|
175
175
|
performance.
|
|
176
176
|
|
|
177
177
|
## Format all files
|
|
178
|
-
To format all files
|
|
178
|
+
To format all files run:
|
|
179
179
|
|
|
180
180
|
```bash
|
|
181
181
|
black spl2_testing_framework tests
|
|
182
|
-
```
|
|
182
|
+
```
|
|
@@ -38,7 +38,7 @@ class PerformanceCheck:
|
|
|
38
38
|
PIPELINE_PATTERN = re.compile(
|
|
39
39
|
r"(?P<fullmatch>"
|
|
40
40
|
r"^\s*?\$pipeline\s*?=\s*?\|?\s*?from\s*?\$source\s*?" # first line of pipeline
|
|
41
|
-
r"(.|\n)
|
|
41
|
+
r"(.|\n)*" # greedy: match everything up to the LAST into $destination
|
|
42
42
|
r"(\|\s*?into\s*?\$destination)" # last line of pipeline definition
|
|
43
43
|
r")",
|
|
44
44
|
flags=re.M | re.VERBOSE,
|
|
@@ -46,8 +46,8 @@ class PerformanceCheck:
|
|
|
46
46
|
|
|
47
47
|
FUNCTION_PATTERN = re.compile(
|
|
48
48
|
r"^(?P<fullmatch>"
|
|
49
|
-
r"(export\s+)?function\s+(?P<f_name>\S+)\((?P<source
|
|
50
|
-
r"
|
|
49
|
+
r"(export\s+)?function\s+(?P<f_name>\S+)\((?P<source>\$\w+)" # function header (source may lack type)
|
|
50
|
+
r"[^{]*{\n\s+return\s+(\|\s+)?(?:from|FROM)\s+(?P=source)\n*" # return statement
|
|
51
51
|
r"(?P<statements>(.|\n)*?)" # anything before closing bracket
|
|
52
52
|
r"(?P<closing_bracket>^\s*})" # closing bracket [ASSUMED IT'S IN THE NEW LINE]
|
|
53
53
|
r")",
|
|
@@ -56,6 +56,20 @@ class PerformanceCheck:
|
|
|
56
56
|
|
|
57
57
|
STATEMENT_PATTERN = re.compile(r"^(?P<lx>\s*?\|.*)$", re.M)
|
|
58
58
|
|
|
59
|
+
SCALAR_FUNCTION_PATTERN = re.compile(
|
|
60
|
+
r"^(?P<fullmatch>"
|
|
61
|
+
r"(?:export\s+)?function\s+(?P<f_name>\S+)\("
|
|
62
|
+
r"[^{]*{\n"
|
|
63
|
+
r"(?P<statements>[\s\S]*?)"
|
|
64
|
+
r"(?P<closing_bracket>^\s*})"
|
|
65
|
+
r")",
|
|
66
|
+
flags=re.M | re.VERBOSE,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
SCALAR_STATEMENT_PATTERN = re.compile(
|
|
70
|
+
r"^(?P<lx>\s*(?:\$\w+\s*=(?!=)|return\s).+)$", re.M
|
|
71
|
+
)
|
|
72
|
+
|
|
59
73
|
def __init__(self, test: Test):
|
|
60
74
|
self._test = test
|
|
61
75
|
self._performance_check_type = test.meta.get("performance", None)
|
|
@@ -135,13 +149,26 @@ class PerformanceCheck:
|
|
|
135
149
|
output = copy(self.code_module)
|
|
136
150
|
|
|
137
151
|
for timestamp_name, value, relative in calculated_diffs:
|
|
152
|
+
marker = f">>> {(value * 1000):.6f}ms [{relative:.2%}] <<< "
|
|
138
153
|
output = re.sub(
|
|
139
154
|
rf"\| eval {timestamp_name}=time\(\)",
|
|
140
|
-
|
|
155
|
+
marker,
|
|
156
|
+
output,
|
|
157
|
+
)
|
|
158
|
+
output = re.sub(
|
|
159
|
+
rf"^\${timestamp_name}\s*=\s*time\(\)\s*\n",
|
|
160
|
+
marker,
|
|
141
161
|
output,
|
|
162
|
+
flags=re.M,
|
|
142
163
|
)
|
|
143
164
|
|
|
144
165
|
output = re.sub(r"\| eval _timestamp_\w+_\d{3}=time\(\)", r"", output)
|
|
166
|
+
output = re.sub(
|
|
167
|
+
r"^\$_timestamp_\w+_\d{3}\s*=\s*time\(\)\s*\n?",
|
|
168
|
+
r"",
|
|
169
|
+
output,
|
|
170
|
+
flags=re.M,
|
|
171
|
+
)
|
|
145
172
|
|
|
146
173
|
_LOGGER.info(output)
|
|
147
174
|
|
|
@@ -154,7 +181,10 @@ class PerformanceCheck:
|
|
|
154
181
|
def _save_output(self, output: str, event_number: int) -> None:
|
|
155
182
|
"""Save output to text file"""
|
|
156
183
|
os.makedirs(self.OUTPUT_DIR, exist_ok=True)
|
|
157
|
-
|
|
184
|
+
safe_name = (
|
|
185
|
+
self._test.name.replace(os.sep, "_").replace("/", "_").replace("\\", "_")
|
|
186
|
+
)
|
|
187
|
+
filename = f"{self.OUTPUT_DIR}/performance_{safe_name}_{event_number}_{datetime.now()}.txt"
|
|
158
188
|
|
|
159
189
|
with open(filename, "w") as f:
|
|
160
190
|
f.write(output)
|
|
@@ -181,13 +211,18 @@ class PerformanceCheck:
|
|
|
181
211
|
flags=re.M,
|
|
182
212
|
)
|
|
183
213
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
214
|
+
pattern = re.compile(r"(\|\s*?into\s*?\$destination)", re.M)
|
|
215
|
+
matches = list(pattern.finditer(code_module))
|
|
216
|
+
if matches:
|
|
217
|
+
last = matches[-1]
|
|
218
|
+
replacement = (
|
|
219
|
+
"| eval _timestamp_perf_stop = time()"
|
|
220
|
+
"| eval _timestamp_perf_exec = _timestamp_perf_stop - _timestamp_perf_start "
|
|
221
|
+
+ last.group(1)
|
|
222
|
+
)
|
|
223
|
+
code_module = (
|
|
224
|
+
code_module[: last.start()] + replacement + code_module[last.end() :]
|
|
225
|
+
)
|
|
191
226
|
|
|
192
227
|
return code_module
|
|
193
228
|
|
|
@@ -196,6 +231,7 @@ class PerformanceCheck:
|
|
|
196
231
|
|
|
197
232
|
functions = self.__parse_functions(code_module)
|
|
198
233
|
pipeline = self.__parse_pipeline(code_module)
|
|
234
|
+
scalar_functions = self.__parse_scalar_functions(code_module)
|
|
199
235
|
|
|
200
236
|
functions.append(pipeline)
|
|
201
237
|
|
|
@@ -203,14 +239,37 @@ class PerformanceCheck:
|
|
|
203
239
|
new_function = self.__add_timestamps_to_function(f)
|
|
204
240
|
code_module = code_module.replace(f["fullmatch"], new_function)
|
|
205
241
|
|
|
242
|
+
for f in scalar_functions:
|
|
243
|
+
new_function = self.__add_timestamps_to_scalar_function(f)
|
|
244
|
+
code_module = code_module.replace(f["fullmatch"], new_function)
|
|
245
|
+
|
|
246
|
+
code_module = self._preserve_timestamps_through_fields(code_module)
|
|
247
|
+
|
|
206
248
|
return code_module
|
|
207
249
|
|
|
250
|
+
@staticmethod
|
|
251
|
+
def _preserve_timestamps_through_fields(code_module: str) -> str:
|
|
252
|
+
"""Replace '| fields' keep-mode with a no-op so _timestamp_perf* fields are not stripped."""
|
|
253
|
+
return re.sub(
|
|
254
|
+
r"\|\s*fields\s+(?!-)(.+)",
|
|
255
|
+
r"| eval _perf_fields_preserved=1",
|
|
256
|
+
code_module,
|
|
257
|
+
)
|
|
258
|
+
|
|
208
259
|
def __parse_functions(self, code_module: str) -> List[Dict]:
|
|
209
260
|
"""Parse all functions from code module. The finding contains groups necessary for parsing it
|
|
210
261
|
and adding timestamps"""
|
|
211
262
|
functions = [m.groupdict() for m in self.FUNCTION_PATTERN.finditer(code_module)]
|
|
212
263
|
return functions
|
|
213
264
|
|
|
265
|
+
def __parse_scalar_functions(self, code_module: str) -> List[Dict]:
|
|
266
|
+
"""Parse scalar functions (no FROM $source) that use $variable = expr assignments."""
|
|
267
|
+
dataset_fn_names = {f["f_name"] for f in self.__parse_functions(code_module)}
|
|
268
|
+
all_fns = [
|
|
269
|
+
m.groupdict() for m in self.SCALAR_FUNCTION_PATTERN.finditer(code_module)
|
|
270
|
+
]
|
|
271
|
+
return [f for f in all_fns if f["f_name"] not in dataset_fn_names]
|
|
272
|
+
|
|
214
273
|
def __parse_pipeline(self, code_module: str) -> Dict:
|
|
215
274
|
"""Parse pipeline definition from code module. The finding contains groups necessary for parsing it
|
|
216
275
|
and adding timestamps"""
|
|
@@ -249,3 +308,25 @@ class PerformanceCheck:
|
|
|
249
308
|
)
|
|
250
309
|
|
|
251
310
|
return new_function
|
|
311
|
+
|
|
312
|
+
def __add_timestamps_to_scalar_function(self, f: Dict) -> str:
|
|
313
|
+
"""Add timestamps to scalar functions that use $variable = expr assignments.
|
|
314
|
+
Injects $-prefixed variable assignments instead of | eval commands."""
|
|
315
|
+
new_function = self.SCALAR_STATEMENT_PATTERN.sub(
|
|
316
|
+
rf"$_time_{f['f_name']}_placeholder = time()\n\g<lx>",
|
|
317
|
+
f["fullmatch"],
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
func_timestamps = re.findall(
|
|
321
|
+
rf"\$_time_{f['f_name']}_placeholder", new_function
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
for n, timestamp in enumerate(func_timestamps):
|
|
325
|
+
new_function = re.sub(
|
|
326
|
+
re.escape(timestamp),
|
|
327
|
+
rf"$_timestamp_perf{f['f_name']}_{n:03d}",
|
|
328
|
+
new_function,
|
|
329
|
+
count=1,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
return new_function
|
|
@@ -177,6 +177,7 @@ class SPL2TestRunner:
|
|
|
177
177
|
_LOGGER.debug("Expected: \n%s", str(box_test.output))
|
|
178
178
|
else:
|
|
179
179
|
_LOGGER.info(f"Output check skipped (expected empty): {box_test.name}")
|
|
180
|
+
_LOGGER.debug("Actual results received: \n%s", str(output_result))
|
|
180
181
|
|
|
181
182
|
if box_test.metrics:
|
|
182
183
|
# Remove fields from actual metrics that are not in expected metrics if configured
|
|
File without changes
|
{spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/__init__.py
RENAMED
|
File without changes
|
{spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/conftest.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.5.0 → spl2_testing_framework-1.5.1}/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
|
|
File without changes
|
{spl2_testing_framework-1.5.0 → spl2_testing_framework-1.5.1}/spl2_testing_framework/tools/utils.py
RENAMED
|
File without changes
|