And into a generic sub element in the output. This makes information like glxinfo and clinfo not a hard requirement and makes the format easier to generate from swineherd. --- framework/backends/json.py | 16 ++- framework/programs/run.py | 3 +- framework/results.py | 10 +- framework/summary/html_.py | 8 +- .../framework/backends/schema/piglit-10.json | 125 ++++++++++++++++++ unittests/framework/backends/shared.py | 22 ++- .../framework/backends/test_json_update.py | 80 +++++++++++ unittests/framework/test_results.py | 46 +++---- 8 files changed, 257 insertions(+), 53 deletions(-) create mode 100644 unittests/framework/backends/schema/piglit-10.json
diff --git a/framework/backends/json.py b/framework/backends/json.py index 882169e09..46ea6f7bc 100644 --- a/framework/backends/json.py +++ b/framework/backends/json.py @@ -53,7 +53,7 @@ __all__ = [ ] # The current version of the JSON results -CURRENT_JSON_VERSION = 9 +CURRENT_JSON_VERSION = 10 # The minimum JSON format supported MINIMUM_SUPPORTED_VERSION = 7 @@ -322,6 +322,7 @@ def _update_results(results, filepath): updates = { 7: _update_seven_to_eight, 8: _update_eight_to_nine, + 9: _update_nine_to_ten, } while results['results_version'] < CURRENT_JSON_VERSION: @@ -400,6 +401,19 @@ def _update_eight_to_nine(result): return result +def _update_nine_to_ten(result): + result['info'] = {} + result['info']['system'] = {} + for e in ['glxinfo', 'wglinfo', 'clinfo', 'lspci', 'uname']: + r = result.pop(e) + if r: + result['info']['system'][e] = r + + result['results_version'] = 10 + + return result + + REGISTRY = Registry( extensions=['.json'], backend=JSONBackend, diff --git a/framework/programs/run.py b/framework/programs/run.py index afb7eb78d..da978fdaf 100644 --- a/framework/programs/run.py +++ b/framework/programs/run.py @@ -250,7 +250,8 @@ def _create_metadata(args, name, forced_test_list): metadata = {'options': opts} metadata['name'] = name - metadata.update(core.collect_system_info()) + metadata['info'] = {} + metadata['info']['system'] = core.collect_system_info() return metadata diff --git a/framework/results.py b/framework/results.py index b936298c3..31cb8dff3 100644 --- a/framework/results.py +++ b/framework/results.py @@ -298,13 +298,8 @@ class Totals(dict): class TestrunResult(object): """The result of a single piglit run.""" def __init__(self): - self.name = None - self.uname = None + self.info = {} self.options = {} - self.glxinfo = None - self.wglinfo = None - self.clinfo = None - self.lspci = None self.time_elapsed = TimeAttribute() self.tests = collections.OrderedDict() self.totals = collections.defaultdict(Totals) @@ -372,8 +367,7 @@ class TestrunResult(object): """ res = cls() - for name in ['name', 'uname', 'options', 'glxinfo', 'wglinfo', 'lspci', - 'results_version', 'clinfo']: + for name in ['name', 'info', 'options', 'results_version']: value = dict_.get(name) if value: setattr(res, name, value) diff --git a/framework/summary/html_.py b/framework/summary/html_.py index 14dd76fa8..945230bc6 100644 --- a/framework/summary/html_.py +++ b/framework/summary/html_.py @@ -100,10 +100,10 @@ def _make_testrun_info(results, destination, exclude=None): totals=each.totals['root'], time=each.time_elapsed.delta, options=each.options, - uname=each.uname, - glxinfo=each.glxinfo, - clinfo=each.clinfo, - lspci=each.lspci)) + uname=each.info['system'].get('uname'), + glxinfo=each.info['system'].get('glxinfo'), + clinfo=each.info['system'].get('clinfo'), + lspci=each.info['system'].get('lspci'))) # Then build the individual test results for key, value in six.iteritems(each.tests): diff --git a/unittests/framework/backends/schema/piglit-10.json b/unittests/framework/backends/schema/piglit-10.json new file mode 100644 index 000000000..e400b9733 --- /dev/null +++ b/unittests/framework/backends/schema/piglit-10.json @@ -0,0 +1,125 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "TestrunResult", + "description": "The collection of all results", + "type": "object", + "properties": { + "__type__": { "type": "string" }, + "name": { "type": "string" }, + "results_version": { "type": "number" }, + "time_elapsed": { "$ref": "#/definitions/timeAttribute" }, + "info": { + "type": "object", + "description": "Relavent information about the system running tests.", + "additionalProperties": { + "type": "object", + "additionalProperties": { "type": "string" } + } + }, + "options": { + "descrption": "The options that were invoked with this run. These are implementation specific and not required.", + "type": "object", + "properties": { + "exclude_tests": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + }, + "include_filter": { + "type": "array", + "items": { "type": "string" } + }, + "exclude_filter": { + "type": "array", + "items": { "type": "string" } + }, + "sync": { "type": "boolean" }, + "valgrind": { "type": "boolean" }, + "monitored": { "type": "boolean" }, + "dmesg": { "type": "boolean" }, + "execute": { "type": "boolean" }, + "concurrent": { "enum": ["none", "all", "some"] }, + "platform": { "type": "string" }, + "log_level": { "type": "string" }, + "env": { + "description": "Environment variables that must be specified", + "type": "object", + "additionalProperties": { "type": "string" } + }, + "profile": { + "type": "array", + "items": { "type": "string" } + } + } + }, + "totals": { + "type": "object", + "description": "A calculation of the group totals.", + "additionalProperties": { + "type": "object", + "properties": { + "crash": { "type": "number" }, + "dmesg-fail": { "type": "number" }, + "dmesg-warn": { "type": "number" }, + "fail": { "type": "number" }, + "incomplete": { "type": "number" }, + "notrun": { "type": "number" }, + "pass": { "type": "number" }, + "skip": { "type": "number" }, + "timeout": { "type": "number" }, + "warn": { "type": "number" } + }, + "additionalProperties": false, + "required": [ "crash", "dmesg-fail", "dmesg-warn", "fail", "incomplete", "notrun", "pass", "skip", "timeout", "warn" ] + } + }, + "tests": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "__type__": { "type": "string" }, + "err": { "type": "string" }, + "exception": { "type": ["string", "null"] }, + "result": { + "type": "string", + "enum": [ "pass", "fail", "crash", "warn", "incomplete", "notrun", "skip", "dmesg-warn", "dmesg-fail" ] + }, + "environment": { "type": "string" }, + "command": { "type": "string" }, + "traceback": { "type": ["string", "null"] }, + "out": { "type": "string" }, + "dmesg": { "type": "string" }, + "pid": { + "type": "array", + "items": { "type": "number" } + }, + "returncode": { "type": [ "number", "null" ] }, + "time": { "$ref": "#/definitions/timeAttribute" }, + "subtests": { + "type": "object", + "properties": { "__type__": { "type": "string" } }, + "additionalProperties": { "type": "string" }, + "required": [ "__type__" ] + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false, + "required": [ "__type__", "name", "results_version", "time_elapsed", "tests", "info" ], + "definitions": { + "timeAttribute": { + "type": "object", + "description": "An element containing a start and end time", + "properties": { + "__type__": { "type": "string" }, + "start": { "type": "number" }, + "end": { "type": "number" } + }, + "additionalProperties": false, + "required": [ "__type__", "start", "end" ] + } + } +} diff --git a/unittests/framework/backends/shared.py b/unittests/framework/backends/shared.py index d9f5790af..59b30c9aa 100644 --- a/unittests/framework/backends/shared.py +++ b/unittests/framework/backends/shared.py @@ -31,24 +31,21 @@ from framework.options import OPTIONS INITIAL_METADATA = { 'name': 'name', 'options': dict(OPTIONS), - 'clinfo': None, - 'glxinfo': None, - 'wglinfo': None, - 'lspci': None, - 'uname': None, + 'info': { + 'system': {} + } } # This is current JSON data, in raw form with only the minimum required # changes. This does not contain piglit specifc objects, only strings, floats, # ints, and Nones (instead of JSON's null) JSON = { - "results_version": 9, + "results_version": 10, "time_elapsed": { "start": 1469638791.2351687, "__type__": "TimeAttribute", "end": 1469638791.4387212 }, - "wglinfo": None, "tests": { "spec@!opengl 1.0@gl-1.0-readpixsanity": { "dmesg": "", @@ -75,11 +72,6 @@ JSON = { " PIGLIT_PLATFORM=\"gbm\"") } }, - # pylint: disable=line-too-long - "lspci": "00:00.0 Host bridge...", - "clinfo": None, - "uname": "uname", - # pylint: enable=line-too-long "options": { "dmesg": False, "concurrent": "some", @@ -102,7 +94,11 @@ JSON = { }, "name": "foo", "__type__": "TestrunResult", - "glxinfo": None, + "info": { + "system": { + "lspci": "00:00.0 Host bridge...", + }, + }, "totals": { "spec": { '__type__': 'Totals', diff --git a/unittests/framework/backends/test_json_update.py b/unittests/framework/backends/test_json_update.py index 29ca19a50..dca6f9d77 100644 --- a/unittests/framework/backends/test_json_update.py +++ b/unittests/framework/backends/test_json_update.py @@ -224,3 +224,83 @@ class TestV8toV9(object): jsonschema.validate( json.loads(json.dumps(result, default=backends.json.piglit_encoder)), schema) + + +class TestV9toV10(object): + """Tests for Version 8 to version 9.""" + + data = { + "results_version": 9, + "name": "test", + "options": { + "profile": ['quick'], + "dmesg": False, + "verbose": False, + "platform": "gbm", + "sync": False, + "valgrind": False, + "filter": [], + "concurrent": "all", + "test_count": 0, + "exclude_tests": [], + "exclude_filter": [], + "env": {}, + }, + "lspci": "stuff", + "uname": "more stuff", + "glxinfo": "and stuff", + "wglinfo": "stuff", + "clinfo": "stuff", + "tests": { + 'a@test': { + "time": { + 'start': 1.2, + 'end': 1.8, + '__type__': 'TimeAttribute' + }, + 'dmesg': '', + 'result': 'fail', + '__type__': 'TestResult', + 'command': '/a/command', + 'traceback': None, + 'out': '', + 'environment': 'A=variable', + 'returncode': 0, + 'err': '', + 'pid': [5], + 'subtests': { + '__type__': 'Subtests', + }, + 'exception': None, + }, + }, + "time_elapsed": { + 'start': 1.2, + 'end': 1.8, + '__type__': 'TimeAttribute' + }, + '__type__': 'TestrunResult', + } + + @pytest.fixture + def result(self, tmpdir): + p = tmpdir.join('result.json') + p.write(json.dumps(self.data, default=backends.json.piglit_encoder)) + with p.open('r') as f: + return backends.json._update_nine_to_ten(backends.json._load(f)) + + @pytest.mark.parametrize("key", ['glxinfo', 'wglinfo', 'clinfo', 'uname', 'lspci']) + def test(self, key, result): + assert key not in result, 'Root key/value not removed' + assert key in result['info']['system'], 'Key not added to info/system' + assert result['info']['system'][key] == self.data[key], \ + 'Value not set properly.' + + def test_valid(self, result): + with open(os.path.join(os.path.dirname(__file__), 'schema', + 'piglit-10.json'), + 'r') as f: + schema = json.load(f) + jsonschema.validate( + json.loads(json.dumps(result, default=backends.json.piglit_encoder)), + schema) diff --git a/unittests/framework/test_results.py b/unittests/framework/test_results.py index 52257e03c..cd7c2423d 100644 --- a/unittests/framework/test_results.py +++ b/unittests/framework/test_results.py @@ -467,13 +467,17 @@ class TestTestrunResult(object): def setup_class(cls): """Setup values used by all tests.""" test = results.TestrunResult() + test.info = { + 'system': { + 'uname': 'this is uname', + 'glxinfo': 'glxinfo', + 'clinfo': 'clinfo', + 'wglinfo': 'wglinfo', + 'lspci': 'this is lspci', + } + } test.name = 'name' - test.uname = 'this is uname' test.options = {'some': 'option'} - test.glxinfo = 'glxinfo' - test.clinfo = 'clinfo' - test.wglinfo = 'wglinfo' - test.lspci = 'this is lspci' test.time_elapsed.end = 1.23 test.tests = {'a test': results.TestResult('pass')} @@ -483,30 +487,21 @@ class TestTestrunResult(object): """name is properly encoded.""" assert self.test['name'] == 'name' - def test_uname(self): - """uname is properly encoded.""" - assert self.test['uname'] == 'this is uname' + def test_info(self): + assert self.test['info'] == { + 'system': { + 'uname': 'this is uname', + 'glxinfo': 'glxinfo', + 'clinfo': 'clinfo', + 'wglinfo': 'wglinfo', + 'lspci': 'this is lspci', + } + } def test_options(self): """options is properly encoded.""" assert dict(self.test['options']) == {'some': 'option'} - def test_glxinfo(self): - """glxinfo is properly encoded.""" - assert self.test['glxinfo'] == 'glxinfo' - - def test_wglinfo(self): - """wglinfo is properly encoded.""" - assert self.test['wglinfo'] == 'wglinfo' - - def test_clinfo(self): - """clinfo is properly encoded.""" - assert self.test['clinfo'] == 'clinfo' - - def test_lspci(self): - """lspci is properly encoded.""" - assert self.test['lspci'] == 'this is lspci' - def test_time(self): """time_elapsed is properly encoded.""" assert self.test['time_elapsed'].end == 1.23 @@ -527,8 +522,7 @@ class TestTestrunResult(object): return results.TestrunResult.from_dict(shared.JSON) @pytest.mark.parametrize("attrib", [ - 'name', 'uname', 'glxinfo', 'wglinfo', 'lspci', 'results_version', - 'clinfo', 'options', + 'name', 'results_version', 'info', 'options', ]) def test_attribs_restored(self, attrib, inst): """tests for basic attributes.""" -- 2.17.1 _______________________________________________ Piglit mailing list Piglit@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/piglit