test: Output results for cockpit's log.html

Cockpit's log.html has a peculiar format, but parsing logs in html is
much easier, because it shows expandable sections for each test.
This commit is contained in:
Lars Karlitski 2019-06-25 10:22:32 +02:00
parent e947e01331
commit f810924042

View File

@ -4,6 +4,7 @@ import argparse
import os import os
import subprocess import subprocess
import sys import sys
import traceback
import unittest import unittest
# import Cockpit's machinery for test VMs and its browser test API # import Cockpit's machinery for test VMs and its browser test API
@ -12,8 +13,6 @@ import testvm # pylint: disable=import-error
def print_exception(etype, value, tb): def print_exception(etype, value, tb):
import traceback
# only include relevant lines # only include relevant lines
limit = 0 limit = 0
while tb and '__unittest' in tb.tb_frame.f_globals: while tb and '__unittest' in tb.tb_frame.f_globals:
@ -31,7 +30,7 @@ class ComposerTestCase(unittest.TestCase):
self.network = testvm.VirtNetwork(0) self.network = testvm.VirtNetwork(0)
self.machine = testvm.VirtMachine(self.image, networking=self.network.host(), memory_mb=2048) self.machine = testvm.VirtMachine(self.image, networking=self.network.host(), memory_mb=2048)
print(f"Starting virtual machine '{self.image}'") print("Starting virtual machine '{}'".format(self.image))
self.machine.start() self.machine.start()
self.machine.wait_boot() self.machine.wait_boot()
@ -89,13 +88,78 @@ class ComposerTestCase(unittest.TestCase):
self.assertEqual(r.returncode, 0) self.assertEqual(r.returncode, 0)
class ComposerTestResult(unittest.TestResult):
def name(self, test):
name = test.id().replace("__main__.", "")
if test.shortDescription():
name += ": " + test.shortDescription()
return name
def startTest(self, test):
super().startTest(test)
print("# ----------------------------------------------------------------------")
print("# ", self.name(test))
print("", flush=True)
def stopTest(self, test):
print(flush=True)
def addSuccess(self, test):
super().addSuccess(test)
print("ok {} {}".format(self.testsRun, self.name(test)))
def addError(self, test, err):
super().addError(test, err)
traceback.print_exception(*err, file=sys.stdout)
print("not ok {} {}".format(self.testsRun, self.name(test)))
def addFailure(self, test, err):
super().addError(test, err)
traceback.print_exception(*err, file=sys.stdout)
print("not ok {} {}".format(self.testsRun, self.name(test)))
def addSkip(self, test, reason):
super().addSkip(test, reason)
print("ok {} {} # SKIP {}".format(self.testsRun, self.name(test), reason))
def addExpectedFailure(self, test, err):
super().addExpectedFailure(test, err)
print("ok {} {}".format(self.testsRun, self.name(test)))
def addUnexpectedSuccess(self, test):
super().addUnexpectedSuccess(test)
print("not ok {} {}".format(self.testsRun, self.name(test)))
class ComposerTestRunner(object):
"""A test runner that (in combination with ComposerTestResult) outputs
results in a way that cockpit's log.html can read and format them.
"""
def __init__(self, failfast=False):
self.failfast = failfast
def run(self, testable):
result = ComposerTestResult()
result.failfast = self.failfast
result.startTestRun()
count = testable.countTestCases()
print("1.." + str(count))
try:
testable(result)
finally:
result.stopTestRun()
return result
def print_tests(tests): def print_tests(tests):
for test in tests: for test in tests:
if isinstance(test, unittest.TestSuite): if isinstance(test, unittest.TestSuite):
print_tests(test) print_tests(test)
elif isinstance(test, unittest.loader._FailedTest): elif isinstance(test, unittest.loader._FailedTest):
name = test.id().replace("unittest.loader._FailedTest.", "") name = test.id().replace("unittest.loader._FailedTest.", "")
print(f"Error: '{name}' does not match a test", file=sys.stderr) print("Error: '{}' does not match a test".format(name), file=sys.stderr)
else: else:
print(test.id().replace("__main__.", "")) print(test.id().replace("__main__.", ""))
@ -119,7 +183,7 @@ def main():
print_tests(tests) print_tests(tests)
return 0 return 0
runner = unittest.TextTestRunner(verbosity=2, failfast=args.sit) runner = ComposerTestRunner(failfast=args.sit)
result = runner.run(tests) result = runner.run(tests)
sys.exit(not result.wasSuccessful()) sys.exit(not result.wasSuccessful())