Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit aecdcfb

Browse files
[conftest] Advanced processing of logging
This patch does the following things: - it processes the calls of logging.error as test errors - it prints the number of errors/warnings for each test
1 parent 2090fbc commit aecdcfb

File tree

1 file changed

+209
-3
lines changed

1 file changed

+209
-3
lines changed

tests/conftest.py

Lines changed: 209 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import pathlib
99
import math
1010
import datetime
11+
import typing
1112

1213
import _pytest.outcomes
1314
import _pytest.unittest
@@ -212,6 +213,12 @@ def helper__build_test_id(item: pytest.Function) -> str:
212213

213214
return testID
214215

216+
217+
# /////////////////////////////////////////////////////////////////////////////
218+
219+
g_error_msg_count_key = pytest.StashKey[int]()
220+
g_warning_msg_count_key = pytest.StashKey[int]()
221+
215222
# /////////////////////////////////////////////////////////////////////////////
216223

217224

@@ -285,6 +292,16 @@ def helper__makereport__call(
285292
assert type(call) == pytest.CallInfo # noqa: E721
286293
assert type(outcome) == pluggy.Result # noqa: E721
287294

295+
# --------
296+
item_error_msg_count = item.stash.get(g_error_msg_count_key, 0)
297+
assert type(item_error_msg_count) == int # noqa: E721
298+
assert item_error_msg_count >= 0
299+
300+
item_warning_msg_count = item.stash.get(g_warning_msg_count_key, 0)
301+
assert type(item_warning_msg_count) == int # noqa: E721
302+
assert item_warning_msg_count >= 0
303+
304+
# --------
288305
rep = outcome.get_result()
289306
assert rep is not None
290307
assert type(rep) == pytest.TestReport # noqa: E721
@@ -336,6 +353,7 @@ def helper__makereport__call(
336353
reasonMsgTempl = "XFAIL REASON: {0}"
337354

338355
logging.error(call.excinfo.value)
356+
item_error_msg_count += 1
339357

340358
assert type(reasonText) == str # noqa: E721
341359

@@ -350,7 +368,13 @@ def helper__makereport__call(
350368

351369
TEST_PROCESS_STATS.incrementFailedTestCount(testID)
352370

353-
logging.error(call.excinfo.value)
371+
if type(call.excinfo.value) == SIGNAL_EXCEPTION: # noqa: E721
372+
assert item_error_msg_count > 0
373+
pass
374+
else:
375+
logging.error(call.excinfo.value)
376+
item_error_msg_count += 1
377+
354378
exitStatus = "FAILED"
355379
elif rep.outcome == "passed":
356380
assert call.excinfo is None
@@ -380,9 +404,11 @@ def helper__makereport__call(
380404

381405
# --------
382406
logging.info("*")
383-
logging.info("* DURATION : {0}".format(timedelta_to_human_text(testDurration)))
407+
logging.info("* DURATION : {0}".format(timedelta_to_human_text(testDurration)))
384408
logging.info("*")
385-
logging.info("* EXIT STATUS : {0}".format(exitStatus))
409+
logging.info("* EXIT STATUS : {0}".format(exitStatus))
410+
logging.info("* ERROR COUNT : {0}".format(item_error_msg_count))
411+
logging.info("* WARNING COUNT: {0}".format(item_warning_msg_count))
386412
logging.info("*")
387413
logging.info("* STOP TEST {0}".format(testID))
388414
logging.info("*")
@@ -437,6 +463,186 @@ def pytest_runtest_makereport(item: pytest.Function, call: pytest.CallInfo):
437463
# /////////////////////////////////////////////////////////////////////////////
438464

439465

466+
class LogErrorWrapper2:
467+
_old_method: any
468+
_counter: typing.Optional[int]
469+
470+
# --------------------------------------------------------------------
471+
def __init__(self):
472+
self._old_method = None
473+
self._counter = None
474+
475+
# --------------------------------------------------------------------
476+
def __enter__(self):
477+
assert self._old_method is None
478+
assert self._counter is None
479+
480+
self._old_method = logging.error
481+
self._counter = 0
482+
483+
logging.error = self
484+
return self
485+
486+
# --------------------------------------------------------------------
487+
def __exit__(self, exc_type, exc_val, exc_tb):
488+
assert self._old_method is not None
489+
assert self._counter is not None
490+
491+
assert logging.error is self
492+
493+
logging.error = self._old_method
494+
495+
self._old_method = None
496+
self._counter = None
497+
return False
498+
499+
# --------------------------------------------------------------------
500+
def __call__(self, *args, **kwargs):
501+
assert self._old_method is not None
502+
assert self._counter is not None
503+
504+
assert type(self._counter) == int # noqa: E721
505+
assert self._counter >= 0
506+
507+
r = self._old_method(*args, **kwargs)
508+
509+
self._counter += 1
510+
assert self._counter > 0
511+
512+
return r
513+
514+
515+
# /////////////////////////////////////////////////////////////////////////////
516+
517+
518+
class LogWarningWrapper2:
519+
_old_method: any
520+
_counter: typing.Optional[int]
521+
522+
# --------------------------------------------------------------------
523+
def __init__(self):
524+
self._old_method = None
525+
self._counter = None
526+
527+
# --------------------------------------------------------------------
528+
def __enter__(self):
529+
assert self._old_method is None
530+
assert self._counter is None
531+
532+
self._old_method = logging.warning
533+
self._counter = 0
534+
535+
logging.warning = self
536+
return self
537+
538+
# --------------------------------------------------------------------
539+
def __exit__(self, exc_type, exc_val, exc_tb):
540+
assert self._old_method is not None
541+
assert self._counter is not None
542+
543+
assert logging.warning is self
544+
545+
logging.warning = self._old_method
546+
547+
self._old_method = None
548+
self._counter = None
549+
return False
550+
551+
# --------------------------------------------------------------------
552+
def __call__(self, *args, **kwargs):
553+
assert self._old_method is not None
554+
assert self._counter is not None
555+
556+
assert type(self._counter) == int # noqa: E721
557+
assert self._counter >= 0
558+
559+
r = self._old_method(*args, **kwargs)
560+
561+
self._counter += 1
562+
assert self._counter > 0
563+
564+
return r
565+
566+
567+
# /////////////////////////////////////////////////////////////////////////////
568+
569+
570+
class SIGNAL_EXCEPTION(Exception):
571+
def __init__(self):
572+
pass
573+
574+
575+
# /////////////////////////////////////////////////////////////////////////////
576+
577+
578+
@pytest.hookimpl(hookwrapper=True)
579+
def pytest_pyfunc_call(pyfuncitem: pytest.Function):
580+
assert pyfuncitem is not None
581+
assert isinstance(pyfuncitem, pytest.Function)
582+
583+
debug__log_error_method = logging.error
584+
assert debug__log_error_method is not None
585+
586+
debug__log_warning_method = logging.warning
587+
assert debug__log_warning_method is not None
588+
589+
pyfuncitem.stash[g_error_msg_count_key] = 0
590+
pyfuncitem.stash[g_warning_msg_count_key] = 0
591+
592+
try:
593+
with LogErrorWrapper2() as logErrorWrapper, LogWarningWrapper2() as logWarningWrapper:
594+
assert type(logErrorWrapper) == LogErrorWrapper2 # noqa: E721
595+
assert logErrorWrapper._old_method is not None
596+
assert type(logErrorWrapper._counter) == int # noqa: E721
597+
assert logErrorWrapper._counter == 0
598+
assert logging.error is logErrorWrapper
599+
600+
assert type(logWarningWrapper) == LogWarningWrapper2 # noqa: E721
601+
assert logWarningWrapper._old_method is not None
602+
assert type(logWarningWrapper._counter) == int # noqa: E721
603+
assert logWarningWrapper._counter == 0
604+
assert logging.warning is logWarningWrapper
605+
606+
r: pluggy.Result = yield
607+
608+
assert r is not None
609+
assert type(r) == pluggy.Result # noqa: E721
610+
611+
assert logErrorWrapper._old_method is not None
612+
assert type(logErrorWrapper._counter) == int # noqa: E721
613+
assert logErrorWrapper._counter >= 0
614+
assert logging.error is logErrorWrapper
615+
616+
assert logWarningWrapper._old_method is not None
617+
assert type(logWarningWrapper._counter) == int # noqa: E721
618+
assert logWarningWrapper._counter >= 0
619+
assert logging.warning is logWarningWrapper
620+
621+
assert g_error_msg_count_key in pyfuncitem.stash
622+
assert g_warning_msg_count_key in pyfuncitem.stash
623+
624+
assert pyfuncitem.stash[g_error_msg_count_key] == 0
625+
assert pyfuncitem.stash[g_warning_msg_count_key] == 0
626+
627+
pyfuncitem.stash[g_error_msg_count_key] = logErrorWrapper._counter
628+
pyfuncitem.stash[g_warning_msg_count_key] = logWarningWrapper._counter
629+
630+
if r.exception is not None:
631+
pass
632+
elif logErrorWrapper._counter == 0:
633+
pass
634+
else:
635+
assert logErrorWrapper._counter > 0
636+
r.force_exception(SIGNAL_EXCEPTION())
637+
finally:
638+
assert logging.error is debug__log_error_method
639+
assert logging.warning is debug__log_warning_method
640+
pass
641+
642+
643+
# /////////////////////////////////////////////////////////////////////////////
644+
645+
440646
def helper__calc_W(n: int) -> int:
441647
assert n > 0
442648

0 commit comments

Comments
 (0)