__author__ = 'pengfei913'
__version__ = "0.8.2"
# TODO: color stderr
# TODO: simplify javascript using ,ore than 1 class in the class attribute?
import datetime
import StringIO
import sys
import time
import unittest
from xml.sax import saxutils
import socket
# ------------------------------------------------------------------------
# The redirectors below are used to capture output during testing. Output
# sent to sys.stdout and sys.stderr are automatically captured. However
# in some cases sys.stdout is already cached before HTMLTestRunner is
# invoked (e.g. calling logging.basicConfig). In order to capture those
# output, use the redirectors for the cached stream.
#
# e.g.
# >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)
# >>>
class OutputRedirector(object):
""" Wrapper to redirect stdout or stderr """
def __init__(self, fp):
self.fp = fp
def write(self, s):
self.fp.write(s)
def writelines(self, lines):
self.fp.writelines(lines)
def flush(self):
self.fp.flush()
stdout_redirector = OutputRedirector(sys.stdout)
stderr_redirector = OutputRedirector(sys.stderr)
class Template_mixin(object):
STATUS = {
0: 'pass',
1: 'fail',
2: 'error',
}
DEFAULT_TITLE = 'Unit Test Report'
# ------------------------------------------------------------------------
# HTML Template
HTML_TMPL = r"""
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html" xmlns="http://www.w3.org/1999/html">
<head>
<title>%(title)s</title>
<meta name="generator" content="%(generator)s"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
%(stylesheet)s
<link href="http://project.laravel.qa.anhouse.com.cn/css/pysele/style.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="http://project.laravel.qa.anhouse.com.cn/js/pysele/jquery.js"></script>
<script type="text/javascript" src="http://project.laravel.qa.anhouse.com.cn/js/pysele/quickwin.verticalbar.js" ></script>
<script type="text/javascript" src="http://project.laravel.qa.anhouse.com.cn/js/pysele/quickwin.horizontalbar.js" ></script>
<script type="text/javascript" src="http://project.laravel.qa.anhouse.com.cn/js/pysele/quickwin.histogram.js" ></script>
<script src="http://project.laravel.qa.anhouse.com.cn/js/pysele/jquery.min.js"></script>
<!--[if IE]>
<script src="http://project.laravel.qa.anhouse.com.cn/js/pysele/excanvas.js"></script>
<![endif]-->
<script type="text/javascript" src="http://project.laravel.qa.anhouse.com.cn/js/pysele/pieChart.js"></script>
</head>
<body>
<script language="javascript" type="text/javascript"><!--
output_list = Array();
/* level - 0:Summary; 1:Failed; 2:All */
function showCase(level) {
trs = document.getElementsByTagName("tr");
for (var i = 0; i < trs.length; i++) {
tr = trs[i];
id = tr.id;
if (id.substr(0,2) == 'ft') {
if (level < 1) {
tr.className = 'hiddenRow';
}
else {
tr.className = '';
}
}
if (id.substr(0,2) == 'pt') {
if (level > 1) {
tr.className = '';
}
else {
tr.className = 'hiddenRow';
}
}
}
}
function showClassDetail(cid, count) {
var id_list = Array(count);
var toHide = 1;
for (var i = 0; i < count; i++) {
tid0 = 't' + cid.substr(1) + '.' + (i+1);
tid = 'f' + tid0;
tr = document.getElementById(tid);
if (!tr) {
tid = 'p' + tid0;
tr = document.getElementById(tid);
}
id_list[i] = tid;
if (tr.className) {
toHide = 0;
}
}
for (var i = 0; i < count; i++) {
tid = id_list[i];
if (toHide) {
document.getElementById('div_'+tid).style.display = 'none'
document.getElementById(tid).className = 'hiddenRow';
}
else {
document.getElementById(tid).className = '';
}
}
}
function showTestDetail(div_id){
var details_div = document.getElementById(div_id)
var displayState = details_div.style.display
// alert(displayState)
if (displayState != 'block' ) {
displayState = 'block'
details_div.style.display = 'block'
}
else {
details_div.style.display = 'none'
}
}
function html_escape(s) {
s = s.replace(/&/g,'&');
s = s.replace(/</g,'<');
s = s.replace(/>/g,'>');
return s;
}
/* obsoleted by detail in <div>
function showOutput(id, name) {
var w = window.open("", //url
name,
"resizable,scrollbars,status,width=800,height=450");
d = w.document;
d.write("<pre>");
d.write(html_escape(output_list[id]));
d.write("\n");
d.write("<a href='javascript:window.close()'>close</a>\n");
d.write("</pre>\n");
d.close();
}
*/
--></script>
%(heading)s
%(envioronment)s
%(rsoverview)s
%(overview)s
%(resultchart)s
%(report)s
%(ending)s
</body>
</html>
"""
# variables: (title, generator, stylesheet, heading, report, ending)
# ------------------------------------------------------------------------
# Stylesheet
#
# alternatively use a <link> for external style sheet, e.g.
# <link rel="stylesheet" href="$url" type="text/css">
STYLESHEET_TMPL = """
<style type="text/css" media="screen">
*{margin:0;padding:0;list-style-type:none;}
table th {
font-weight: bold;
}
table td {
text-align: center;
vertical-align: middle;
}
body, html{
height: 100%;
width: 100%;
margin: 0; padding: 0;
}
/* -- heading ---------------------------------------------------------------------- */
h1 {font-family: "宋体" ;font-size: 300%;font-weight:bold;margin-top:40px;margin-bottom: 40px;}
.heading {
margin-top: 0ex;
margin-bottom: 1ex;
}
.heading .attribute {
margin-top: 1ex;
margin-bottom: 0;
text-indent:12em;
}
/* -- add content ------------------------------------------------------------------------ */
p{
text-indent: 12em;
font-family: "宋体";
font-size: 110%;
font-weight: bold;
margin-bottom: 6px;
text-padding:10px;
}
/* -- 测试环境 ------------------------------------------------------------------------ */
.environment table {
height: 80%;
width: 60%;
margin: auto;
}
.environment table th {
background: sandybrown;
}
.environment table td {
margin: auto;
}
.environment table {
height: 80%;
width: 60%;
margin: auto;
}
/* -- 测试结果概要 ------------------------------------------------------------------------ */
.result table {
height: 80%;
width: 60%;
margin: auto;
}
.result table th {
background: sandybrown;
}
.result table td {
margin: auto;
}
/* -- 测试用例分布: ------------------------------------------------------------------------ */
#container{width:900px;margin:15px auto; overflow:hidden}
#chart, #chartData{border:1px solid #333;background:#ebedf2 url("http://project.laravel.qa.anhouse.com.cn/image/pysele/gradient.png") repeat-x 0 0;}
#chart{display:block;margin:0px 0 20px 45px;float:left;cursor:pointer;}
#chartData{width:200px;margin:0 40px 180px 50px;float:right;border-collapse:collapse;box-shadow:0 0 1em rgba(0, 0, 0, 0.5);-moz-box-shadow:0 0 1em rgba(0, 0, 0, 0.5);-webkit-box-shadow:0 0 1em rgba(0, 0, 0, 0.5);background-position:0 -100px;}
#chartData th, #chartData td{padding:0.5em;border:1px dotted #666;text-align:left;}
#chartData th{border-bottom:2px solid #333;text-transform:uppercase;}
#chartData td{cursor:pointer;}
#chartData td.highlight{background:#e8e8e8;}
#chartData tr:hover td{background:#f0f0f0;}
/* -- 测试结果分布: ------------------------------------------------------------------------ */
.result_r1 {
margin-left: 280px;
margin-top:20px;
}
/* -- css div popup ------------------------------------------------------------------------ */
a.popup_link {
}
a.popup_link:hover {
color: red;
}
.popup_window {
display: none;
position: relative;
left: 0px;
top: 0px;
/*border: solid #627173 1px; */
padding: 10px;
background-color: #FF0000;
font-family: "Lucida Console", "Courier New", Courier, monospace;
text-align: left;
font-size: 8pt;
width: 500px;
}
}
/* -- report ------------------------------------------------------------------------ */
.result_detail table {
height: 90%;
width: 50%;
margin:auto;
margin-top: 10px;
}
.result_detail table th {
background: forestgreen;
}
.result_detail table td {
margin:auto;
}
#show_detail_line {
margin-top: 10ex;
margin-bottom: 1ex;
}
#result_table {
width: 75%;
border-collapse: collapse;
border: 1px solid #777;
margin:0 auto;
}
#header_row {
font-weight: bold;
color: white;
background-color: #777;
}
#result_table td {
border: 1px solid #777;
padding: 2px;
}
#total_row { font-weight: bold; }
.passClass { background-color: #6c6; }
.failClass { background-color: #c60; }
.errorClass { background-color: #c00; }
.passCase { color: #6c6; }
.failCase { color: #c60; font-weight: bold; }
.errorCase { color: #c00; font-weight: bold; }
.hiddenRow { display: none; }
.testcase { margin-left: 2em; }
/* -- ending ---------------------------------------------------------------------- */
.foot {
text-align: center;
padding:10px 0;
color:#999;;
font-size:10px;
font-family: "sans-serif";
display: 1ine;
margin-top: 20px;
background: #e8e8e8 none repeat scroll 0 0;
}
</style>
"""
# ------------------------------------------------------------------------
# Heading
#
HEADING_TMPL = """
<h1 align="center">%(title)s</h1>
<div class='heading'>
%(parameters)s
</div>
""" # variables: (title, parameters, description)
HEADING_ATTRIBUTE_TMPL = """<p class='attribute'><strong>%(name)s:</strong> %(value)s</p>
""" # variables: (name, value)
# ------------------------------------------------------------------------
# Report
#
ENVIRONMENT_TMPL="""
<p>测试环境:</p>
<div class="environment" >
<table border="1" aligin="center" >
<tr aligin="center" width="200">
<th>电脑名称</th>
<th>IP</th>
<th>运行浏览器</th>
<th>运行环境</th>
</tr>
<tr>
<td aligin="center" width="200">
%(hostname)s
</td>
<td aligin="center" width="200">
%(ip)s
</td>
<td aligin="center" width="200">
%(browser)s
</td>
<td aligin="center" width="200">
%(url)s
</td>
</tr>
</table>
</div>
"""
RESULT_OVERVIEW_TMPL="""
<p>测试结果概要:</p>
<div class="result">
<table aligin="center" border="1">
<tr bgcolor="#76EE00">
<th aligin="center" width="200">
用例总数
</th>
<th aligin="center" width="200">
用例通过率
</th>
<th aligin="center" width="200">
用例失败率
</th>
<th aligin="center" width="200">
用例错误率
</th>
</tr>
<tr>
<td aligin="center" width="200">
%(all_count)s
</td>
<td aligin="center" width="200">
%(pass_rate)s
</td>
<td aligin="center" width="200">
%(fail_rate)s
</td>
<td aligin="center" width="200">
%(error_rate)s
</td>
</tr>
</table>
</div>
"""
OVERVIEW_TMPL="""
<p>测试用例分布:</p>
<style type="text/css">
body{background:#fff;color:#333;font-family:"Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;font-size:0.9em;}
</style>
<div style="text-align:center;clear:both;">
<script src="/gg_bd_ad_720x90.js" type="text/javascript"></script>
<script src="/follow.js" type="text/javascript"></script>
</div>
<div id="container">
<canvas id="chart" width="560" height="490"></canvas>
<table id="chartData">
<tr>
<th>用例模块</th><th>用例数量</th>
</tr>
<tr style="color:#0DA068">
<td>短信平台</td><td>%(Sms_count)s</td>
</tr>
<tr style="color:#194E9C">
<td>权限系统</td><td>%(Permission_count)s</td>
</tr>
</table>
</div>
"""
RESULT_CHART_TMPL="""
<p>测试结果分布:</p>
<style type="text/css">
</style>
<div class="result_r1">
<canvas id ="myCanvas" style="border:1px solid #d3d3d3;" ></canvas>
<script type="text/javascript">
var jasonData = {
"title": "平安好房业务测试情况",
"verticaltitle": "用例数(个)",
"horizontaltitle": "用例模块",
"data":
[{ "category": "通过", "datacollection": [{ "title": "短信平台", "count":%(Sms_pass_count)s }, { "title": "权限系统", "count": %(Permission_pass_count)s }]},
{ "category": "失败", "datacollection": [{ "title": " ", "count": %(Sms_fail_count)s},{ "title": " ", "count": %(Permission_fail_count)s }] },
{ "category": "错误", "datacollection": [{ "title": " ", "count": %(Sms_error_count)s}, { "title": " ", "count":%(Permission_error_count)s}] }] };
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var v = new histogram(ctx, jasonData,1);
v.draw();
</script>
</div>
"""
REPORT_TMPL = """
<p>测试详情:</p>
<div class="result_table">
<table id='result_table'>
<colgroup>
<col align='left' />
<col align='right' />
<col align='right' />
<col align='right' />
<col align='right' />
<col align='right' />
</colgroup>
<tr id='header_row'>
<td>Test Group/Test case</td>
<td>用例数</td>
<td>用例通过数</td>
<td>用例失败数</td>
<td>用例错误数</td>
<td>查看详情</td>
<td>截图</td>
</tr>
%(test_list)s
</table>
</div>
""" # variables: (test_list, count, Pass, fail, error)
REPORT_CLASS_TMPL = r"""
<tr class='%(style)s'>
<td>%(desc)s</td>
<td>%(count)s</td>
<td>%(Pass)s</td>
<td>%(fail)s</td>
<td>%(error)s</td>
<td><a href="javascript:showClassDetail('%(cid)s',%(count)s)">Detail</a></td>
<td> </td>
</tr>
""" # variables: (style, desc, count, Pass, fail, error, cid)
REPORT_TEST_WITH_OUTPUT_TMPL = r"""
<tr id='%(tid)s' class='%(Class)s'>
<td class='%(style)s'><div class='test_case'>%(desc)s</div></td>
<td colspan='5' align='center'>
<!--css div popup start-->
<a class="popup_link" οnfοcus='this.blur();' href="javascript:showTestDetail('div_%(tid)s')" >
%(status)s</a>
<div id='div_%(tid)s' class="popup_window">
<div style='text-align: right; color:red;cursor:pointer'>
<a οnfοcus='this.blur();' οnclick="document.getElementById('div_%(tid)s').style.display = 'none' " >
[x]</a>
</div>
<pre>
%(script)s
</pre>
</div>
<!--css div popup end-->
</td>
<td align='center'>
<a href="%(image)s"title="%(image)s">
<img src="http://project.laravel.qa.anhouse.com.cn/image/pysele/test.png"height=20 width=20 border=0/></a>
</td>
</tr>
""" # variables: (tid, Class, style, desc, status)
REPORT_TEST_NO_OUTPUT_TMPL = r"""
<tr id='%(tid)s' class='%(Class)s'>
<td class='%(style)s'><div class='test_case'>%(desc)s</div></td>
<td colspan='5' align='center'>%(status)s</td>
</tr>
""" # variables: (tid, Class, style, desc, status)
REPORT_TEST_OUTPUT_TMPL = r"""
%(id)s: %(output)s
"""
REPORT_TEST_OUTPUT_IMAGE = r"""
%(screenshot)s
"""
# REPORT_TEST_OUTPUT_CASEID = r"""
# %(case_id)s
#""" # variables: (id, output)
# ------------------------------------------------------------------------
# ENDING
#
ENDING_TMPL = """
<div class="foot" >
<p>
<image src="http://project.laravel.qa.anhouse.com.cn/image/pysele/hf-logo.png" width="127px" height="46px" margin-left="30px" />
</p>
<p >版权所有 © 平安好房(上海)电子商务有限公司 测试二组 Copyright © 2014 pinganfang.com All Rights Reserved
<a hfef="###">沪ICP备 14012051号</a>
</p>
</div>
"""
# -------------------- The end of the Template class -------------------
TestResult = unittest.TestResult
class _TestResult(TestResult):
# note: _TestResult is a pure representation of results.
# It lacks the output and reporting ability compares to unittest._TextTestResult.
def __init__(self, verbosity=1):
TestResult.__init__(self)
self.stdout0 = None
self.stderr0 = None
self.success_count = 0
self.failure_count = 0
self.error_count = 0
self.verbosity = verbosity
# result is a list of result in 4 tuple
# (
# result code (0: success; 1: fail; 2: error),
# TestCase object,
# Test output (byte string),
# stack trace,
# )
self.result = []
def startTest(self, test):
TestResult.startTest(self, test)
# just one buffer for both stdout and stderr
self.outputBuffer = StringIO.StringIO()
stdout_redirector.fp = self.outputBuffer
stderr_redirector.fp = self.outputBuffer
self.stdout0 = sys.stdout
self.stderr0 = sys.stderr
sys.stdout = stdout_redirector
sys.stderr = stderr_redirector
def complete_output(self):
"""
Disconnect output redirection and return buffer.
Safe to call multiple times.
"""
if self.stdout0:
sys.stdout = self.stdout0
sys.stderr = self.stderr0
self.stdout0 = None
self.stderr0 = None
return self.outputBuffer.getvalue()
def stopTest(self, test):
# Usually one of addSuccess, addError or addFailure would have been called.
# But there are some path in unittest that would bypass this.
# We must disconnect stdout in stopTest(), which is guaranteed to be called.
self.complete_output()
def addSuccess(self, test):
self.success_count += 1
TestResult.addSuccess(self, test)
output = self.complete_output()
self.result.append((0, test, output, ''))
if self.verbosity > 1:
sys.stderr.write('ok ')
sys.stderr.write(str(test))
sys.stderr.write('\n')
else:
sys.stderr.write('.')
def addError(self, test, err):
self.error_count += 1
TestResult.addError(self, test, err)
_, _exc_str = self.errors[-1]
output = self.complete_output()
self.result.append((2, test, output, _exc_str))
if self.verbosity > 1:
sys.stderr.write('E ')
sys.stderr.write(str(test))
sys.stderr.write('\n')
else:
sys.stderr.write('E')
def addFailure(self, test, err):
self.failure_count += 1
TestResult.addFailure(self, test, err)
_, _exc_str = self.failures[-1]
output = self.complete_output()
self.result.append((1, test, output, _exc_str))
if self.verbosity > 1:
sys.stderr.write('F ')
sys.stderr.write(str(test))
sys.stderr.write('\n')
else:
sys.stderr.write('F')
class HTMLTestRunner(Template_mixin):
"""
"""
def __init__(self, stream=sys.stdout, verbosity=1, title=None):
self.stream = stream
self.verbosity = verbosity
if title is None:
self.title = self.DEFAULT_TITLE
else:
self.title = title
self.startTime = datetime.datetime.now()
def run(self, test):
"Run the given test case or test suite."
result = _TestResult(self.verbosity)
test(result)
self.stopTime = datetime.datetime.now()
self.generateReport(test, result)
print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)
return result
def sortResult(self, result_list):
# unittest does not seems to run in any particular order.
# Here at least we want to group them together by class.
rmap = {}
classes = []
for n,t,o,e in result_list:
cls = t.__class__
if not rmap.has_key(cls):
rmap[cls] = []
classes.append(cls)
rmap[cls].append((n,t,o,e))
r = [(cls, rmap[cls]) for cls in classes]
return r
def getReportAttributes(self, result):
"""
Return report attributes as a list of (name, value).
Override this to add custom attributes.
"""
startTime = str(self.startTime)[:19]
duration = str(self.stopTime - self.startTime)
status = []
if result.success_count: status.append('Pass %s' % result.success_count)
if result.failure_count: status.append('Failure %s' % result.failure_count)
if result.error_count: status.append('Error %s' % result.error_count )
if status:
status = ' '.join(status)
else:
status = 'none'
return [
('Start Time', startTime),
('Duration', duration),
('Status', status),
]
def generateReport(self, test, result):
report_attrs = self.getReportAttributes(result)
generator = 'HTMLTestRunner %s' % __version__
stylesheet = self._generate_stylesheet()
heading = self._generate_heading(report_attrs)
envioronment=self._generate_Environment()
rsoverview=self._generate_RsOverview(result)
overview=self._generate_Overview(result)
resultchart=self._generate_Resultchart(result)
report = self._generate_report(result)
ending = self._generate_ending()
output = self.HTML_TMPL % dict(
title = saxutils.escape(self.title),
generator = generator,
stylesheet = stylesheet,
heading = heading,
overview=overview,
resultchart=resultchart,
envioronment=envioronment,
rsoverview=rsoverview,
report = report,
ending = ending,
)
self.stream.write(output.encode('utf8'))
def _generate_Environment(self):
hostname=socket.gethostname()
ip=socket.gethostbyname(hostname)
url="http://www.pinganfang.com/"
browser="chrome浏览器"
envioronment=self.ENVIRONMENT_TMPL % dict(
ip=ip,
hostname=hostname,
url=url,
browser=browser,
)
return envioronment
def _generate_RsOverview(self,result):
rows = []
sortedResult = self.sortResult(result.result)
for cid, (cls, cls_results) in enumerate(sortedResult):
# subtotal for a class
np = nf = ne = 0
for n,t,o,e in cls_results:
if n == 0: np += 1
elif n == 1: nf += 1
else: ne += 1
# format class description
if cls.__module__ == "__main__":
name = cls.__name__
else:
name = "%s.%s" % (cls.__module__, cls.__name__)
doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
desc = doc and '%s: %s' % (name, doc) or name
row = self.REPORT_CLASS_TMPL % dict(
style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',
desc = desc,
count = np+nf+ne,
Pass = np,
fail = nf,
error = ne,
cid = 'c%s' % (cid+1),
)
rows.append(row)
for tid, (n,t,o,e) in enumerate(cls_results):
self._generate_report_test(rows, cid, tid, n, t, o, e)
all_count= result.success_count+result.failure_count+result.error_count
Pass=result.success_count
fail = result.failure_count
error = result.error_count
pass_rate=Pass/all_count
fail_rate=fail/all_count
error_rate=error/all_count
rsoverview = self.RESULT_OVERVIEW_TMPL % dict(
all_count=all_count,
pass_rate=pass_rate*'100%',
fail_rate=fail_rate*'100%',
error_rate=error_rate*'100%',
)
return rsoverview
def _generate_Overview(self,result):
rows = []
sortedResult = self.sortResult(result.result)
x={}
for cid, (cls, cls_results) in enumerate(sortedResult):
# subtotal for a class
np = nf = ne = 0
for n,t,o,e in cls_results:
if n == 0: np += 1
elif n == 1: nf += 1
else: ne += 1
# format class description
if cls.__module__ == "__main__":
name = cls.__name__
else:
name = "%s.%s" % (cls.__module__, cls.__name__)
doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
desc = doc and '%s: %s' % (name, doc) or name
module=desc.split('.')[1]
x[module]=np+nf+ne
z={'Sms':0,'Permission':0}
for i in z.keys():
if i in x.keys():
z[i]=x[i]
else:
z[i]=0
overview=self.OVERVIEW_TMPL % dict(
Sms_count=z['Sms'],
Permission_count=z['Permission'],
)
return overview
def _generate_Resultchart(self,result):
rows = []
sortedResult = self.sortResult(result.result)
x={}
b={}
c={}
d={}
for cid, (cls, cls_results) in enumerate(sortedResult):
# subtotal for a class
np = nf = ne = 0
for n,t,o,e in cls_results:
if n == 0: np += 1
elif n == 1: nf += 1
else: ne += 1
# format class description
if cls.__module__ == "__main__":
name = cls.__name__
else:
name = "%s.%s" % (cls.__module__, cls.__name__)
doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
desc = doc and '%s: %s' % (name, doc) or name
module=desc.split('.')[1]
x[module]=np+nf+ne
b[module]=np
c[module]=nf
d[module]=ne
z={'Sms':0,'Permission':0}
p={'Sms':0,'Permission':0}
f={'Sms':0,'Permission':0}
e={'Sms':0,'Permission':0}
for i in z.keys():
if i in x.keys():
p[i]=b[i]
f[i]=c[i]
e[i]=d[i]
else:
p[i]=0
f[i]=0
e[i]=0
rschart=self.RESULT_CHART_TMPL % dict (
Sms_pass_count=p['Sms'],
Sms_fail_count=f['Sms'],
Sms_error_count=e['Sms'],
Permission_pass_count=p['Permission'],
Permission_fail_count=f['Permission'],
Permission_error_count=e['Permission'],
)
return rschart
def _generate_stylesheet(self):
return self.STYLESHEET_TMPL
def _generate_heading(self, report_attrs):
a_lines = []
for name, value in report_attrs:
line = self.HEADING_ATTRIBUTE_TMPL % dict(
name = saxutils.escape(name),
value = saxutils.escape(value),
)
a_lines.append(line)
heading = self.HEADING_TMPL % dict(
title = saxutils.escape(self.title),
parameters = ''.join(a_lines),
)
return heading
def _generate_report(self, result):
rows = []
sortedResult = self.sortResult(result.result)
for cid, (cls, cls_results) in enumerate(sortedResult):
# subtotal for a class
np = nf = ne = 0
for n,t,o,e in cls_results:
if n == 0: np += 1
elif n == 1: nf += 1
else: ne += 1
# format class description
if cls.__module__ == "__main__":
name = cls.__name__
else:
name = "%s.%s" % (cls.__module__, cls.__name__)
doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
desc = doc and '%s: %s' % (name, doc) or name
row = self.REPORT_CLASS_TMPL % dict(
style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',
desc = desc,
count = np+nf+ne,
Pass = np,
fail = nf,
error = ne,
cid = 'c%s' % (cid+1),
)
rows.append(row)
for tid, (n,t,o,e) in enumerate(cls_results):
self._generate_report_test(rows, cid, tid, n, t, o, e)
report = self.REPORT_TMPL % dict(
test_list = ''.join(rows),
count = str(result.success_count+result.failure_count+result.error_count),
Pass = str(result.success_count),
fail = str(result.failure_count),
error = str(result.error_count),
)
return report
def _generate_report_test(self, rows, cid, tid, n, t, o, e):
# e.g. 'pt1.1', 'ft1.1', etc
has_output = bool(o or e)
tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1)
name = t.id().split('.')[-1]
doc = t.shortDescription() or ""
desc = doc and ('%s: %s' % (name, doc)) or name
tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL
# o and e should be byte string because they are collected from stdout and stderr?
if isinstance(o,str):
# TODO: some problem with 'string_escape': it escape \n and mess up formating
# uo = unicode(o.encode('string_escape'))
uo = o.decode('latin-1')
else:
uo = o
if isinstance(e,str):
# TODO: some problem with 'string_escape': it escape \n and mess up formating
# ue = unicode(e.encode('string_escape'))
ue = e.decode('latin-1')
else:
ue = e
script = self.REPORT_TEST_OUTPUT_TMPL % dict(
id = tid,
output = saxutils.escape(uo+ue),
)
image=self.REPORT_TEST_OUTPUT_IMAGE %dict(
screenshot=saxutils.escape(uo+ue),
)
# caseid=self.REPORT_TEST_OUTPUT_CASEID %dict(
# case_id=saxutils.escape(uo+ue),
# )
row = tmpl % dict(
tid = tid,
Class = (n == 0 and 'hiddenRow' or 'none'),
style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'),
desc = desc,
script = script,
image=image[image.find("image"):(int(image.find("png"))+3)],
#caseid=caseid[caseid.find("case"):(int(caseid.find("case"))+9)],
status = self.STATUS[n],
)
rows.append(row)
if not has_output:
return
def _generate_ending(self):
return self.ENDING_TMPL
##############################################################################
# Facilities for running tests from the command line
##############################################################################
# Note: Reuse unittest.TestProgram to launch test. In the future we may
# build our own launcher to support more specific command line
# parameters like test title, CSS, etc.
class TestProgram(unittest.TestProgram):
"""
A variation of the unittest.TestProgram. Please refer to the base
class for command line parameters.
"""
def runTests(self):
# Pick HTMLTestRunner as the default test runner.
# base class's testRunner parameter is not useful because it means
# we have to instantiate HTMLTestRunner before we know self.verbosity.
if self.testRunner is None:
self.testRunner = HTMLTestRunner(verbosity=self.verbosity)
unittest.TestProgram.runTests(self)
main = TestProgram
##############################################################################
# Executing this module from the command line
##############################################################################
if __name__ == "__main__":
main(module=None)
all_tests
#-*-coding:utf-8-*-
__author__ = 'pengfei913'
#coding=utf-8
import sys ,re ,os,math
import time
reload(sys)
sys.setdefaultencoding("utf-8")
import datetime
import logging
u=os.getcwd()
sys.path.append(u)
from test_case import *
import unittest
import HTMLTestRunner
import allcase_list #调用数组文件
#获取数组方法
alltestnames = allcase_list.caselist()
suite = unittest.TestSuite()
#suite.addTest(alltestnames.BaiduTest('test_case'))
if __name__ == '__main__':
for test in alltestnames:
try:
#最关键的就是这一句,循环执行数据数的里的用例。
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(test))
except :
print 'ERROR: Skipping tests from "%s".' % test
try:
__import__(test)
except ImportError:
print 'Could not import the test module.'
else:
print 'Could not load the test suite.'
from traceback import print_exc
print_exc()
print
print 'Running the tests...'
#取当前时间
# now = time.strftime("%Y-%m-%d-%H_%M_%S",time.localtime(time.time()))
now=datetime.datetime.now()
otherStyleTime=now.strftime("%Y-%m-%d %H:%M:%S")
homedir = os.getcwd()
print homedir
#把当前时间加到报告中
filename=homedir+"\\test_result\\"+"HtmlReport"+otherStyleTime.replace(":","_")+".html"#定义个报告存放路径,支持相对路径。
fp = file(filename, 'wb')
runner =HTMLTestRunner.HTMLTestRunner(
stream=fp,
title=u'平安好房自动化测试报告',
)
runner.run(suite)
allcase_list
#-*-coding:utf-8-*-
__author__ = 'pengfei913'
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
def caselist():
alltestnames = [
'test_case.Permission.Login',
'test_case.Sms.Login',
'test_case.Baidu.Login'
]
print "success read case list success!!"
return alltestnames