summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--framework/programs/summary.py37
-rw-r--r--framework/summary/__init__.py2
-rw-r--r--framework/summary/feature.py99
-rw-r--r--framework/summary/html_.py23
-rwxr-xr-xpiglit4
-rw-r--r--templates/feature.mako73
6 files changed, 236 insertions, 2 deletions
diff --git a/framework/programs/summary.py b/framework/programs/summary.py
index 8711ee54c..b23f1ef31 100644
--- a/framework/programs/summary.py
+++ b/framework/programs/summary.py
@@ -35,6 +35,7 @@ __all__ = [
'console',
'csv',
'html',
+ 'feature'
]
@@ -224,3 +225,39 @@ def aggregate(input_):
print("Aggregated file written to: {}.{}".format(
outfile, backends.compression.get_mode()))
+
+
+@exceptions.handler
+def feature(input_):
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-o", "--overwrite",
+ action="store_true",
+ help="Overwrite existing directories")
+ parser.add_argument("featureFile",
+ metavar="<Feature json file>",
+ help="Json file containing the features description")
+ parser.add_argument("summaryDir",
+ metavar="<Summary Directory>",
+ help="Directory to put HTML files in")
+ parser.add_argument("resultsFiles",
+ metavar="<Results Files>",
+ nargs="*",
+ help="Results files to include in HTML")
+ args = parser.parse_args(input_)
+
+ # If args.list and args.resultsFiles are empty, then raise an error
+ if not args.featureFile and not args.resultsFiles:
+ raise parser.error("Missing required option -l or <resultsFiles>")
+
+ # If args.list and args.resultsFiles are empty, then raise an error
+ if not args.resultsFiles or not path.exists(args.featureFile):
+ raise parser.error("Missing json file")
+
+ # if overwrite is requested delete the output directory
+ if path.exists(args.summaryDir) and args.overwrite:
+ shutil.rmtree(args.summaryDir)
+
+ # If the requested directory doesn't exist, create it or throw an error
+ core.checkDir(args.summaryDir, not args.overwrite)
+
+ summary.feat(args.resultsFiles, args.summaryDir, args.featureFile)
diff --git a/framework/summary/__init__.py b/framework/summary/__init__.py
index 8a1ff8a79..0f0f144f0 100644
--- a/framework/summary/__init__.py
+++ b/framework/summary/__init__.py
@@ -25,5 +25,5 @@
# public parts here, so that we have a nice interface to work with.
from __future__ import absolute_import, division, print_function
-from .html_ import html
+from .html_ import html, feat
from .console_ import console
diff --git a/framework/summary/feature.py b/framework/summary/feature.py
new file mode 100644
index 000000000..2fb8f50e3
--- /dev/null
+++ b/framework/summary/feature.py
@@ -0,0 +1,99 @@
+# Copyright (c) 2015 Intel Corporation
+
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# This permission notice shall be included in all copies or
+# substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR(S) BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from __future__ import print_function, division, absolute_import
+
+import copy
+
+try:
+ import simplejson as json
+except ImportError:
+ import json
+
+from framework import core, exceptions, profile, status
+
+
+class FeatResults(object): # pylint: disable=too-few-public-methods
+ """Container object for results.
+
+ Has the results, feature profiles and feature computed results.
+
+ """
+ def __init__(self, results, json_file):
+
+ with open(json_file) as data:
+ feature_data = json.load(data)
+
+ self.feat_fractions = {}
+ self.feat_status = {}
+ self.features = set()
+ self.results = results
+
+ profiles = {}
+
+ # we expect all the result sets to be for the same profile
+ profile_orig = profile.load_test_profile(results[0].options['profile'][0])
+
+ for feature in feature_data:
+ self.features.add(feature)
+
+ incl_str = feature_data[feature]["include_tests"]
+ excl_str = feature_data[feature]["exclude_tests"]
+
+ include_filter = [incl_str] if incl_str and not incl_str.isspace() else []
+ exclude_filter = [excl_str] if excl_str and not excl_str.isspace() else []
+
+ opts = core.Options(include_filter=include_filter,
+ exclude_filter=exclude_filter)
+
+ profiles[feature] = copy.deepcopy(profile_orig)
+
+ # An empty list will raise PiglitFatalError exception
+ # But for reporting we need to handle this situation
+ try:
+ profiles[feature]._prepare_test_list(opts)
+ except exceptions.PiglitFatalError:
+ pass
+
+ for results in self.results:
+ self.feat_fractions[results.name] = {}
+ self.feat_status[results.name] = {}
+
+ for feature in feature_data:
+ result_set = set(results.tests)
+ profile_set = set(profiles[feature].test_list)
+
+ common_set = profile_set & result_set
+ passed_list = [x for x in common_set if results.tests[x].result == status.PASS]
+
+ total = len(common_set)
+ passed = len(passed_list)
+
+ self.feat_fractions[results.name][feature] = (passed, total)
+ if total == 0:
+ self.feat_status[results.name][feature] = status.NOTRUN
+ else:
+ if 100 * passed // total >= feature_data[feature]["target_rate"]:
+ self.feat_status[results.name][feature] = status.PASS
+ else:
+ self.feat_status[results.name][feature] = status.FAIL
diff --git a/framework/summary/html_.py b/framework/summary/html_.py
index 12172b75c..60d7f5e60 100644
--- a/framework/summary/html_.py
+++ b/framework/summary/html_.py
@@ -37,9 +37,11 @@ from mako.lookup import TemplateLookup
from framework import backends, exceptions
from .common import Results, escape_filename, escape_pathname
+from .feature import FeatResults
__all__ = [
'html',
+ 'feat'
]
_TEMP_DIR = os.path.join(
@@ -62,8 +64,9 @@ def _copy_static_files(destination):
os.path.join(destination, "result.css"))
-def _make_testrun_info(results, destination, exclude):
+def _make_testrun_info(results, destination, exclude=None):
"""Create the pages for each results file."""
+ exclude = exclude or {}
result_css = os.path.join(destination, "result.css")
index = os.path.join(destination, "index.html")
@@ -146,6 +149,14 @@ def _make_comparison_pages(results, destination, exclude):
page=page, pages=pages))
+def _make_feature_info(results, destination):
+ """Create the feature readiness page."""
+
+ with open(os.path.join(destination, "feature.html"), 'w') as out:
+ out.write(_TEMPLATES.get_template('feature.mako').render(
+ results=results))
+
+
def html(results, destination, exclude):
"""
Produce HTML summaries.
@@ -161,3 +172,13 @@ def html(results, destination, exclude):
_copy_static_files(destination)
_make_testrun_info(results, destination, exclude)
_make_comparison_pages(results, destination, exclude)
+
+
+def feat(results, destination, feat_desc):
+ """Produce HTML feature readiness summary."""
+
+ feat_res = FeatResults([backends.load(i) for i in results], feat_desc)
+
+ _copy_static_files(destination)
+ _make_testrun_info(feat_res, destination)
+ _make_feature_info(feat_res, destination)
diff --git a/piglit b/piglit
index 5ae43e90a..c2a2e2bb5 100755
--- a/piglit
+++ b/piglit
@@ -139,6 +139,10 @@ def main():
add_help=False,
help="Aggregate incomplete piglit run.")
aggregate.set_defaults(func=summary.aggregate)
+ feature = summary_parser.add_parser('feature',
+ add_help=False,
+ help="generate feature readiness html report.")
+ feature.set_defaults(func=summary.feature)
# Parse the known arguments (piglit run or piglit summary html for
# example), and then pass the arguments that this parser doesn't know about
diff --git a/templates/feature.mako b/templates/feature.mako
new file mode 100644
index 000000000..ac9bc8677
--- /dev/null
+++ b/templates/feature.mako
@@ -0,0 +1,73 @@
+<%!
+ import posixpath # this must be posixpath, since we want /'s not \'s
+ import re
+
+
+ def feat_result(result):
+ """Percentage result string"""
+ return '{}/{}'.format(result[0], result[1])
+
+
+ def escape_filename(key):
+ """Avoid reserved characters in filenames."""
+ return re.sub(r'[<>:"|?*#]', '_', key)
+
+
+ def escape_pathname(key):
+ """ Remove / and \\ from names """
+ return re.sub(r'[/\\]', '_', key)
+
+
+ def normalize_href(href):
+ """Force backward slashes in URLs."""
+ return href.replace('\\', '/')
+%>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <title>Result summary</title>
+ <link rel="stylesheet" href="index.css" type="text/css" />
+ </head>
+ <body>
+ <h1>Feature readiness</h1>
+ <table>
+ <colgroup>
+ ## Name Column
+ <col />
+
+ ## Status columns
+ ## Create an additional column for each summary
+ % for _ in xrange(len(results.results)):
+ <col />
+ % endfor
+ </colgroup>
+ <tr>
+ <th/>
+ % for res in results.results:
+ <th class="head"><b>${res.name}</b><br />\
+ (<a href="${normalize_href(posixpath.join(escape_pathname(res.name), 'index.html'))}">info</a>)</th>
+ % endfor
+ </tr>
+ % for feature in results.features:
+ <tr>
+ ## Add the left most column, the feature name
+ <td>
+ <div class="group">
+ <b>${feature}</b>
+ </div>
+ </td>
+ ## add the feature totals
+ % for res in results.results:
+ <td class="${results.feat_status[res.name][feature]}">
+ <b>${feat_result(results.feat_fractions[res.name][feature])}</b>
+ </td>
+ % endfor
+ </tr>
+ % endfor
+ </table>
+ </body>
+</html>