从分段输出文件中提取第一行和最后一行的算法
Algorithm for extracting first and last lines from sectionalized output file
我试图从 Pytest 会话的终端输出中逐行解析 FAILURES 部分,识别每个测试的测试名称和测试文件名,然后我想将它们附加在一起以形成“完全合格的测试”名称”(FQTN),例如tests/test_1.py::test_3_fails
。我还想获取并保存回溯信息(测试名称和测试文件名之间的信息)。
解析部分很简单,我已经有了匹配测试名称和测试文件名的工作正则表达式,我可以基于它提取回溯信息。我的 FQTN 问题是算法 - 我似乎无法弄清楚识别测试名称的整体逻辑,然后是测试的文件名,wihch 出现在后面的一行。我不仅需要适应 FAILURES 部分中间的测试,还需要适应 FAILURES 部分的第一个测试和最后一个测试。
举个例子。这是测试期间所有失败的输出部分 运行,以及一些出现在失败之前和之后的终端输出。
.
.
.
============================================== ERRORS ===============================================
__________________________________ ERROR at setup of test_c_error ___________________________________
@pytest.fixture
def error_fixture():
> assert 0
E assert 0
tests/test_2.py:19: AssertionError
============================================= FAILURES ==============================================
___________________________________________ test_3_fails ____________________________________________
log_testname = None
def test_3_fails(log_testname):
> assert 0
E assert 0
tests/test_1.py:98: AssertionError
--------------------------------------- Captured stdout setup ---------------------------------------
Running test tests.test_1...
Running test tests.test_1...
Setting test up...
Setting test up...
Executing test...
Executing test...
Tearing test down...
Tearing test down...
---------------------------------------- Captured log setup -----------------------------------------
INFO root:test_1.py:68 Running test tests.test_1...
INFO root:test_1.py:69 Setting test up...
INFO root:test_1.py:70 Executing test...
INFO root:test_1.py:72 Tearing test down...
______________________________________ test_8_causes_a_warning ______________________________________
log_testname = None
def test_8_causes_a_warning(log_testname):
> assert api_v1() == 1
E TypeError: api_v1() missing 1 required positional argument: 'log_testname'
tests/test_1.py:127: TypeError
--------------------------------------- Captured stdout setup ---------------------------------------
Running test tests.test_1...
Running test tests.test_1...
Setting test up...
Setting test up...
Executing test...
Executing test...
Tearing test down...
Tearing test down...
---------------------------------------- Captured log setup -----------------------------------------
INFO root:test_1.py:68 Running test tests.test_1...
INFO root:test_1.py:69 Setting test up...
INFO root:test_1.py:70 Executing test...
INFO root:test_1.py:72 Tearing test down...
___________________________ test_16_fail_compare_dicts_for_pytest_icdiff ____________________________
def test_16_fail_compare_dicts_for_pytest_icdiff():
listofStrings = ["Hello", "hi", "there", "at", "this"]
listofInts = [7, 10, 45, 23, 77]
assert len(listofStrings) == len(listofInts)
> assert listofStrings == listofInts
E AssertionError: assert ['Hello', 'hi... 'at', 'this'] == [7, 10, 45, 23, 77]
E At index 0 diff: 'Hello' != 7
E Full diff:
E - [7, 10, 45, 23, 77]
E + ['Hello', 'hi', 'there', 'at', 'this']
tests/test_1.py:210: AssertionError
____________________________________________ test_b_fail ____________________________________________
def test_b_fail():
> assert 0
E assert 0
tests/test_2.py:27: AssertionError
============================================== PASSES ===============================================
___________________________________________ test_4_passes ___________________________________________
--------------------------------------- Captured stdout setup ---------------------------------------
Running test tests.test_1...
Running test tests.test_1...
Setting test up...
Setting test up...
Executing test...
Executing test...
Tearing test down...
Tearing test down...
.
.
.
这里有人精通算法吗,也许是一些伪代码,显示了获取每个测试名称及其相关测试文件名的总体方法?
这是我为测试用例报告获取呈现摘要的建议。使用这个存根作为一个粗略的想法 - 您可能想要遍历报告并首先转储呈现的摘要,然后使用 curses 魔法来显示收集的数据。
一些测试:
import pytest
def test_1():
assert False
def test_2():
raise RuntimeError('call error')
@pytest.fixture
def f():
raise RuntimeError('setup error')
def test_3(f):
assert True
@pytest.fixture
def g():
yield
raise RuntimeError('teardown error')
def test_4(g):
assert True
呈现 test_3
案例摘要的虚拟插件示例。将片段放入 conftest.py
:
def pytest_unconfigure(config):
# example: get rendered output for test case `test_spam.py::test_3`
# get the reporter
reporter = config.pluginmanager.getplugin('terminalreporter')
# create a buffer to dump reporter output to
import io
buf = io.StringIO()
# fake tty or pytest will not colorize the output
buf.isatty = lambda: True
# replace writer in reporter to dump the output in buffer instead of stdout
from _pytest.config import create_terminal_writer
# I want to use the reporter again later to dump the rendered output,
# so I store the original writer here (you probably don't need it)
original_writer = reporter._tw
writer = create_terminal_writer(config, file=buf)
# replace the writer
reporter._tw = writer
# find the report for `test_spam.py::test_3` (we already know it will be an error report)
errors = reporter.stats['error']
test_3_report = next(
report for report in errors if report.nodeid == 'test_spam.py::test_3'
)
# dump the summary along with the stack trace for the report of `test_spam.py::test_3`
reporter._outrep_summary(test_3_report)
# print dumped contents
# you probably don't need this - this is just for demo purposes
# restore the original writer to write to stdout again
reporter._tw = original_writer
reporter.section('My own section', sep='>')
reporter.write(buf.getvalue())
reporter.write_sep('<')
A pytest
运行 现在产生一个额外的部分
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> My own section >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@pytest.fixture
def f():
> raise RuntimeError('setup error')
E RuntimeError: setup error
test_spam.py:14: RuntimeError
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
堆栈跟踪的渲染方式与 pytest
在 ERRORS
摘要部分中的渲染方式相同。如果需要,您可以使用不同测试用例的结果 - 如有必要,替换 reporter.stats
部分(errors
或 failed
,甚至 passed
- 尽管摘要应该是空的对于通过的测试)并修改测试用例 nodeid
.
我试图从 Pytest 会话的终端输出中逐行解析 FAILURES 部分,识别每个测试的测试名称和测试文件名,然后我想将它们附加在一起以形成“完全合格的测试”名称”(FQTN),例如tests/test_1.py::test_3_fails
。我还想获取并保存回溯信息(测试名称和测试文件名之间的信息)。
解析部分很简单,我已经有了匹配测试名称和测试文件名的工作正则表达式,我可以基于它提取回溯信息。我的 FQTN 问题是算法 - 我似乎无法弄清楚识别测试名称的整体逻辑,然后是测试的文件名,wihch 出现在后面的一行。我不仅需要适应 FAILURES 部分中间的测试,还需要适应 FAILURES 部分的第一个测试和最后一个测试。
举个例子。这是测试期间所有失败的输出部分 运行,以及一些出现在失败之前和之后的终端输出。
.
.
.
============================================== ERRORS ===============================================
__________________________________ ERROR at setup of test_c_error ___________________________________
@pytest.fixture
def error_fixture():
> assert 0
E assert 0
tests/test_2.py:19: AssertionError
============================================= FAILURES ==============================================
___________________________________________ test_3_fails ____________________________________________
log_testname = None
def test_3_fails(log_testname):
> assert 0
E assert 0
tests/test_1.py:98: AssertionError
--------------------------------------- Captured stdout setup ---------------------------------------
Running test tests.test_1...
Running test tests.test_1...
Setting test up...
Setting test up...
Executing test...
Executing test...
Tearing test down...
Tearing test down...
---------------------------------------- Captured log setup -----------------------------------------
INFO root:test_1.py:68 Running test tests.test_1...
INFO root:test_1.py:69 Setting test up...
INFO root:test_1.py:70 Executing test...
INFO root:test_1.py:72 Tearing test down...
______________________________________ test_8_causes_a_warning ______________________________________
log_testname = None
def test_8_causes_a_warning(log_testname):
> assert api_v1() == 1
E TypeError: api_v1() missing 1 required positional argument: 'log_testname'
tests/test_1.py:127: TypeError
--------------------------------------- Captured stdout setup ---------------------------------------
Running test tests.test_1...
Running test tests.test_1...
Setting test up...
Setting test up...
Executing test...
Executing test...
Tearing test down...
Tearing test down...
---------------------------------------- Captured log setup -----------------------------------------
INFO root:test_1.py:68 Running test tests.test_1...
INFO root:test_1.py:69 Setting test up...
INFO root:test_1.py:70 Executing test...
INFO root:test_1.py:72 Tearing test down...
___________________________ test_16_fail_compare_dicts_for_pytest_icdiff ____________________________
def test_16_fail_compare_dicts_for_pytest_icdiff():
listofStrings = ["Hello", "hi", "there", "at", "this"]
listofInts = [7, 10, 45, 23, 77]
assert len(listofStrings) == len(listofInts)
> assert listofStrings == listofInts
E AssertionError: assert ['Hello', 'hi... 'at', 'this'] == [7, 10, 45, 23, 77]
E At index 0 diff: 'Hello' != 7
E Full diff:
E - [7, 10, 45, 23, 77]
E + ['Hello', 'hi', 'there', 'at', 'this']
tests/test_1.py:210: AssertionError
____________________________________________ test_b_fail ____________________________________________
def test_b_fail():
> assert 0
E assert 0
tests/test_2.py:27: AssertionError
============================================== PASSES ===============================================
___________________________________________ test_4_passes ___________________________________________
--------------------------------------- Captured stdout setup ---------------------------------------
Running test tests.test_1...
Running test tests.test_1...
Setting test up...
Setting test up...
Executing test...
Executing test...
Tearing test down...
Tearing test down...
.
.
.
这里有人精通算法吗,也许是一些伪代码,显示了获取每个测试名称及其相关测试文件名的总体方法?
这是我为测试用例报告获取呈现摘要的建议。使用这个存根作为一个粗略的想法 - 您可能想要遍历报告并首先转储呈现的摘要,然后使用 curses 魔法来显示收集的数据。
一些测试:
import pytest
def test_1():
assert False
def test_2():
raise RuntimeError('call error')
@pytest.fixture
def f():
raise RuntimeError('setup error')
def test_3(f):
assert True
@pytest.fixture
def g():
yield
raise RuntimeError('teardown error')
def test_4(g):
assert True
呈现 test_3
案例摘要的虚拟插件示例。将片段放入 conftest.py
:
def pytest_unconfigure(config):
# example: get rendered output for test case `test_spam.py::test_3`
# get the reporter
reporter = config.pluginmanager.getplugin('terminalreporter')
# create a buffer to dump reporter output to
import io
buf = io.StringIO()
# fake tty or pytest will not colorize the output
buf.isatty = lambda: True
# replace writer in reporter to dump the output in buffer instead of stdout
from _pytest.config import create_terminal_writer
# I want to use the reporter again later to dump the rendered output,
# so I store the original writer here (you probably don't need it)
original_writer = reporter._tw
writer = create_terminal_writer(config, file=buf)
# replace the writer
reporter._tw = writer
# find the report for `test_spam.py::test_3` (we already know it will be an error report)
errors = reporter.stats['error']
test_3_report = next(
report for report in errors if report.nodeid == 'test_spam.py::test_3'
)
# dump the summary along with the stack trace for the report of `test_spam.py::test_3`
reporter._outrep_summary(test_3_report)
# print dumped contents
# you probably don't need this - this is just for demo purposes
# restore the original writer to write to stdout again
reporter._tw = original_writer
reporter.section('My own section', sep='>')
reporter.write(buf.getvalue())
reporter.write_sep('<')
A pytest
运行 现在产生一个额外的部分
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> My own section >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@pytest.fixture
def f():
> raise RuntimeError('setup error')
E RuntimeError: setup error
test_spam.py:14: RuntimeError
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
堆栈跟踪的渲染方式与 pytest
在 ERRORS
摘要部分中的渲染方式相同。如果需要,您可以使用不同测试用例的结果 - 如有必要,替换 reporter.stats
部分(errors
或 failed
,甚至 passed
- 尽管摘要应该是空的对于通过的测试)并修改测试用例 nodeid
.