提交 ee2e6aee 编写于 作者: L Lukáš Doktor

plugins.xunit: Add support to limit test log size

Some tests might produce too much logs which might result in too big
xunit results, that might be hard/impossible to publish. Let's allow
setting a limit to number of characters to those files and embed only
1/2 of the limit from the beginning and 1/2 of the limit from the end of
the log.
Signed-off-by: NLukáš Doktor <ldoktor@redhat.com>
上级 380d99bc
...@@ -23,6 +23,7 @@ from xml.dom.minidom import Document ...@@ -23,6 +23,7 @@ from xml.dom.minidom import Document
from avocado.core.parser import FileOrStdoutAction from avocado.core.parser import FileOrStdoutAction
from avocado.core.output import LOG_UI from avocado.core.output import LOG_UI
from avocado.core.plugin_interfaces import CLI, Result from avocado.core.plugin_interfaces import CLI, Result
from avocado.utils import data_structures
class XUnitResult(Result): class XUnitResult(Result):
...@@ -53,7 +54,8 @@ class XUnitResult(Result): ...@@ -53,7 +54,8 @@ class XUnitResult(Result):
testcase.setAttribute('time', self._get_attr(state, 'time_elapsed')) testcase.setAttribute('time', self._get_attr(state, 'time_elapsed'))
return testcase return testcase
def _create_failure_or_error(self, document, test, element_type): def _create_failure_or_error(self, document, test, element_type,
max_log_size=None):
element = document.createElement(element_type) element = document.createElement(element_type)
element.setAttribute('type', self._get_attr(test, 'fail_class')) element.setAttribute('type', self._get_attr(test, 'fail_class'))
element.setAttribute('message', self._get_attr(test, 'fail_reason')) element.setAttribute('message', self._get_attr(test, 'fail_reason'))
...@@ -63,6 +65,20 @@ class XUnitResult(Result): ...@@ -63,6 +65,20 @@ class XUnitResult(Result):
system_out = document.createElement('system-out') system_out = document.createElement('system-out')
try: try:
with open(test.get("logfile"), "r") as logfile_obj: with open(test.get("logfile"), "r") as logfile_obj:
if max_log_size is not None:
logfile_obj.seek(0, 2)
log_size = logfile_obj.tell()
if log_size < max_log_size:
text_output = logfile_obj.read()
else:
size = max_log_size / 2
logfile_obj.seek(0, 0)
text_output = logfile_obj.read(size)
text_output += ("\n\n--[ CUT DUE TO XML PER TEST "
"LIMIT ]--\n\n")
logfile_obj.seek(-size / 2, 2)
text_output += logfile_obj.read()
else:
text_output = logfile_obj.read() text_output = logfile_obj.read()
except (TypeError, IOError): except (TypeError, IOError):
text_output = self.UNKNOWN text_output = self.UNKNOWN
...@@ -71,7 +87,7 @@ class XUnitResult(Result): ...@@ -71,7 +87,7 @@ class XUnitResult(Result):
system_out.appendChild(system_out_cdata) system_out.appendChild(system_out_cdata)
return element, system_out return element, system_out
def _render(self, result): def _render(self, result, max_test_log_size):
document = Document() document = Document()
testsuite = document.createElement('testsuite') testsuite = document.createElement('testsuite')
testsuite.setAttribute('name', 'avocado') testsuite.setAttribute('name', 'avocado')
...@@ -92,7 +108,8 @@ class XUnitResult(Result): ...@@ -92,7 +108,8 @@ class XUnitResult(Result):
elif status == 'FAIL': elif status == 'FAIL':
element, system_out = self._create_failure_or_error(document, element, system_out = self._create_failure_or_error(document,
test, test,
'failure') 'failure',
max_test_log_size)
testcase.appendChild(element) testcase.appendChild(element)
testcase.appendChild(system_out) testcase.appendChild(system_out)
elif status == 'CANCEL': elif status == 'CANCEL':
...@@ -100,7 +117,8 @@ class XUnitResult(Result): ...@@ -100,7 +117,8 @@ class XUnitResult(Result):
else: else:
element, system_out = self._create_failure_or_error(document, element, system_out = self._create_failure_or_error(document,
test, test,
'error') 'error',
max_test_log_size)
testcase.appendChild(element) testcase.appendChild(element)
testcase.appendChild(system_out) testcase.appendChild(system_out)
testsuite.appendChild(testcase) testsuite.appendChild(testcase)
...@@ -114,7 +132,8 @@ class XUnitResult(Result): ...@@ -114,7 +132,8 @@ class XUnitResult(Result):
if not result.tests_total: if not result.tests_total:
return return
content = self._render(result) max_test_log_size = getattr(job.args, 'xunit_max_test_log_chars', None)
content = self._render(result, max_test_log_size)
if getattr(job.args, 'xunit_job_result', 'off') == 'on': if getattr(job.args, 'xunit_job_result', 'off') == 'on':
xunit_path = os.path.join(job.logdir, 'results.xml') xunit_path = os.path.join(job.logdir, 'results.xml')
with open(xunit_path, 'wb') as xunit_file: with open(xunit_path, 'wb') as xunit_file:
...@@ -156,5 +175,11 @@ class XUnitCLI(CLI): ...@@ -156,5 +175,11 @@ class XUnitCLI(CLI):
help=('Enables default xUnit result in the job results directory. ' help=('Enables default xUnit result in the job results directory. '
'File will be named "results.xml".')) 'File will be named "results.xml".'))
run_subcommand_parser.output.add_argument(
'--xunit-max-test-log-chars', metavar='SIZE',
type=lambda x: data_structures.DataSize(x).b, help="Limit the "
"attached job log to given number of characters (k/m/g suffix "
"allowed)")
def run(self, args): def run(self, args):
pass pass
...@@ -88,6 +88,12 @@ output in the standard output of the runner, simply use:: ...@@ -88,6 +88,12 @@ output in the standard output of the runner, simply use::
.. note:: The dash `-` in the option `--xunit`, it means that the xunit result .. note:: The dash `-` in the option `--xunit`, it means that the xunit result
should go to the standard output. should go to the standard output.
.. note:: In case your tests produce very long outputs, you can limit the
number of embedded characters by
`--xunit-max-test-log-chars`. If the output in the log file is
longer it only attaches up-to max-test-log-chars characters
one half starting from the beginning of the content, the other
half from the end of the content.
JSON JSON
~~~~ ~~~~
......
...@@ -94,6 +94,10 @@ introduced by the next LTS version are: ...@@ -94,6 +94,10 @@ introduced by the next LTS version are:
that ``MemoryError`` is a Python standard exception, that is that ``MemoryError`` is a Python standard exception, that is
intended to be used on different situations. intended to be used on different situations.
* Added possibility to limit the amount of characters embedded as
"system-out" in the xunit output plugin (``--xunit-max-test-log-chars
XX``).
Complete list of changes Complete list of changes
======================== ========================
......
...@@ -131,6 +131,9 @@ Options for subcommand `run` (`avocado run --help`):: ...@@ -131,6 +131,9 @@ Options for subcommand `run` (`avocado run --help`)::
--xunit-job-result {on,off} --xunit-job-result {on,off}
Enables default xUnit result in the job results Enables default xUnit result in the job results
directory. File will be named "results.xml". directory. File will be named "results.xml".
--xunit-max-test-log-chars SIZE
Limit the attached job log to given number of
characters (k/m/g suffix allowed)
-z, --archive Archive (ZIP) files generated by tests -z, --archive Archive (ZIP) files generated by tests
output check arguments: output check arguments:
......
...@@ -94,6 +94,30 @@ class xUnitSucceedTest(unittest.TestCase): ...@@ -94,6 +94,30 @@ class xUnitSucceedTest(unittest.TestCase):
"Failed to validate against %s, content:\n%s" % "Failed to validate against %s, content:\n%s" %
(self.junit, xml)) (self.junit, xml))
def test_max_test_log_size(self):
log = tempfile.NamedTemporaryFile(dir=self.tmpdir, delete=False)
log_content = b"1234567890" * 100
log.write(log_content)
log_path = log.name
log.close()
self.test1._Test__status = "ERROR"
self.test1._Test__logfile = log_path
self.test_result.start_test(self.test1)
self.test_result.end_test(self.test1.get_state())
self.test_result.end_tests()
xunit_result = xunit.XUnitResult()
xunit_result.render(self.test_result, self.job)
with open(self.job.args.xunit_output, 'rb') as fp:
unlimited = fp.read()
self.job.args.xunit_max_test_log_chars = 10
xunit_result.render(self.test_result, self.job)
with open(self.job.args.xunit_output, 'rb') as fp:
limited = fp.read()
self.assertLess(len(limited), len(unlimited) - 500,
"Length of xunit limitted to 10 chars was greater "
"than (unlimited - 500). Unlimited output:\n%s\n\n"
"Limited output:\n%s" % (unlimited, limited))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册