1
0
mirror of https://git.yoctoproject.org/poky synced 2026-06-05 02:20:12 +00:00

oeqa/core: Implement proper extra result collection and serialization

Implement handling of extra result (e.g. ptestresult) collection with
the addition of a "extraresults" extraction function in OETestResult. In
order to be able to serialize and deserialize the extraresults data,
allow OETestResult add* calls to take a details kwarg. The subunit
module can handle cross-process transfer of binary data for the details
kwarg. With a TestResult proxy class to sit inbetween to encode and
decode to and from json.

(From OE-Core rev: b0831d43606415807af80e2aa1d0566d0b8c209c)

Signed-off-by: Nathan Rossi <nathan@nathanrossi.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Nathan Rossi
2019-09-07 12:55:06 +00:00
committed by Richard Purdie
parent 52ba1a3d44
commit 1220faf665
2 changed files with 96 additions and 6 deletions
+37 -4
View File
@@ -43,6 +43,7 @@ class OETestResult(_TestResult):
self.starttime = {}
self.endtime = {}
self.progressinfo = {}
self.extraresults = {}
# Inject into tc so that TestDepends decorator can see results
tc.results = self
@@ -129,19 +130,51 @@ class OETestResult(_TestResult):
return 'UNKNOWN', None
def addSuccess(self, test):
def extractExtraResults(self, test, details = None):
extraresults = None
if details is not None and "extraresults" in details:
extraresults = details.get("extraresults", {})
elif hasattr(test, "extraresults"):
extraresults = test.extraresults
if extraresults is not None:
for k, v in extraresults.items():
# handle updating already existing entries (e.g. ptestresults.sections)
if k in self.extraresults:
self.extraresults[k].update(v)
else:
self.extraresults[k] = v
def addError(self, test, *args, details = None):
self.extractExtraResults(test, details = details)
return super(OETestResult, self).addError(test, *args)
def addFailure(self, test, *args, details = None):
self.extractExtraResults(test, details = details)
return super(OETestResult, self).addFailure(test, *args)
def addSuccess(self, test, details = None):
#Added so we can keep track of successes too
self.successes.append((test, None))
super(OETestResult, self).addSuccess(test)
self.extractExtraResults(test, details = details)
return super(OETestResult, self).addSuccess(test)
def addExpectedFailure(self, test, *args, details = None):
self.extractExtraResults(test, details = details)
return super(OETestResult, self).addExpectedFailure(test, *args)
def addUnexpectedSuccess(self, test, details = None):
self.extractExtraResults(test, details = details)
return super(OETestResult, self).addUnexpectedSuccess(test)
def logDetails(self, json_file_dir=None, configuration=None, result_id=None,
dump_streams=False):
self.tc.logger.info("RESULTS:")
result = {}
result = self.extraresults
logs = {}
if hasattr(self.tc, "extraresults"):
result = self.tc.extraresults
result.update(self.tc.extraresults)
for case_name in self.tc._registry['cases']:
case = self.tc._registry['cases'][case_name]
+59 -2
View File
@@ -21,6 +21,7 @@ import testtools
import threading
import time
import io
import json
import subunit
from queue import Queue
@@ -28,6 +29,8 @@ from itertools import cycle
from subunit import ProtocolTestCase, TestProtocolClient
from subunit.test_results import AutoTimingTestResultDecorator
from testtools import ThreadsafeForwardingResult, iterate_tests
from testtools.content import Content
from testtools.content_type import ContentType
from oeqa.utils.commands import get_test_layer
import bb.utils
@@ -70,6 +73,58 @@ class BBThreadsafeForwardingResult(ThreadsafeForwardingResult):
self.semaphore.release()
super(BBThreadsafeForwardingResult, self)._add_result_with_semaphore(method, test, *args, **kwargs)
class ProxyTestResult:
# a very basic TestResult proxy, in order to modify add* calls
def __init__(self, target):
self.result = target
def _addResult(self, method, test, *args, **kwargs):
return method(test, *args, **kwargs)
def addError(self, test, *args, **kwargs):
self._addResult(self.result.addError, test, *args, **kwargs)
def addFailure(self, test, *args, **kwargs):
self._addResult(self.result.addFailure, test, *args, **kwargs)
def addSuccess(self, test, *args, **kwargs):
self._addResult(self.result.addSuccess, test, *args, **kwargs)
def addExpectedFailure(self, test, *args, **kwargs):
self._addResult(self.result.addExpectedFailure, test, *args, **kwargs)
def addUnexpectedSuccess(self, test, *args, **kwargs):
self._addResult(self.result.addUnexpectedSuccess, test, *args, **kwargs)
def __getattr__(self, attr):
return getattr(self.result, attr)
class ExtraResultsDecoderTestResult(ProxyTestResult):
def _addResult(self, method, test, *args, **kwargs):
if "details" in kwargs and "extraresults" in kwargs["details"]:
if isinstance(kwargs["details"]["extraresults"], Content):
kwargs = kwargs.copy()
kwargs["details"] = kwargs["details"].copy()
extraresults = kwargs["details"]["extraresults"]
data = bytearray()
for b in extraresults.iter_bytes():
data += b
extraresults = json.loads(data.decode())
kwargs["details"]["extraresults"] = extraresults
return method(test, *args, **kwargs)
class ExtraResultsEncoderTestResult(ProxyTestResult):
def _addResult(self, method, test, *args, **kwargs):
if hasattr(test, "extraresults"):
extras = lambda : [json.dumps(test.extraresults).encode()]
kwargs = kwargs.copy()
if "details" not in kwargs:
kwargs["details"] = {}
else:
kwargs["details"] = kwargs["details"].copy()
kwargs["details"]["extraresults"] = Content(ContentType("application", "json", {'charset': 'utf8'}), extras)
return method(test, *args, **kwargs)
#
# We have to patch subunit since it doesn't understand how to handle addError
# outside of a running test case. This can happen if classSetUp() fails
@@ -116,7 +171,9 @@ class ConcurrentTestSuite(unittest.TestSuite):
result.threadprogress = {}
for i, (test, testnum) in enumerate(tests):
result.threadprogress[i] = []
process_result = BBThreadsafeForwardingResult(result, semaphore, i, testnum, totaltests)
process_result = BBThreadsafeForwardingResult(
ExtraResultsDecoderTestResult(result),
semaphore, i, testnum, totaltests)
# Force buffering of stdout/stderr so the console doesn't get corrupted by test output
# as per default in parent code
process_result.buffer = True
@@ -231,7 +288,7 @@ def fork_for_tests(concurrency_num, suite):
# as per default in parent code
subunit_client.buffer = True
subunit_result = AutoTimingTestResultDecorator(subunit_client)
process_suite.run(subunit_result)
process_suite.run(ExtraResultsEncoderTestResult(subunit_result))
if ourpid != os.getpid():
os._exit(0)
if newbuilddir: