cucu 1.3.16__py3-none-any.whl → 1.3.17__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 cucu might be problematic. Click here for more details.
- cucu/cli/core.py +15 -36
- cucu/db.py +96 -42
- cucu/environment.py +13 -15
- cucu/formatter/rundb.py +4 -6
- cucu/reporter/html.py +265 -453
- cucu/reporter/templates/feature.html +2 -2
- cucu/reporter/templates/flat.html +7 -7
- cucu/reporter/templates/index.html +18 -18
- cucu/reporter/templates/scenario.html +34 -34
- cucu/utils.py +55 -13
- {cucu-1.3.16.dist-info → cucu-1.3.17.dist-info}/METADATA +1 -1
- {cucu-1.3.16.dist-info → cucu-1.3.17.dist-info}/RECORD +14 -14
- {cucu-1.3.16.dist-info → cucu-1.3.17.dist-info}/WHEEL +0 -0
- {cucu-1.3.16.dist-info → cucu-1.3.17.dist-info}/entry_points.txt +0 -0
cucu/cli/core.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
import json
|
|
3
2
|
import os
|
|
4
3
|
import shutil
|
|
5
4
|
import signal
|
|
5
|
+
import sqlite3
|
|
6
6
|
import sys
|
|
7
7
|
import time
|
|
8
8
|
import xml.etree.ElementTree as ET
|
|
@@ -154,12 +154,6 @@ def main():
|
|
|
154
154
|
help="the location to put the test report when --generate-report is used",
|
|
155
155
|
type=click.Path(path_type=Path),
|
|
156
156
|
)
|
|
157
|
-
@click.option(
|
|
158
|
-
"--report-only-failures",
|
|
159
|
-
default=False,
|
|
160
|
-
is_flag=True,
|
|
161
|
-
help="when set the HTML test report will only contain the failed test results",
|
|
162
|
-
)
|
|
163
157
|
@click.option(
|
|
164
158
|
"-r",
|
|
165
159
|
"--results",
|
|
@@ -228,7 +222,6 @@ def run(
|
|
|
228
222
|
preserve_results,
|
|
229
223
|
record_env_vars,
|
|
230
224
|
report,
|
|
231
|
-
report_only_failures,
|
|
232
225
|
results,
|
|
233
226
|
runtime_timeout,
|
|
234
227
|
feature_timeout,
|
|
@@ -291,9 +284,6 @@ def run(
|
|
|
291
284
|
if junit_with_stacktrace:
|
|
292
285
|
os.environ["CUCU_JUNIT_WITH_STACKTRACE"] = "true"
|
|
293
286
|
|
|
294
|
-
if report_only_failures:
|
|
295
|
-
os.environ["CUCU_REPORT_ONLY_FAILURES"] = "true"
|
|
296
|
-
|
|
297
287
|
if record_env_vars:
|
|
298
288
|
os.environ["CUCU_RECORD_ENV_VARS"] = "true"
|
|
299
289
|
|
|
@@ -514,7 +504,6 @@ def run(
|
|
|
514
504
|
_generate_report(
|
|
515
505
|
results_dir=results,
|
|
516
506
|
report_folder=report,
|
|
517
|
-
only_failures=report_only_failures,
|
|
518
507
|
junit_folder=junit,
|
|
519
508
|
)
|
|
520
509
|
|
|
@@ -522,7 +511,6 @@ def run(
|
|
|
522
511
|
def _generate_report(
|
|
523
512
|
results_dir: Path,
|
|
524
513
|
report_folder: Path,
|
|
525
|
-
only_failures: False,
|
|
526
514
|
junit_folder: Path | None = None,
|
|
527
515
|
combine: bool = False,
|
|
528
516
|
):
|
|
@@ -534,9 +522,7 @@ def _generate_report(
|
|
|
534
522
|
if results_dir.exists():
|
|
535
523
|
consolidate_database_files(results_dir, combine)
|
|
536
524
|
|
|
537
|
-
report_location = reporter.generate(
|
|
538
|
-
results_dir, report_folder, only_failures=only_failures
|
|
539
|
-
)
|
|
525
|
+
report_location = reporter.generate(results_dir, report_folder)
|
|
540
526
|
print(f"HTML test report at {report_location}")
|
|
541
527
|
|
|
542
528
|
if junit_folder:
|
|
@@ -559,12 +545,6 @@ def _generate_report(
|
|
|
559
545
|
@click.argument(
|
|
560
546
|
"results_dir", default="results", type=click.Path(path_type=Path)
|
|
561
547
|
)
|
|
562
|
-
@click.option(
|
|
563
|
-
"--only-failures",
|
|
564
|
-
default=False,
|
|
565
|
-
is_flag=True,
|
|
566
|
-
help="when set the HTML test report will only contain the failed test results",
|
|
567
|
-
)
|
|
568
548
|
@click.option(
|
|
569
549
|
"-l",
|
|
570
550
|
"--logging-level",
|
|
@@ -599,7 +579,6 @@ def _generate_report(
|
|
|
599
579
|
)
|
|
600
580
|
def report(
|
|
601
581
|
results_dir: Path,
|
|
602
|
-
only_failures,
|
|
603
582
|
logging_level,
|
|
604
583
|
show_skips,
|
|
605
584
|
output: Path,
|
|
@@ -617,23 +596,23 @@ def report(
|
|
|
617
596
|
if show_skips:
|
|
618
597
|
os.environ["CUCU_SHOW_SKIPS"] = "true"
|
|
619
598
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
599
|
+
run_db_path = results_dir / "run.db"
|
|
600
|
+
if run_db_path.exists():
|
|
601
|
+
# query cucu_run to get the original filepath used during the run
|
|
602
|
+
with sqlite3.connect(run_db_path) as conn:
|
|
603
|
+
cursor = conn.cursor()
|
|
604
|
+
cursor.execute(
|
|
605
|
+
"SELECT filepath FROM cucu_run ORDER BY start_at DESC LIMIT 1"
|
|
606
|
+
)
|
|
607
|
+
row = cursor.fetchone()
|
|
608
|
+
if row:
|
|
609
|
+
filepath = row[0]
|
|
610
|
+
# initialize any underlying custom step code things
|
|
611
|
+
behave_init(filepath)
|
|
632
612
|
|
|
633
613
|
_generate_report(
|
|
634
614
|
results_dir=results_dir,
|
|
635
615
|
report_folder=output,
|
|
636
|
-
only_failures=only_failures,
|
|
637
616
|
junit_folder=junit,
|
|
638
617
|
combine=combine,
|
|
639
618
|
)
|
cucu/db.py
CHANGED
|
@@ -5,7 +5,6 @@ Database creation and management utilities for cucu.
|
|
|
5
5
|
import logging
|
|
6
6
|
import sqlite3
|
|
7
7
|
import sys
|
|
8
|
-
from datetime import datetime
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
|
|
11
10
|
from peewee import (
|
|
@@ -20,7 +19,9 @@ from peewee import (
|
|
|
20
19
|
from playhouse.sqlite_ext import JSONField, SqliteExtDatabase
|
|
21
20
|
from tenacity import RetryError
|
|
22
21
|
|
|
22
|
+
from cucu import logger as cucu_logger
|
|
23
23
|
from cucu.config import CONFIG
|
|
24
|
+
from cucu.utils import get_iso_timestamp_with_ms, parse_iso_timestamp
|
|
24
25
|
|
|
25
26
|
db_filepath = CONFIG["RUN_DB_PATH"]
|
|
26
27
|
db = SqliteExtDatabase(db_filepath)
|
|
@@ -89,7 +90,7 @@ class scenario(BaseModel):
|
|
|
89
90
|
column_name="feature_run_id",
|
|
90
91
|
)
|
|
91
92
|
name = TextField()
|
|
92
|
-
seq =
|
|
93
|
+
seq = IntegerField(null=True)
|
|
93
94
|
status = TextField(null=True)
|
|
94
95
|
duration = FloatField(null=True)
|
|
95
96
|
start_at = DateTimeField(null=True)
|
|
@@ -124,13 +125,14 @@ class step(BaseModel):
|
|
|
124
125
|
stderr = JSONField()
|
|
125
126
|
error_message = JSONField(null=True)
|
|
126
127
|
exception = JSONField(null=True)
|
|
127
|
-
debug_output =
|
|
128
|
+
debug_output = JSONField()
|
|
128
129
|
browser_info = JSONField()
|
|
129
130
|
text = JSONField(null=True)
|
|
130
131
|
table_data = JSONField(null=True)
|
|
131
132
|
location = TextField()
|
|
132
|
-
browser_logs =
|
|
133
|
-
screenshots = JSONField(
|
|
133
|
+
browser_logs = JSONField()
|
|
134
|
+
screenshots = JSONField()
|
|
135
|
+
image_dir = TextField(null=True)
|
|
134
136
|
|
|
135
137
|
|
|
136
138
|
def record_cucu_run():
|
|
@@ -139,12 +141,11 @@ def record_cucu_run():
|
|
|
139
141
|
worker_run_id = CONFIG["WORKER_RUN_ID"]
|
|
140
142
|
|
|
141
143
|
db.connect(reuse_if_open=True)
|
|
142
|
-
start_at = datetime.now().isoformat()
|
|
143
144
|
cucu_run.create(
|
|
144
145
|
cucu_run_id=cucu_run_id_val,
|
|
145
146
|
full_arguments=sys.argv,
|
|
146
147
|
filepath=filepath,
|
|
147
|
-
start_at=
|
|
148
|
+
start_at=parse_iso_timestamp(get_iso_timestamp_with_ms()),
|
|
148
149
|
)
|
|
149
150
|
|
|
150
151
|
parent_id = (
|
|
@@ -156,7 +157,7 @@ def record_cucu_run():
|
|
|
156
157
|
worker_run_id=worker_run_id,
|
|
157
158
|
cucu_run_id=cucu_run_id_val,
|
|
158
159
|
parent_id=parent_id,
|
|
159
|
-
start_at=
|
|
160
|
+
start_at=parse_iso_timestamp(get_iso_timestamp_with_ms()),
|
|
160
161
|
)
|
|
161
162
|
|
|
162
163
|
return str(db_filepath)
|
|
@@ -173,7 +174,7 @@ def record_feature(feature_obj):
|
|
|
173
174
|
if isinstance(feature_obj.description, list)
|
|
174
175
|
else str(feature_obj.description),
|
|
175
176
|
tags=feature_obj.tags,
|
|
176
|
-
start_at=
|
|
177
|
+
start_at=parse_iso_timestamp(get_iso_timestamp_with_ms()),
|
|
177
178
|
)
|
|
178
179
|
|
|
179
180
|
|
|
@@ -186,7 +187,7 @@ def record_scenario(scenario_obj):
|
|
|
186
187
|
line_number=scenario_obj.line,
|
|
187
188
|
seq=scenario_obj.seq,
|
|
188
189
|
tags=scenario_obj.tags,
|
|
189
|
-
start_at=getattr(scenario_obj, "start_at", None),
|
|
190
|
+
start_at=parse_iso_timestamp(getattr(scenario_obj, "start_at", None)),
|
|
190
191
|
)
|
|
191
192
|
|
|
192
193
|
|
|
@@ -214,31 +215,25 @@ def start_step_record(step_obj, scenario_run_id):
|
|
|
214
215
|
has_substeps=getattr(step_obj, "has_substeps", False),
|
|
215
216
|
section_level=getattr(step_obj, "section_level", None),
|
|
216
217
|
browser_info="",
|
|
217
|
-
browser_logs=
|
|
218
|
-
|
|
218
|
+
browser_logs=[],
|
|
219
|
+
error_message=[],
|
|
220
|
+
debug_logs=[],
|
|
221
|
+
debug_output=[],
|
|
219
222
|
stderr=[],
|
|
220
223
|
stdout=[],
|
|
224
|
+
screenshots=[],
|
|
221
225
|
)
|
|
222
226
|
|
|
223
227
|
|
|
224
228
|
def finish_step_record(step_obj, duration):
|
|
225
229
|
db.connect(reuse_if_open=True)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
for screenshot in step_obj.screenshots:
|
|
229
|
-
screenshot_info = {
|
|
230
|
-
"step_name": screenshot.get("step_name"),
|
|
231
|
-
"label": screenshot.get("label"),
|
|
232
|
-
"location": screenshot.get("location"),
|
|
233
|
-
"size": screenshot.get("size"),
|
|
234
|
-
"filepath": screenshot.get("filepath"),
|
|
235
|
-
}
|
|
236
|
-
screenshot_infos.append(screenshot_info)
|
|
237
|
-
|
|
238
|
-
error_message = None
|
|
230
|
+
|
|
231
|
+
error_message = []
|
|
239
232
|
exception = []
|
|
240
233
|
if step.error_message and step_obj.status.name == "failed":
|
|
241
|
-
error_message = CONFIG.hide_secrets(
|
|
234
|
+
error_message = CONFIG.hide_secrets(
|
|
235
|
+
step_obj.error_message
|
|
236
|
+
).splitlines()
|
|
242
237
|
|
|
243
238
|
if error := step_obj.exception:
|
|
244
239
|
if isinstance(error, RetryError):
|
|
@@ -255,35 +250,30 @@ def finish_step_record(step_obj, duration):
|
|
|
255
250
|
exception = error_lines
|
|
256
251
|
|
|
257
252
|
step.update(
|
|
258
|
-
browser_info=getattr(step_obj, "browser_info",
|
|
259
|
-
browser_logs=getattr(step_obj, "browser_logs",
|
|
260
|
-
debug_output=getattr(step_obj, "debug_output",
|
|
253
|
+
browser_info=getattr(step_obj, "browser_info", {}),
|
|
254
|
+
browser_logs=getattr(step_obj, "browser_logs", []),
|
|
255
|
+
debug_output=getattr(step_obj, "debug_output", []),
|
|
261
256
|
duration=duration,
|
|
262
257
|
end_at=getattr(step_obj, "end_at", None),
|
|
263
258
|
error_message=error_message,
|
|
264
259
|
exception=exception,
|
|
265
260
|
has_substeps=getattr(step_obj, "has_substeps", False),
|
|
266
261
|
parent_seq=getattr(step_obj, "parent_seq", None),
|
|
267
|
-
screenshots=
|
|
262
|
+
screenshots=getattr(step_obj, "screenshots", []),
|
|
268
263
|
section_level=getattr(step_obj, "section_level", None),
|
|
269
264
|
seq=step_obj.seq,
|
|
270
|
-
start_at=getattr(step_obj, "start_at", None),
|
|
265
|
+
start_at=parse_iso_timestamp(getattr(step_obj, "start_at", None)),
|
|
271
266
|
status=step_obj.status.name,
|
|
272
267
|
stderr=getattr(step_obj, "stderr", []),
|
|
273
268
|
stdout=getattr(step_obj, "stdout", []),
|
|
269
|
+
image_dir=getattr(step_obj, "step_image_dir", None),
|
|
274
270
|
).where(step.step_run_id == step_obj.step_run_id).execute()
|
|
275
271
|
|
|
276
272
|
|
|
277
273
|
def finish_scenario_record(scenario_obj):
|
|
278
274
|
db.connect(reuse_if_open=True)
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
else:
|
|
282
|
-
start_at = None
|
|
283
|
-
if getattr(scenario_obj, "end_at", None):
|
|
284
|
-
end_at = datetime.fromisoformat(scenario_obj.end_at)
|
|
285
|
-
else:
|
|
286
|
-
end_at = None
|
|
275
|
+
start_at = parse_iso_timestamp(getattr(scenario_obj, "start_at", None))
|
|
276
|
+
end_at = parse_iso_timestamp(getattr(scenario_obj, "end_at", None))
|
|
287
277
|
if start_at and end_at:
|
|
288
278
|
duration = (end_at - start_at).total_seconds()
|
|
289
279
|
else:
|
|
@@ -321,7 +311,7 @@ def finish_feature_record(feature_obj):
|
|
|
321
311
|
db.connect(reuse_if_open=True)
|
|
322
312
|
feature.update(
|
|
323
313
|
status=feature_obj.status.name,
|
|
324
|
-
end_at=
|
|
314
|
+
end_at=parse_iso_timestamp(get_iso_timestamp_with_ms()),
|
|
325
315
|
custom_data=feature_obj.custom_data,
|
|
326
316
|
).where(feature.feature_run_id == feature_obj.feature_run_id).execute()
|
|
327
317
|
|
|
@@ -330,7 +320,7 @@ def finish_worker_record(custom_data=None, worker_run_id=None):
|
|
|
330
320
|
db.connect(reuse_if_open=True)
|
|
331
321
|
target_worker_run_id = worker_run_id or CONFIG["WORKER_RUN_ID"]
|
|
332
322
|
worker.update(
|
|
333
|
-
end_at=
|
|
323
|
+
end_at=parse_iso_timestamp(get_iso_timestamp_with_ms()),
|
|
334
324
|
custom_data=custom_data,
|
|
335
325
|
).where(worker.worker_run_id == target_worker_run_id).execute()
|
|
336
326
|
|
|
@@ -338,7 +328,7 @@ def finish_worker_record(custom_data=None, worker_run_id=None):
|
|
|
338
328
|
def finish_cucu_run_record():
|
|
339
329
|
db.connect(reuse_if_open=True)
|
|
340
330
|
cucu_run.update(
|
|
341
|
-
end_at=
|
|
331
|
+
end_at=parse_iso_timestamp(get_iso_timestamp_with_ms()),
|
|
342
332
|
).where(cucu_run.cucu_run_id == CONFIG["CUCU_RUN_ID"]).execute()
|
|
343
333
|
|
|
344
334
|
|
|
@@ -351,6 +341,57 @@ def create_database_file(db_filepath):
|
|
|
351
341
|
db.init(db_filepath)
|
|
352
342
|
db.connect(reuse_if_open=True)
|
|
353
343
|
db.create_tables([cucu_run, worker, feature, scenario, step])
|
|
344
|
+
db.execute_sql("""
|
|
345
|
+
CREATE VIEW IF NOT EXISTS flat_all AS
|
|
346
|
+
WITH scenario_with_steps AS (
|
|
347
|
+
SELECT
|
|
348
|
+
*,
|
|
349
|
+
COUNT(st.step_run_id) AS steps
|
|
350
|
+
FROM scenario s
|
|
351
|
+
LEFT JOIN step st ON s.scenario_run_id = st.scenario_run_id
|
|
352
|
+
GROUP BY s.scenario_run_id
|
|
353
|
+
)
|
|
354
|
+
SELECT
|
|
355
|
+
COUNT(DISTINCT s.feature_run_id) AS features,
|
|
356
|
+
COUNT(s.scenario_run_id) AS scenarios,
|
|
357
|
+
SUM(CASE WHEN s.status = 'passed' THEN 1 ELSE 0 END) AS passed,
|
|
358
|
+
SUM(CASE WHEN s.status = 'failed' THEN 1 ELSE 0 END) AS failed,
|
|
359
|
+
SUM(CASE WHEN s.status = 'skipped' THEN 1 ELSE 0 END) AS skipped,
|
|
360
|
+
SUM(CASE WHEN s.status = 'errored' THEN 1 ELSE 0 END) AS errored,
|
|
361
|
+
SUM(s.duration) AS duration,
|
|
362
|
+
SUM(s.steps) AS steps
|
|
363
|
+
FROM scenario_with_steps s
|
|
364
|
+
""")
|
|
365
|
+
db.execute_sql("""
|
|
366
|
+
CREATE VIEW IF NOT EXISTS flat_feature AS
|
|
367
|
+
WITH feature_first_level AS (
|
|
368
|
+
SELECT
|
|
369
|
+
w.cucu_run_id,
|
|
370
|
+
f.start_at,
|
|
371
|
+
f.name AS feature_name,
|
|
372
|
+
COUNT(s.scenario_run_id) AS scenarios,
|
|
373
|
+
SUM(CASE WHEN s.status = 'passed' THEN 1 ELSE 0 END) AS passed,
|
|
374
|
+
SUM(CASE WHEN s.status = 'failed' THEN 1 ELSE 0 END) AS failed,
|
|
375
|
+
SUM(CASE WHEN s.status = 'skipped' THEN 1 ELSE 0 END) AS skipped,
|
|
376
|
+
SUM(CASE WHEN s.status = 'errored' THEN 1 ELSE 0 END) AS errored,
|
|
377
|
+
SUM(s.duration) AS duration
|
|
378
|
+
FROM cucu_run r
|
|
379
|
+
JOIN worker w ON r.cucu_run_id = w.cucu_run_id
|
|
380
|
+
JOIN feature f ON w.worker_run_id = f.worker_run_id
|
|
381
|
+
JOIN scenario s ON f.feature_run_id = s.feature_run_id
|
|
382
|
+
GROUP BY f.feature_run_id
|
|
383
|
+
)
|
|
384
|
+
SELECT
|
|
385
|
+
*,
|
|
386
|
+
CASE
|
|
387
|
+
WHEN failed > 0 THEN 'failed'
|
|
388
|
+
WHEN errored > 0 THEN 'errored'
|
|
389
|
+
WHEN passed > 0 THEN 'passed'
|
|
390
|
+
WHEN skipped > 0 THEN 'skipped'
|
|
391
|
+
END AS status
|
|
392
|
+
FROM feature_first_level
|
|
393
|
+
ORDER BY start_at ASC
|
|
394
|
+
""")
|
|
354
395
|
db.execute_sql("""
|
|
355
396
|
CREATE VIEW IF NOT EXISTS flat AS
|
|
356
397
|
SELECT
|
|
@@ -416,7 +457,12 @@ def consolidate_database_files(results_dir, combine=False):
|
|
|
416
457
|
results_path = Path(results_dir)
|
|
417
458
|
target_db_path = results_path / "run.db"
|
|
418
459
|
if not target_db_path.exists():
|
|
460
|
+
cucu_logger.info(
|
|
461
|
+
f"Creating new consolidated database at {target_db_path}"
|
|
462
|
+
)
|
|
419
463
|
create_database_file(target_db_path)
|
|
464
|
+
else:
|
|
465
|
+
cucu_logger.debug(f"Found existing database at {target_db_path}")
|
|
420
466
|
|
|
421
467
|
if not combine:
|
|
422
468
|
db_files = [
|
|
@@ -428,6 +474,14 @@ def consolidate_database_files(results_dir, combine=False):
|
|
|
428
474
|
db for db in results_path.rglob("run*.db") if db != Path("run.db")
|
|
429
475
|
]
|
|
430
476
|
|
|
477
|
+
if not db_files:
|
|
478
|
+
cucu_logger.debug("No database files found to consolidate.")
|
|
479
|
+
return
|
|
480
|
+
else:
|
|
481
|
+
cucu_logger.debug(
|
|
482
|
+
f"Found {len(db_files)} database files to consolidate."
|
|
483
|
+
)
|
|
484
|
+
|
|
431
485
|
tables_to_copy = ["cucu_run", "worker", "feature", "scenario", "step"]
|
|
432
486
|
with sqlite3.connect(target_db_path) as target_conn:
|
|
433
487
|
target_cursor = target_conn.cursor()
|
cucu/environment.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import datetime
|
|
2
1
|
import json
|
|
3
2
|
import sys
|
|
4
3
|
import traceback
|
|
@@ -13,6 +12,8 @@ from cucu.page_checks import init_page_checks
|
|
|
13
12
|
from cucu.utils import (
|
|
14
13
|
TeeStream,
|
|
15
14
|
ellipsize_filename,
|
|
15
|
+
get_iso_timestamp_with_ms,
|
|
16
|
+
parse_iso_timestamp,
|
|
16
17
|
take_screenshot,
|
|
17
18
|
)
|
|
18
19
|
|
|
@@ -90,7 +91,7 @@ def before_scenario(ctx, scenario):
|
|
|
90
91
|
|
|
91
92
|
# reset the step timer dictionary
|
|
92
93
|
ctx.step_timers = {}
|
|
93
|
-
scenario.start_at =
|
|
94
|
+
scenario.start_at = get_iso_timestamp_with_ms()
|
|
94
95
|
|
|
95
96
|
if config.CONFIG["CUCU_RESULTS_DIR"] is not None:
|
|
96
97
|
ctx.scenario_dir = ctx.feature_dir / ellipsize_filename(scenario.name)
|
|
@@ -205,7 +206,7 @@ def after_scenario(ctx, scenario):
|
|
|
205
206
|
CONFIG.to_yaml_without_secrets()
|
|
206
207
|
)
|
|
207
208
|
|
|
208
|
-
scenario.end_at =
|
|
209
|
+
scenario.end_at = get_iso_timestamp_with_ms()
|
|
209
210
|
|
|
210
211
|
|
|
211
212
|
def download_mht_data(ctx):
|
|
@@ -237,7 +238,7 @@ def cleanup_browsers(ctx):
|
|
|
237
238
|
|
|
238
239
|
|
|
239
240
|
def before_step(ctx, step):
|
|
240
|
-
step.start_at =
|
|
241
|
+
step.start_at = get_iso_timestamp_with_ms()
|
|
241
242
|
|
|
242
243
|
sys.stdout.captured()
|
|
243
244
|
sys.stderr.captured()
|
|
@@ -268,16 +269,16 @@ def after_step(ctx, step):
|
|
|
268
269
|
|
|
269
270
|
# Capture debug output from the TeeStream for this step
|
|
270
271
|
if hasattr(ctx, "scenario_debug_log_tee"):
|
|
271
|
-
step.debug_output = ctx.scenario_debug_log_tee.getvalue()
|
|
272
|
+
step.debug_output = ctx.scenario_debug_log_tee.getvalue().splitlines()
|
|
272
273
|
else:
|
|
273
|
-
step.debug_output =
|
|
274
|
+
step.debug_output = []
|
|
274
275
|
|
|
275
|
-
step.end_at =
|
|
276
|
+
step.end_at = get_iso_timestamp_with_ms()
|
|
276
277
|
|
|
277
278
|
# calculate duration from ISO timestamps
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
279
|
+
ctx.scenario.previous_step_duration = (
|
|
280
|
+
parse_iso_timestamp(step.end_at) - parse_iso_timestamp(step.start_at)
|
|
281
|
+
).total_seconds()
|
|
281
282
|
|
|
282
283
|
# when set this means we're running in parallel mode using --workers and
|
|
283
284
|
# we want to see progress reported using simply dots
|
|
@@ -325,16 +326,13 @@ def after_step(ctx, step):
|
|
|
325
326
|
hook(ctx)
|
|
326
327
|
|
|
327
328
|
# Capture browser logs and info for this step
|
|
328
|
-
step.browser_logs =
|
|
329
|
-
|
|
329
|
+
step.browser_logs = []
|
|
330
330
|
browser_info = {"has_browser": False}
|
|
331
331
|
if ctx.browser:
|
|
332
|
-
browser_logs = []
|
|
333
332
|
for log in ctx.browser.get_log():
|
|
334
333
|
log_entry = json.dumps(log)
|
|
335
|
-
browser_logs.append(
|
|
334
|
+
step.browser_logs.append(log)
|
|
336
335
|
ctx.browser_log_tee.write(f"{log_entry}\n")
|
|
337
|
-
step.browser_logs = "\n".join(browser_logs)
|
|
338
336
|
|
|
339
337
|
tab_info = ctx.browser.get_tab_info()
|
|
340
338
|
|
cucu/formatter/rundb.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
from __future__ import absolute_import
|
|
3
3
|
|
|
4
|
-
import datetime
|
|
5
4
|
import os
|
|
6
5
|
import time
|
|
7
6
|
from pathlib import Path
|
|
@@ -26,6 +25,7 @@ from cucu.db import (
|
|
|
26
25
|
)
|
|
27
26
|
from cucu.utils import (
|
|
28
27
|
generate_short_id,
|
|
28
|
+
get_iso_timestamp_with_ms,
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
|
|
@@ -125,7 +125,7 @@ class RundbFormatter(Formatter):
|
|
|
125
125
|
for index, step in enumerate(self.this_steps):
|
|
126
126
|
if getattr(step, "seq", -1) == -1:
|
|
127
127
|
step.seq = index + 1 # 1-based sequence
|
|
128
|
-
finish_step_record(step,
|
|
128
|
+
finish_step_record(step, 0)
|
|
129
129
|
|
|
130
130
|
finish_scenario_record(self.this_scenario)
|
|
131
131
|
|
|
@@ -137,7 +137,7 @@ class RundbFormatter(Formatter):
|
|
|
137
137
|
|
|
138
138
|
self.this_scenario = scenario
|
|
139
139
|
self.this_steps = []
|
|
140
|
-
self.next_start_at =
|
|
140
|
+
self.next_start_at = get_iso_timestamp_with_ms()
|
|
141
141
|
scenario_run_id_seed = (
|
|
142
142
|
f"{scenario.feature.feature_run_id}_{time.perf_counter()}"
|
|
143
143
|
)
|
|
@@ -185,9 +185,7 @@ class RundbFormatter(Formatter):
|
|
|
185
185
|
def result(self, step):
|
|
186
186
|
"""Called after processing a step result is known, applies to executed/skipped too."""
|
|
187
187
|
step.start_at = self.next_start_at
|
|
188
|
-
self.next_start_at = step.end_at =
|
|
189
|
-
:-3
|
|
190
|
-
]
|
|
188
|
+
self.next_start_at = step.end_at = get_iso_timestamp_with_ms()
|
|
191
189
|
previous_step_duration = getattr(
|
|
192
190
|
self.this_scenario, "previous_step_duration", 0
|
|
193
191
|
)
|