From 9596b210c5f1d7601d8939ed71e5e18ccde4c652 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Wed, 2 Dec 2020 09:39:01 +0100 Subject: add test.report_fragment() Allow enriching the junit output with arbitrary subtasks within a test. The current aim is, for handover tests, to not just show that a test failed, but to show exactly which steps worked and which didn't, e.g.: handover.py/01_bts0_started PASSED handover.py/02.1_ms0_attach PASSED handover.py/02.2_ms1_attach PASSED handover.py/02.3_subscribed_in_msc PASSED handover.py/03_call_established PASSED handover.py/04.1_bts1_started FAILED In this case it is immediately obvious from looking at the jenkins results analyzer that bts1 is the cause of the test failure, and it is visible which parts of the test are flaky, over time. First user Will be the upcoming handover_2G suite, in I0b2671304165a1aaae2b386af46fbd8b098e3bd8. Change-Id: I4ca9100b6f8db24d1f7e0a09b3b7ba88b8ae3b59 --- selftest/suite_test/suite_test.ok | 75 +++++++++++++++++++--- selftest/suite_test/suite_test.ok.ign | 5 ++ selftest/suite_test/suite_test.py | 12 ++++ .../suitedirA/test_suite/test_report_fragment.py | 11 ++++ 4 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 selftest/suite_test/suitedirA/test_suite/test_report_fragment.py (limited to 'selftest') diff --git a/selftest/suite_test/suite_test.ok b/selftest/suite_test/suite_test.ok index 3790e1a..9f60b70 100644 --- a/selftest/suite_test/suite_test.ok +++ b/selftest/suite_test/suite_test.ok @@ -168,15 +168,67 @@ tst hello_world.py:[LINENR] Test passed (N.N sec) [test_suite↪hello_world.py] --------------------------------------------------------------------- trial test_suite PASS --------------------------------------------------------------------- -PASS: test_suite (pass: 1, skip: 7) +PASS: test_suite (pass: 1, skip: 8) pass: hello_world.py (N.N sec) skip: mo_mt_sms.py skip: mo_sms.py skip: test_error.py skip: test_fail.py skip: test_fail_raise.py + skip: test_report_fragment.py skip: test_suite_params.py skip: test_timeout.py +- run report fragment test + +--------------------------------------------------------------------- +trial test_suite +--------------------------------------------------------------------- + +---------------------------------------------- +trial test_suite test_report_fragment.py +---------------------------------------------- +tst test_report_fragment.py:[LINENR]: a step in the first fragment [test_suite↪test_report_fragment.py:[LINENR]] +tst test_report_fragment.py:[LINENR]: ----- Report fragment: test_suite/test_report_fragment.py/fragment1: pass (N.Ns) [test_suite↪test_report_fragment.py:[LINENR]] +tst test_report_fragment.py:[LINENR]: a step in the second fragment [test_suite↪test_report_fragment.py:[LINENR]] +tst test_report_fragment.py:[LINENR]: ----- Report fragment: test_suite/test_report_fragment.py/fragment2: pass (N.Ns) [test_suite↪test_report_fragment.py:[LINENR]] +tst test_report_fragment.py:[LINENR]: a step in the third fragment [test_suite↪test_report_fragment.py:[LINENR]] +tst test_report_fragment.py:[LINENR]: ----- Report fragment: test_suite/test_report_fragment.py/fragment3: FAIL (N.Ns) [test_suite↪test_report_fragment.py:[LINENR]] +tst test_report_fragment.py:[LINENR]: ERR: Exception: failure in the third fragment [test_suite↪test_report_fragment.py:[LINENR]] +tst test_report_fragment.py:[LINENR]: Test FAILED (N.N sec) [test_suite↪test_report_fragment.py:[LINENR]] +--------------------------------------------------------------------- +trial test_suite FAIL +--------------------------------------------------------------------- +FAIL: test_suite (fail: 1, skip: 8) + skip: hello_world.py (N.N sec) + skip: mo_mt_sms.py + skip: mo_sms.py + skip: test_error.py + skip: test_fail.py + skip: test_fail_raise.py + FAIL: test_report_fragment.py (N.N sec) Exception: failure in the third fragment + skip: test_suite_params.py + skip: test_timeout.py + + +################################### junit XML: + +---------------------------------------------- +trial test_suite hello_world.py +---------------------------------------------- +[TIMESTAMP] tst hello_world.py:[LINENR]: hello world +[TIMESTAMP] tst hello_world.py:[LINENR]: I am 'test_suite' / 'hello_world.py' +[TIMESTAMP] tst hello_world.py:[LINENR]: one +two +three +[TIMESTAMP] tst hello_world.py:[LINENR] Test passed (N.N sec) +test log file not availabletest log file not availabletest log file not availabletest log file not availabletest log file not availablefailure in the third fragment[BACKTRACE] +raise Exception('failure in the third fragment') + +Exception: failure in the third fragment +test log file not availabletest log file not available +################################### + + - a test with an error @@ -193,13 +245,14 @@ tst test_error.py:[LINENR]: Test FAILED (N.N sec) [test_suite↪test_error.py:[ --------------------------------------------------------------------- trial test_suite FAIL --------------------------------------------------------------------- -FAIL: test_suite (fail: 1, skip: 7) +FAIL: test_suite (fail: 1, skip: 8) skip: hello_world.py (N.N sec) skip: mo_mt_sms.py skip: mo_sms.py FAIL: test_error.py (N.N sec) AssertionError: test_error.py:[LINENR]: assert False skip: test_fail.py skip: test_fail_raise.py + skip: test_report_fragment.py (N.N sec) skip: test_suite_params.py skip: test_timeout.py @@ -218,13 +271,14 @@ tst test_fail.py:[LINENR]: Test FAILED (N.N sec) [test_suite↪test_fail.py:[LI --------------------------------------------------------------------- trial test_suite FAIL --------------------------------------------------------------------- -FAIL: test_suite (fail: 1, skip: 7) +FAIL: test_suite (fail: 1, skip: 8) skip: hello_world.py (N.N sec) skip: mo_mt_sms.py skip: mo_sms.py skip: test_error.py (N.N sec) FAIL: test_fail.py (N.N sec) EpicFail: This failure is expected skip: test_fail_raise.py + skip: test_report_fragment.py (N.N sec) skip: test_suite_params.py skip: test_timeout.py @@ -242,13 +296,14 @@ tst test_fail_raise.py:[LINENR]: Test FAILED (N.N sec) [test_suite↪test_fail_ --------------------------------------------------------------------- trial test_suite FAIL --------------------------------------------------------------------- -FAIL: test_suite (fail: 1, skip: 7) +FAIL: test_suite (fail: 1, skip: 8) skip: hello_world.py (N.N sec) skip: mo_mt_sms.py skip: mo_sms.py skip: test_error.py (N.N sec) skip: test_fail.py (N.N sec) FAIL: test_fail_raise.py (N.N sec) ExpectedFail: This failure is expected + skip: test_report_fragment.py (N.N sec) skip: test_suite_params.py skip: test_timeout.py - test with half empty scenario @@ -397,13 +452,14 @@ tst hello_world.py:[LINENR] Test passed (N.N sec) [test_suite↪hello_world.py] --------------------------------------------------------------------- trial test_suite PASS --------------------------------------------------------------------- -PASS: test_suite (pass: 1, skip: 7) +PASS: test_suite (pass: 1, skip: 8) pass: hello_world.py (N.N sec) skip: mo_mt_sms.py skip: mo_sms.py skip: test_error.py skip: test_fail.py skip: test_fail_raise.py + skip: test_report_fragment.py skip: test_suite_params.py skip: test_timeout.py - test with scenario @@ -552,13 +608,14 @@ tst hello_world.py:[LINENR] Test passed (N.N sec) [test_suite↪hello_world.py] --------------------------------------------------------------------- trial test_suite PASS --------------------------------------------------------------------- -PASS: test_suite (pass: 1, skip: 7) +PASS: test_suite (pass: 1, skip: 8) pass: hello_world.py (N.N sec) skip: mo_mt_sms.py skip: mo_sms.py skip: test_error.py skip: test_fail.py skip: test_fail_raise.py + skip: test_report_fragment.py skip: test_suite_params.py skip: test_timeout.py - test with scenario and modifiers @@ -753,13 +810,14 @@ tst hello_world.py:[LINENR] Test passed (N.N sec) [test_suite↪hello_world.py] --------------------------------------------------------------------- trial test_suite PASS --------------------------------------------------------------------- -PASS: test_suite (pass: 1, skip: 7) +PASS: test_suite (pass: 1, skip: 8) pass: hello_world.py (N.N sec) skip: mo_mt_sms.py skip: mo_sms.py skip: test_error.py skip: test_fail.py skip: test_fail_raise.py + skip: test_report_fragment.py skip: test_suite_params.py skip: test_timeout.py - test with suite-specific config @@ -962,13 +1020,14 @@ tst test_timeout.py:[LINENR]: Test FAILED (N.N sec) [test_suite↪test_timeout. --------------------------------------------------------------------- trial test_suite FAIL --------------------------------------------------------------------- -FAIL: test_suite (fail: 1, pass: 1, skip: 6) +FAIL: test_suite (fail: 1, pass: 1, skip: 7) skip: hello_world.py skip: mo_mt_sms.py skip: mo_sms.py skip: test_error.py skip: test_fail.py skip: test_fail_raise.py + skip: test_report_fragment.py pass: test_suite_params.py (N.N sec) FAIL: test_timeout.py (N.N sec) Error: test_timeout.py:[LINENR] Test Timeout triggered: 1 seconds elapsed [test_suite↪test_timeout.py:[LINENR]↪test_timeout.py] - test with template overlay diff --git a/selftest/suite_test/suite_test.ok.ign b/selftest/suite_test/suite_test.ok.ign index 460da92..d8f0224 100644 --- a/selftest/suite_test/suite_test.ok.ign +++ b/selftest/suite_test/suite_test.ok.ign @@ -1,6 +1,11 @@ /[^ ]*/selftest/ [PATH]/selftest/ \.py:[0-9]* .py:[LINENR] \([0-9.]+ sec\) (N.N sec) +\([0-9.]+s\) (N.Ns) {combining_scenarios='resources', scenario='foo'}:.* {combining_scenarios='resources', scenario='foo'}: [RESOURCE_DICT] test_suite-[0-9]*-[0-9]* test_suite-[ID_NUM]-[ID_NUM] suiteC-[0-9]*-[0-9]* suiteC-[ID_NUM]-[ID_NUM] +line [0-9]+ line [LINENR] +[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9]+ [TIMESTAMP] +time="[0-9]+" time="[VAL]" +timestamp="[^"]+" timestamp="[TIMESTAMP]" diff --git a/selftest/suite_test/suite_test.py b/selftest/suite_test/suite_test.py index 9708037..be4b3c4 100755 --- a/selftest/suite_test/suite_test.py +++ b/selftest/suite_test/suite_test.py @@ -3,6 +3,7 @@ import os import sys import _prep import shutil +import re from osmo_gsm_tester.core import log from osmo_gsm_tester.core import config from osmo_gsm_tester.core import util @@ -11,6 +12,8 @@ from osmo_gsm_tester.core import scenario from osmo_gsm_tester.core import suite from osmo_gsm_tester.core.schema import generate_schemas, get_all_schema +import xml.etree.ElementTree as et + config.override_conf = os.path.join(os.path.dirname(sys.argv[0]), 'paths.conf') example_trial_dir = os.path.join('test_trial_tmp') @@ -51,6 +54,15 @@ s = suite.SuiteRun(trial, 'test_suite', s_def) results = s.run_tests('hello_world.py') print(report.suite_to_text(s)) +print('- run report fragment test') +results = s.run_tests('test_report_fragment.py') +print(report.suite_to_text(s)) +xml = et.tostring(report.suite_to_junit(s)).decode('utf-8') +xml = re.sub('Traceback.*raise', '[BACKTRACE]\nraise', xml, flags=re.M + re.DOTALL) +print('\n\n################################### junit XML:\n' + + xml + + '\n###################################\n\n') + log.style_change(src=True) #log.style_change(trace=True) print('\n- a test with an error') diff --git a/selftest/suite_test/suitedirA/test_suite/test_report_fragment.py b/selftest/suite_test/suitedirA/test_suite/test_report_fragment.py new file mode 100644 index 0000000..06ff37d --- /dev/null +++ b/selftest/suite_test/suitedirA/test_suite/test_report_fragment.py @@ -0,0 +1,11 @@ +from osmo_gsm_tester.testenv import * + +with test.report_fragment('fragment1'): + print('a step in the first fragment') + +with test.report_fragment('fragment2'): + print('a step in the second fragment') + +with test.report_fragment('fragment3'): + print('a step in the third fragment') + raise Exception('failure in the third fragment') -- cgit v1.2.3