未验证 提交 56aa6112 编写于 作者: L Lukáš Doktor

Merging pull request 1385

* https://github.com/avocado-framework/avocado:
  Makefile: use setup.py for getting the current versions
  Result: have a fallback result instance
  HTML Result: use existing result instead of custom result dict
  HTML Result: give the right name to ReportModel input
  HTML result: use pkg_resources to locate required resources
......@@ -12,7 +12,7 @@
PYTHON=$(shell which python)
PYTHON26=$(shell $(PYTHON) -V 2>&1 | grep 2.6 -q && echo true || echo false)
VERSION=$(shell $(PYTHON) $(CURDIR)/avocado/core/version.py)
VERSION=$(shell $(PYTHON) setup.py --version)
DESTDIR=/
BUILDIR=$(CURDIR)/debian/avocado
PROJECT=avocado
......
......@@ -17,16 +17,14 @@ HTML output module.
import codecs
import os
import shutil
import sys
import time
import subprocess
import urllib
import pystache
import pkg_resources
from .result import Result
from ..utils import path as utils_path
from ..utils import runtime
def check_resource_requirements():
......@@ -35,23 +33,19 @@ def check_resource_requirements():
Currently, only the template file is looked for
"""
base_path = os.path.dirname(sys.modules[__name__].__file__)
html_resources_path = os.path.join(base_path, 'resources', 'htmlresult')
template = os.path.join(html_resources_path, 'templates', 'report.mustache')
return os.path.exists(template)
return pkg_resources.resource_exists(
'avocado.core',
'resources/htmlresult/templates/report.mustache')
class ReportModel(object):
"""
Prepares JSON that can be passed up to mustache for rendering.
Prepares an object that can be passed up to mustache for rendering.
"""
def __init__(self, json_input, html_output):
"""
Base JSON that comes from test results.
"""
self.json = json_input
def __init__(self, result, html_output):
self.result = result
self.html_output = html_output
self.html_output_dir = os.path.abspath(os.path.dirname(html_output))
......@@ -69,13 +63,14 @@ class ReportModel(object):
return value
def job_id(self):
return self.json['job_id']
return self.result.job_unique_id
def execution_time(self):
return "%.2f" % self.json['time']
return "%.2f" % self.result.tests_total_time
def results_dir(self, relative_links=True):
results_dir = os.path.abspath(os.path.dirname(self.json['debuglog']))
results_dir = os.path.abspath(os.path.dirname(
self.result.logfile))
if relative_links:
return os.path.relpath(results_dir, self.html_output_dir)
else:
......@@ -85,17 +80,20 @@ class ReportModel(object):
return os.path.basename(self.results_dir(False))
def logdir(self):
return os.path.relpath(self.json['logdir'], self.html_output_dir)
logdir = os.path
path = os.path.relpath(self.result.logdir,
self.html_output_dir)
return urllib.quote(path)
def total(self):
return self.json['total']
return self.result.tests_total
def passed(self):
return self.json['pass']
return self.result.passed
def pass_rate(self):
total = float(self.json['total'])
passed = float(self.json['pass'])
total = float(self.result.tests_total)
passed = float(self.result.passed)
if total > 0:
pr = 100 * (passed / total)
else:
......@@ -130,27 +128,34 @@ class ReportModel(object):
"RUNNING": "info",
"NOSTATUS": "info",
"INTERRUPTED": "danger"}
test_info = self.json['tests']
test_info = []
results_dir = self.results_dir(False)
for t in test_info:
for t in self.result.tests:
formatted = {}
logdir = os.path.join(results_dir, 'test-results', t['logdir'])
t['logdir'] = os.path.relpath(logdir, self.html_output_dir)
formatted['logdir'] = os.path.relpath(logdir, self.html_output_dir)
logfile = os.path.join(logdir, 'debug.log')
t['logfile'] = os.path.relpath(logfile, self.html_output_dir)
t['logfile_basename'] = os.path.basename(logfile)
t['time'] = "%.2f" % t['time']
t['time_start'] = time.strftime("%Y-%m-%d %H:%M:%S",
time.localtime(t['time_start']))
t['row_class'] = mapping[t['status']]
formatted['logfile'] = os.path.relpath(logfile, self.html_output_dir)
formatted['logfile_basename'] = os.path.basename(logfile)
formatted['time'] = "%.2f" % t['time_elapsed']
formatted['time_start'] = time.strftime("%Y-%m-%d %H:%M:%S",
time.localtime(t['time_start']))
formatted['row_class'] = mapping[t['status']]
exhibition_limit = 40
if len(t['fail_reason']) > exhibition_limit:
t['fail_reason'] = ('<a data-container="body" '
'data-toggle="popover" '
'data-placement="top" '
'title="Error Details" '
'data-content="%s">%s...</a>' %
(t['fail_reason'],
t['fail_reason'][:exhibition_limit]))
fail_reason = t.get('fail_reason')
if fail_reason is None:
fail_reason = '<unknown>'
fail_reason = str(fail_reason)
if len(fail_reason) > exhibition_limit:
fail_reason = ('<a data-container="body" '
'data-toggle="popover" '
'data-placement="top" '
'title="Error Details" '
'data-content="%s">%s...</a>' %
('fail_reason',
'fail_reason'[:exhibition_limit]))
formatted['fail_reason'] = fail_reason
test_info.append(formatted)
return test_info
def _sysinfo_phase(self, phase):
......@@ -209,67 +214,48 @@ class HTMLResult(Result):
self.output = force_html_file
else:
self.output = self.args.html_output
self.json = None
def start_tests(self):
"""
Called once before any tests are executed.
"""
Result.start_tests(self)
self.json = {'debuglog': self.logfile,
'job_id': runtime.CURRENT_JOB.unique_id,
'tests': []}
def end_test(self, state):
"""
Called when the given test has been run.
:param state: result of :class:`avocado.core.test.Test.get_state`.
:type state: dict
"""
Result.end_test(self, state)
t = {'test': str(state.get('name', "<unknown>")),
'url': state.get('name', "<unknown>"),
'time_start': state.get('time_start', -1),
'time_end': state.get('time_end', -1),
'time': state.get('time_elapsed', -1),
'status': state.get('status', "ERROR"),
'fail_reason': str(state.get('fail_reason', "<unknown>")),
'whiteboard': state.get('whiteboard', "<unknown>"),
'logdir': urllib.quote(state.get('logdir', "<unknown>")),
'logfile': urllib.quote(state.get('logfile', "<unknown>"))
}
self.json['tests'].append(t)
def end_tests(self):
"""
Called once after all tests are executed.
"""
Result.end_tests(self)
self.json.update({
'total': len(self.json['tests']),
'pass': self.passed,
'errors': self.errors,
'failures': self.failed,
'skip': self.skipped,
'time': self.tests_total_time
})
self._render_report()
def _copy_static_resources(self):
module = 'avocado.core'
base_path = 'resources/htmlresult/static'
for top_dir in pkg_resources.resource_listdir(module, base_path):
rsrc_dir = base_path + '/%s' % top_dir
if pkg_resources.resource_isdir(module, rsrc_dir):
rsrc_files = pkg_resources.resource_listdir(module, rsrc_dir)
for rsrc_file in rsrc_files:
source = pkg_resources.resource_filename(
module,
rsrc_dir + '/%s' % rsrc_file)
dest = os.path.join(
os.path.dirname(os.path.abspath(self.output)),
top_dir,
os.path.basename(source))
pkg_resources.ensure_directory(dest)
shutil.copy(source, dest)
def _render_report(self):
context = ReportModel(json_input=self.json, html_output=self.output)
base_path = os.path.dirname(sys.modules[__name__].__file__)
html_resources_path = os.path.join(base_path, 'resources', 'htmlresult')
template = os.path.join(html_resources_path, 'templates', 'report.mustache')
context = ReportModel(result=self,
html_output=self.output)
template = pkg_resources.resource_string(
'avocado.core',
'resources/htmlresult/templates/report.mustache')
# pylint: disable=E0611
try:
if hasattr(pystache, 'Renderer'):
renderer = pystache.Renderer('utf-8', 'utf-8')
report_contents = renderer.render(open(template, 'r').read(), context)
report_contents = renderer.render(template, context)
else:
from pystache import view
v = view.View(open(template, 'r').read(), context)
v = view.View(template, context)
report_contents = v.render('utf8') # encodes into ascii
report_contents = codecs.decode("utf8") # decode to unicode
except UnicodeDecodeError as details:
......@@ -278,23 +264,14 @@ class HTMLResult(Result):
ui = logging.getLogger("avocado.app")
ui.critical("\n" + ("-" * 80))
ui.critical("HTML failed to render the template: %s\n\n",
open(template, 'r').read())
template)
ui.critical("-" * 80)
ui.critical("%s:\n\n", details)
ui.critical("%r\n\n", self.json)
ui.critical("%r", getattr(details, "object", "object not found"))
ui.critical("-" * 80)
raise
static_basedir = os.path.join(html_resources_path, 'static')
output_dir = os.path.dirname(os.path.abspath(self.output))
utils_path.init_dir(output_dir)
for resource_dir in os.listdir(static_basedir):
res_dir = os.path.join(static_basedir, resource_dir)
out_dir = os.path.join(output_dir, resource_dir)
if os.path.exists(out_dir):
shutil.rmtree(out_dir)
shutil.copytree(res_dir, out_dir)
self._copy_static_resources()
with codecs.open(self.output, 'w', 'utf-8') as report_file:
report_file.write(report_contents)
......
......@@ -264,6 +264,8 @@ class Job(object):
for klass in self.args.test_result_classes:
test_result_instance = klass(self)
self.result_proxy.add_output_plugin(test_result_instance)
else:
self.result_proxy.add_output_plugin(result.Result(self))
def _make_test_result(self):
"""
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册