提交 23a48d7b 编写于 作者: R Rudá Moura

Merge pull request #431 from lmr/htmlresult_fixes

HTML result fixes
......@@ -19,7 +19,7 @@ import os
import shutil
import sys
import time
import webbrowser
import subprocess
try:
import pystache
......@@ -31,9 +31,9 @@ else:
from avocado import runtime
from avocado.core import exit_codes
from avocado.core import output
from avocado.plugins import plugin
from avocado.result import TestResult
from avocado.utils import path as utils_path
from avocado.plugins import plugin
class ReportModel(object):
......@@ -57,7 +57,8 @@ class ReportModel(object):
return "%.2f" % self.json['time']
def _results_dir(self, relative_links=True):
debuglog_abspath = os.path.abspath(os.path.dirname(self.json['debuglog']))
debuglog_abspath = os.path.abspath(os.path.dirname(
self.json['debuglog']))
html_output_abspath = os.path.abspath(os.path.dirname(self.html_output))
if relative_links:
return os.path.relpath(debuglog_abspath, html_output_abspath)
......@@ -86,7 +87,8 @@ class ReportModel(object):
return "%.2f" % pr
def _get_sysinfo(self, sysinfo_file):
sysinfo_path = os.path.join(self._results_dir(relative_links=False), 'sysinfo', 'pre', sysinfo_file)
sysinfo_path = os.path.join(self._results_dir(relative_links=False),
'sysinfo', 'pre', sysinfo_file)
try:
with open(sysinfo_path, 'r') as sysinfo_file:
sysinfo_contents = sysinfo_file.read()
......@@ -114,23 +116,33 @@ class ReportModel(object):
"INTERRUPTED": "danger"}
test_info = self.json['tests']
for t in test_info:
t['link'] = os.path.join(self._results_dir(relative_links=self.relative_links), 'test-results', t['url'], 'debug.log')
t['link_basename'] = os.path.basename(t['link'])
t['dir_link'] = os.path.join(self._results_dir(relative_links=self.relative_links), 'test-results', t['url'])
t['logdir'] = os.path.join(self._results_dir(
relative_links=self.relative_links),
'test-results', t['logdir'])
t['logfile'] = os.path.join(self._results_dir(
relative_links=self.relative_links),
'test-results', t['logdir'],
'debug.log')
t['logfile_basename'] = os.path.basename(t['logfile'])
t['time'] = "%.2f" % t['time']
t['time_start'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(t['time_start']))
t['time_start'] = time.strftime("%Y-%m-%d %H:%M:%S",
time.localtime(t['time_start']))
t['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]))
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]))
return test_info
def sysinfo(self):
sysinfo_list = []
base_path = os.path.join(self._results_dir(relative_links=False), 'sysinfo', 'pre')
base_path = os.path.join(self._results_dir(relative_links=False),
'sysinfo', 'pre')
try:
sysinfo_files = os.listdir(base_path)
except OSError:
......@@ -147,7 +159,8 @@ class ReportModel(object):
sysinfo_dict['element_id'] = 'heading_%s' % s_id
sysinfo_dict['collapse_id'] = 'collapse_%s' % s_id
except OSError:
sysinfo_dict[s_f] = 'Error reading sysinfo file %s' % sysinfo_path
sysinfo_dict[s_f] = ('Error reading sysinfo file %s' %
sysinfo_path)
sysinfo_list.append(sysinfo_dict)
s_id += 1
return sysinfo_list
......@@ -197,6 +210,8 @@ class HTMLTestResult(TestResult):
'status': state['status'],
'fail_reason': state['fail_reason'],
'whiteboard': state['whiteboard'],
'logdir': state['logdir'],
'logfile': state['logfile']
}
self.json['tests'].append(t)
......@@ -221,7 +236,8 @@ class HTMLTestResult(TestResult):
else:
relative_links = False
context = ReportModel(json_input=self.json, html_output=self.output, relative_links=relative_links)
context = ReportModel(json_input=self.json, html_output=self.output,
relative_links=relative_links)
renderer = pystache.Renderer('utf-8', 'utf-8')
html = HTML()
template = html.get_resource_path('templates', 'report.mustache')
......@@ -240,7 +256,15 @@ class HTMLTestResult(TestResult):
if self.args is not None:
if getattr(self.args, 'open_browser'):
webbrowser.open(self.output)
# if possible, put browser in separate process group, so
# keyboard interrupts don't affect browser as well as Python
setsid = getattr(os, 'setsid', None)
if not setsid:
setsid = getattr(os, 'setpgrp', None)
inout = file(os.devnull, "r+")
cmd = ['xdg-open', self.output]
subprocess.Popen(cmd, close_fds=True, stdin=inout, stdout=inout,
stderr=inout, preexec_fn=setsid)
class HTML(plugin.Plugin):
......@@ -260,25 +284,27 @@ class HTML(plugin.Plugin):
self.parser.runner.add_argument(
'--html', type=str,
dest='html_output',
help=('Enable HTML output to the file where the result should be written. '
'The option - is not supported since not all HTML resources can be '
'embedded into a single file (page resources will be copied to the '
help=('Enable HTML output to the file where the result should be '
'written. The value - (output to stdout) is not supported '
'since not all HTML resources can be embedded into a '
'single file (page resources will be copied to the '
'output file dir)'))
self.parser.runner.add_argument(
'--relative-links',
dest='relative_links',
action='store_true',
default=False,
help=('On the HTML report, generate anchor links with relative instead of absolute paths. Current: %s' %
False))
help=('On the HTML report, generate anchor links with relative '
'instead of absolute paths. Current: %s' % False))
self.parser.runner.add_argument(
'--open-browser',
dest='open_browser',
action='store_true',
default=False,
help='Open the generated report on your preferred browser. '
'This works even if --html was not explicitly passed, since an HTML '
'report is always generated on the job results dir. Current: %s' % False)
'This works even if --html was not explicitly passed, '
'since an HTML report is always generated on the job '
'results dir. Current: %s' % False)
self.configured = True
def activate(self, app_args):
......@@ -287,10 +313,12 @@ class HTML(plugin.Plugin):
if app_args.html_output == '-':
view = output.View(app_args=app_args)
view.notify(event='error',
msg="HTML to stdout not supported "
"(not all HTML resources can be embedded to a single file)")
msg='HTML to stdout not supported '
'(not all HTML resources can be embedded '
'on a single file)')
sys.exit(exit_codes.AVOCADO_JOB_FAIL)
else:
self.parser.application.set_defaults(html_result=HTMLTestResult)
self.parser.application.set_defaults(
html_result=HTMLTestResult)
except AttributeError:
pass
......@@ -61,6 +61,9 @@ class JSONTestResult(TestResult):
'time': state['time_elapsed'],
'status': state['status'],
'whiteboard': state['whiteboard'],
'logdir': state['logdir'],
'logfile': state['logfile'],
'fail_reason': str(state['fail_reason'])
}
self.json['tests'].append(t)
......
......@@ -50,15 +50,27 @@ class RemoteTestRunner(TestRunner):
if result.exit_status == 127:
raise exceptions.JobError('Remote machine does not have avocado '
'installed')
json_result = None
for json_output in result.stdout.splitlines():
# We expect dictionary:
if json_output.startswith('{') and json_output.endswith('}'):
try:
return json.loads(json_output)
json_result = json.loads(json_output)
except ValueError:
pass
raise ValueError("Could not parse JSON from avocado remote output:"
"\n%s" % result.stdout)
if json_result is None:
raise ValueError("Could not parse JSON from avocado remote output:"
"\n%s" % result.stdout)
for t_dict in json_result['tests']:
logdir = os.path.dirname(self.result.stream.debuglog)
logdir = os.path.join(logdir, 'test-results')
logdir = os.path.join(logdir, os.path.relpath(t_dict['url'], '/'))
t_dict['logdir'] = logdir
t_dict['logfile'] = os.path.join(logdir, 'debug.log')
return json_result
def run_suite(self, test_suite):
"""
......@@ -79,7 +91,11 @@ class RemoteTestRunner(TestRunner):
time=tst['time'],
start=tst['start'],
end=tst['end'],
status=tst['status'])
status=tst['status'],
logdir=tst['logdir'],
logfile=tst['logfile'],
fail_reason=tst['fail_reason']
)
state = test.get_state()
self.result.start_test(state)
self.result.check_test(state)
......
......@@ -68,11 +68,11 @@
{{#tests}}
<tr class="{{row_class}}">
<td>{{time_start}}</td>
<td><a href="{{dir_link}}">{{url}}</a></td>
<td><a href="{{logdir}}">{{url}}</a></td>
<td>{{status}}</td>
<td>{{time}}</td>
<td>{{& fail_reason}}</td>
<td><a href="{{link}}">{{link_basename}}</a></td>
<td><a href="{{logfile}}">{{logfile_basename}}</a></td>
</tr>
{{/tests}}
</table>
......
......@@ -612,7 +612,8 @@ class RemoteTest(object):
Mimics :class:`avocado.test.Test` for remote tests.
"""
def __init__(self, name, status, time, start, end):
def __init__(self, name, status, time, start, end, fail_reason, logdir,
logfile):
note = "Not supported yet"
self.name = name
self.tagged_name = name
......@@ -623,9 +624,11 @@ class RemoteTest(object):
self.fail_class = note
self.traceback = note
self.text_output = note
self.fail_reason = note
self.fail_reason = fail_reason
self.whiteboard = ''
self.job_unique_id = ''
self.logdir = logdir
self.logfile = logfile
def get_state(self):
"""
......
#!/usr/bin/env python
import unittest
import os
from flexmock import flexmock, flexmock_teardown
from avocado.plugins import remote
cwd = os.getcwd()
JSON_RESULTS = ('Something other than json\n'
'{"tests": [{"test": "sleeptest.1", "url": "sleeptest", '
'"fail_reason": "None", '
'"status": "PASS", "time": 1.23, "start": 0, "end": 1.23}],'
'"debuglog": "/home/user/avocado/logs/run-2014-05-26-15.45.'
'37/debug.log", "errors": 0, "skip": 0, "time": 1.4, '
'"logdir": "/local/path/test-results%s/sleeptest", '
'"logdir": "/local/path/test-results%s/sleeptest", '
'"start": 0, "end": 1.4, "pass": 1, "failures": 0, "total": '
'1}\nAdditional stuff other than json')
'1}\nAdditional stuff other than json' % (cwd, cwd))
class RemoteTestRunnerTest(unittest.TestCase):
......@@ -42,7 +47,10 @@ class RemoteTestRunnerTest(unittest.TestCase):
'text_output': 'Not supported yet', 'time_end': 1.23,
'tagged_name': u'sleeptest.1', 'time_elapsed': 1.23,
'fail_class': 'Not supported yet', 'job_unique_id': '',
'fail_reason': 'Not supported yet'}
'fail_reason': 'None',
'logdir': '/local/path/test-results%s/sleeptest' % cwd,
'logfile': '/local/path/test-results%s/sleeptest/debug.log' %
cwd}
Results.should_receive('start_test').once().with_args(args).ordered()
Results.should_receive('check_test').once().with_args(args).ordered()
(Remote.should_receive('receive_files')
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册