HtmlReport为我所用

  1 import datetime
  2 import StringIO
  3 import sys
  4 import time
  5 import unittest
  6 from xml.sax import saxutils
  7 
  8 
  9 class Template_mixin(object):
 10     """
 11     Define a HTML template for report customerization and generation.
 12 
 13     Overall structure of an HTML report
 14 
 15     HTML
 16     +------------------------+
 17     |<html>                  |
 18     |  <head>                |
 19     |                        |
 20     |   STYLESHEET           |
 21     |   +----------------+   |
 22     |   |                |   |
 23     |   +----------------+   |
 24     |                        |
 25     |  </head>               |
 26     |                        |
 27     |  <body>                |
 28     |                        |
 29     |   HEADING              |
 30     |   +----------------+   |
 31     |   |                |   |
 32     |   +----------------+   |
 33     |                        |
 34     |   REPORT               |
 35     |   +----------------+   |
 36     |   |                |   |
 37     |   +----------------+   |
 38     |                        |
 39     |   ENDING               |
 40     |   +----------------+   |
 41     |   |                |   |
 42     |   +----------------+   |
 43     |                        |
 44     |  </body>               |
 45     |</html>                 |
 46     +------------------------+
 47     """
 48 
 49     STATUS = {
 50     0: 'pass',
 51     1: 'fail',
 52     2: 'error',
 53     }
 54 
 55     DEFAULT_TITLE = 'Unit Test Report'
 56     DEFAULT_DESCRIPTION = ''
 57 
 58     # ------------------------------------------------------------------------
 59     # HTML Template
 60 
 61     HTML_TMPL = r"""<?xml version="1.0" encoding="UTF-8"?>
 62 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 63 <html xmlns="http://www.w3.org/1999/xhtml">
 64 <head>
 65     <title>%(title)s</title>
 66     <meta name="generator" content="%(generator)s"/>
 67     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 68     %(stylesheet)s
 69 </head>
 70 <body>
 71 <script language="javascript" type="text/javascript"><!--
 72 output_list = Array();
 73 
 74 /* level - 0:Summary; 1:Failed; 2:All */
 75 function showCase(level) {
 76     trs = document.getElementsByTagName("tr");
 77     for (var i = 0; i < trs.length; i++) {
 78         tr = trs[i];
 79         id = tr.id;
 80         if (id.substr(0,2) == 'ft') {
 81             if (level < 1) {
 82                 tr.className = 'hiddenRow';
 83             }
 84             else {
 85                 tr.className = '';
 86             }
 87         }
 88         if (id.substr(0,2) == 'pt') {
 89             if (level > 1) {
 90                 tr.className = '';
 91             }
 92             else {
 93                 tr.className = 'hiddenRow';
 94             }
 95         }
 96     }
 97 }
 98 
 99 
100 function showClassDetail(cid, count) {
101     var id_list = Array(count);
102     var toHide = 1;
103     for (var i = 0; i < count; i++) {
104         tid0 = 't' + cid.substr(1) + '.' + (i+1);
105         tid = 'f' + tid0;
106         tr = document.getElementById(tid);
107         if (!tr) {
108             tid = 'p' + tid0;
109             tr = document.getElementById(tid);
110         }
111         id_list[i] = tid;
112         if (tr.className) {
113             toHide = 0;
114         }
115     }
116     for (var i = 0; i < count; i++) {
117         tid = id_list[i];
118         if (toHide) {
119             document.getElementById('div_'+tid).style.display = 'none'
120             document.getElementById(tid).className = 'hiddenRow';
121         }
122         else {
123             document.getElementById(tid).className = '';
124         }
125     }
126 }
127 
128 
129 function showTestDetail(div_id){
130     var details_div = document.getElementById(div_id)
131     var displayState = details_div.style.display
132     // alert(displayState)
133     if (displayState != 'block' ) {
134         displayState = 'block'
135         details_div.style.display = 'block'
136     }
137     else {
138         details_div.style.display = 'none'
139     }
140 }
141 
142 
143 function html_escape(s) {
144     s = s.replace(/&/g,'&amp;');
145     s = s.replace(/</g,'&lt;');
146     s = s.replace(/>/g,'&gt;');
147     return s;
148 }
149 
150 /* obsoleted by detail in <div>
151 function showOutput(id, name) {
152     var w = window.open("", //url
153                     name,
154                     "resizable,scrollbars,status,width=800,height=450");
155     d = w.document;
156     d.write("<pre>");
157     d.write(html_escape(output_list[id]));
158     d.write("\n");
159     d.write("<a href='javascript:window.close()'>close</a>\n");
160     d.write("</pre>\n");
161     d.close();
162 }
163 */
164 --></script>
165 
166 %(heading)s
167 %(report)s
168 %(ending)s
169 
170 </body>
171 </html>
172 """
173     # variables: (title, generator, stylesheet, heading, report, ending)
174 
175 
176     # ------------------------------------------------------------------------
177     # Stylesheet
178     #
179     # alternatively use a <link> for external style sheet, e.g.
180     #   <link rel="stylesheet" href="$url" type="text/css">
181 
182     STYLESHEET_TMPL = """
183 <style type="text/css" media="screen">
184 body        { font-family: verdana, arial, helvetica, sans-serif; font-size: 80%; }
185 table       { font-size: 100%; }
186 pre         { }
187 
188 /* -- heading ---------------------------------------------------------------------- */
189 h1 {
190     font-size: 16pt;
191     color: gray;
192 }
193 .heading {
194     margin-top: 0ex;
195     margin-bottom: 1ex;
196 }
197 
198 .heading .attribute {
199     margin-top: 1ex;
200     margin-bottom: 0;
201 }
202 
203 .heading .description {
204     margin-top: 4ex;
205     margin-bottom: 6ex;
206 }
207 
208 /* -- css div popup ------------------------------------------------------------------------ */
209 a.popup_link {
210 }
211 
212 a.popup_link:hover {
213     color: red;
214 }
215 
216 .popup_window {
217     display: none;
218     position: relative;
219     left: 0px;
220     top: 0px;
221     /*border: solid #627173 1px; */
222     padding: 10px;
223     background-color: #E6E6D6;
224     font-family: "Lucida Console", "Courier New", Courier, monospace;
225     text-align: left;
226     font-size: 8pt;
227     width: 500px;
228 }
229 
230 }
231 /* -- report ------------------------------------------------------------------------ */
232 #show_detail_line {
233     margin-top: 3ex;
234     margin-bottom: 1ex;
235 }
236 #result_table {
237     width: 80%;
238     border-collapse: collapse;
239     border: 1px solid #777;
240 }
241 #header_row {
242     font-weight: bold;
243     color: white;
244     background-color: #777;
245 }
246 #result_table td {
247     border: 1px solid #777;
248     padding: 2px;
249 }
250 #total_row  { font-weight: bold; }
251 .passClass  { background-color: #6c6; }
252 .failClass  { background-color: #c60; }
253 .errorClass { background-color: #c00; }
254 .passCase   { color: #6c6; }
255 .failCase   { color: #c60; font-weight: bold; }
256 .errorCase  { color: #c00; font-weight: bold; }
257 .hiddenRow  { display: none; }
258 .testcase   { margin-left: 2em; }
259 
260 
261 /* -- ending ---------------------------------------------------------------------- */
262 #ending {
263 }
264 
265 </style>
266 """
267 
268 
269 
270     # ------------------------------------------------------------------------
271     # Heading
272     #
273 
274     HEADING_TMPL = """<div class='heading'>
275 <h1>%(title)s</h1>
276 %(parameters)s
277 <p class='description'>%(description)s</p>
278 </div>
279 
280 """ # variables: (title, parameters, description)
281 
282     HEADING_ATTRIBUTE_TMPL = """<p class='attribute'><strong>%(name)s:</strong> %(value)s</p>
283 """ # variables: (name, value)
284 
285 
286 
287     # ------------------------------------------------------------------------
288     # Report
289     #
290 
291     REPORT_TMPL = """
292 <p id='show_detail_line'>Show
293 <a href='javascript:showCase(0)'>Summary</a>
294 <a href='javascript:showCase(1)'>Failed</a>
295 <a href='javascript:showCase(2)'>All</a>
296 </p>
297 <table id='result_table'>
298 <colgroup>
299 <col align='left' />
300 <col align='right' />
301 <col align='right' />
302 <col align='right' />
303 <col align='right' />
304 <col align='right' />
305 </colgroup>
306 <tr id='header_row'>
307     <td>Test Group/Test case</td>
308     <td>Count</td>
309     <td>Pass</td>
310     <td>Fail</td>
311     <td>Error</td>
312     <td>View</td>
313 </tr>
314 %(test_list)s
315 <tr id='total_row'>
316     <td>Total</td>
317     <td>%(count)s</td>
318     <td>%(Pass)s</td>
319     <td>%(fail)s</td>
320     <td>%(error)s</td>
321     <td>&nbsp;</td>
322 </tr>
323 </table>
324 """ # variables: (test_list, count, Pass, fail, error)
325 
326     REPORT_CLASS_TMPL = r"""
327 <tr class='%(style)s'>
328     <td>%(desc)s</td>
329     <td>%(count)s</td>
330     <td>%(Pass)s</td>
331     <td>%(fail)s</td>
332     <td>%(error)s</td>
333     <td><a href="javascript:showClassDetail('%(cid)s',%(count)s)">Detail</a></td>
334 </tr>
335 """ # variables: (style, desc, count, Pass, fail, error, cid)
336 
337 
338     REPORT_TEST_WITH_OUTPUT_TMPL = r"""
339 <tr id='%(tid)s' class='%(Class)s'>
340     <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
341     <td colspan='5' align='center'>
342 
343     <!--css div popup start-->
344     <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_%(tid)s')" >
345         %(status)s</a>
346 
347     <div id='div_%(tid)s' class="popup_window">
348         <div style='text-align: right; color:red;cursor:pointer'>
349         <a onfocus='this.blur();' onclick="document.getElementById('div_%(tid)s').style.display = 'none' " >
350            [x]</a>
351         </div>
352         <pre>
353         test
354         %(script)s
355         </pre>
356     </div>
357     <!--css div popup end-->
358 
359     </td>
360 </tr>
361 """ # variables: (tid, Class, style, desc, status)
362 
363 
364     REPORT_TEST_NO_OUTPUT_TMPL = r"""
365 <tr id='%(tid)s' class='%(Class)s'>
366     <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
367     <td colspan='5' align='center'>%(status)s</td>
368 </tr>
369 """ # variables: (tid, Class, style, desc, status)
370 
371 
372     REPORT_TEST_OUTPUT_TMPL = r"""
373 %(id)s: %(output)s
374 """ # variables: (id, output)
375 
376 
377 
378     # ------------------------------------------------------------------------
379     # ENDING
380     #
381 
382     ENDING_TMPL = """<div id='ending'>&nbsp;</div>"""
383 
384 
385 class HTMLTestRunner(Template_mixin):
386     """
387     """
388     def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None):
389         self.stream = stream
390         self.verbosity = verbosity
391         if title is None:
392             self.title = self.DEFAULT_TITLE
393         else:
394             self.title = title
395         if description is None:
396             self.description = self.DEFAULT_DESCRIPTION
397         else:
398             self.description = description
399 
400         self.startTime = datetime.datetime.now()
401 
402     
403     #def run(self, test):
404     #    "Run the given test case or test suite."
405     #    result = _TestResult(self.verbosity)
406     #    test(result)
407     #    self.stopTime = datetime.datetime.now()
408     #    self.generateReport(test, result)
409     #    print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)
410     #    return result
411 
412     def sortResult(self, result_list):
413         # unittest does not seems to run in any particular order.
414         # Here at least we want to group them together by class.
415         rmap = {}
416         classes = []
417         for n,t,o,e in result_list:
418             cls = t.__class__
419             if not rmap.has_key(cls):
420                 rmap[cls] = []
421                 classes.append(cls)
422             rmap[cls].append((n,t,o,e))
423         r = [(cls, rmap[cls]) for cls in classes]
424         return r
425 
426 
427     def getReportAttributes(self, result):
428         """
429         Return report attributes as a list of (name, value).
430         Override this to add custom attributes.
431         """
432         startTime = str(self.startTime)[:19]
433         self.stopTime = self.startTime + datetime.timedelta(days =1)
434         self.startTime = datetime.datetime.now()
435         duration = str(self.stopTime - self.startTime)
436         status = []
437         if result.success_count: status.append('Pass %s'    % result.success_count)
438         if result.failure_count: status.append('Failure %s' % result.failure_count)
439         if result.error_count:   status.append('Error %s'   % result.error_count  )
440         if status:
441             status = ' '.join(status)
442         else:
443             status = 'none'
444         return [
445             ('Start Time', startTime),
446             ('Duration', duration),
447             ('Status', status),
448         ]
449 
450 
451     def generateReport(self, result):
452         report_attrs = self.getReportAttributes(result)
453         generator = 'HTMLTestRunner %s' % '111111'
454         stylesheet = self._generate_stylesheet()
455         heading = self._generate_heading(report_attrs)
456         report = self._generate_report(result)
457         ending = self._generate_ending()
458         output = self.HTML_TMPL % dict(
459             title = saxutils.escape(self.title),
460             generator = generator,
461             stylesheet = stylesheet,
462             heading = heading,
463             report = report,
464             ending = ending,
465         )
466         self.stream.write(output.encode('utf8'))
467 
468 
469     def _generate_stylesheet(self):
470         return self.STYLESHEET_TMPL
471 
472 
473     def _generate_heading(self, report_attrs):
474         a_lines = []
475         for name, value in report_attrs:
476             line = self.HEADING_ATTRIBUTE_TMPL % dict(
477                     name = saxutils.escape(name),
478                     value = saxutils.escape(value),
479                 )
480             a_lines.append(line)
481         heading = self.HEADING_TMPL % dict(
482             title = saxutils.escape(self.title),
483             parameters = ''.join(a_lines),
484             description = saxutils.escape(self.description),
485         )
486         return heading
487 
488 
489     def _generate_report(self, result):
490         rows = []
491         #sortedResult = self.sortResult(result.result)
492         #for cid, (cls, cls_results) in enumerate(sortedResult):
493         for cid, cls_results in enumerate(result.result):
494             # subtotal for a class
495             np = nf = ne = 0
496             for cls_item in cls_results:
497                 if cls_item['status'] == 'passed':
498                     np += 1
499                 elif cls_item['status'] == 'failed':
500                     nf += 1
501                 else:
502                     ne += 1
503             '''
504             for n,t,o,e in cls_results:
505                 if n == 0: np += 1
506                 elif n == 1: nf += 1
507                 else: ne += 1
508             '''
509             
510             # format class description
511             '''
512             if cls.__module__ == "__main__":
513                 name = cls.__name__
514             else:
515                 name = "%s.%s" % (cls.__module__, cls.__name__)
516             doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
517             desc = doc and '%s: %s' % (name, doc) or name
518             '''
519             desc = 'testttttttttttttttttttt'
520             row = self.REPORT_CLASS_TMPL % dict(
521                 style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',
522                 desc = desc,
523                 count = np+nf+ne,
524                 Pass = np,
525                 fail = nf,
526                 error = ne,
527                 cid = 'c%s' % (cid+1),
528             )
529             print 'style',ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass'
530             rows.append(row)
531 
532             for tid, cls_items in enumerate(cls_results):
533                 
534                 self._generate_report_test(rows, cid, tid, cls_items['status'], 
535                                            cls_items['name'], 'aaaaaaaaa', 
536                                            'bbbbbbb\n\n\n\n\n\n\n\n\ccccccccccc')
537             '''
538             for tid, (n,t,o,e) in enumerate(cls_results):
539                 self._generate_report_test(rows, cid, tid, n, t, o, e)
540             '''
541         report = self.REPORT_TMPL % dict(
542             test_list = ''.join(rows),
543             count = str(result.success_count+result.failure_count+result.error_count),
544             Pass = str(result.success_count),
545             fail = str(result.failure_count),
546             error = str(result.error_count),
547         )
548         return report
549 
550 
551     def _generate_report_test(self, rows, cid, tid, n, t, o, e):
552         # e.g. 'pt1.1', 'ft1.1', etc
553         has_output = bool(o or e)
554         tid = (n == 'passed' and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1)
555         '''
556         name = t.id().split('.')[-1]
557         doc = t.shortDescription() or ""
558         desc = doc and ('%s: %s' % (name, doc)) or name
559         '''
560         desc = t
561         tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL
562 
563         # o and e should be byte string because they are collected from stdout and stderr?
564         if isinstance(o,str):
565             # TODO: some problem with 'string_escape': it escape \n and mess up formating
566             # uo = unicode(o.encode('string_escape'))
567             uo = o.decode('latin-1')
568         else:
569             uo = o
570         if isinstance(e,str):
571             # TODO: some problem with 'string_escape': it escape \n and mess up formating
572             # ue = unicode(e.encode('string_escape'))
573             ue = e.decode('latin-1')
574         else:
575             ue = e
576 
577         script = self.REPORT_TEST_OUTPUT_TMPL % dict(
578             id = tid,
579             output = saxutils.escape(uo+ue),
580         )
581         #print script,'aaaaaaaaaa'
582         row = tmpl % dict(
583             tid = tid,
584             Class = (n == 0 and 'hiddenRow' or 'none'),
585             style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'),
586             desc = desc,
587             script = script,
588             #status = self.STATUS[n],
589             status = n
590         )
591         rows.append(row)
592         if not has_output:
593             return
594 
595     def _generate_ending(self):
596         return self.ENDING_TMPL
597 
598 class GetResult():
599     
600     def __init__(self):
601         self.success_count = 10
602         self.failure_count = 5
603         self.error_count = 3
604         self.result = [[{'status':'failed','name':'tc11',},
605                        {'status':'passed','name':'tc12',},
606                        {'status':'error','name':'tc13',}],
607                        
608                        [{'status':'failed','name':'tc21',},
609                        {'status':'passed','name':'tc22',},
610                        {'status':'error','name':'tc23',}],
611                        ]
612 
613 if __name__ == '__main__':
614     
615     filename="./xxx111.html" 
616     fp=file(filename,'wb')
617     result = GetResult()
618     print enumerate(result.result)
619 
620     for a,b in enumerate(result.result):
621         print a
622         print b
623         for w,c in enumerate(b):
624             print w
625             print c
626 
627         #print c
628     report = HTMLTestRunner(stream=fp,title='Report_title',description='Report_description')
629     report.generateReport(result)
630     pass

 

posted on 2014-08-15 14:43  Newbie wang  阅读(1208)  评论(0)    收藏  举报