1#!/usr/bin/env python
2
3import sys, re, os.path, cgi, stat, math
4from optparse import OptionParser
5from color import getColorizer
6
7class tblCell(object):
8    def __init__(self, text, value = None, props = None):
9        self.text = text
10        self.value = value
11        self.props = props
12
13class tblColumn(object):
14    def __init__(self, caption, title = None, props = None):
15        self.text = caption
16        self.title = title
17        self.props = props
18
19class tblRow(object):
20    def __init__(self, colsNum, props = None):
21        self.cells = [None] * colsNum
22        self.props = props
23
24def htmlEncode(str):
25    return '<br/>'.join([cgi.escape(s) for s in str])
26
27class table(object):
28    def_align = "left"
29    def_valign = "middle"
30    def_color = None
31    def_colspan = 1
32    def_rowspan = 1
33    def_bold = False
34    def_italic = False
35    def_text="-"
36
37    def __init__(self, caption = None):
38        self.columns = {}
39        self.rows = []
40        self.ridx = -1;
41        self.caption = caption
42        pass
43
44    def newRow(self, **properties):
45        if len(self.rows) - 1 == self.ridx:
46            self.rows.append(tblRow(len(self.columns), properties))
47        else:
48            self.rows[ridx + 1].props = properties
49        self.ridx += 1
50        return self.rows[self.ridx]
51
52    def trimLastRow(self):
53        if self.rows:
54            self.rows.pop()
55        if self.ridx >= len(self.rows):
56            self.ridx = len(self.rows) - 1
57
58    def newColumn(self, name, caption, title = None, **properties):
59        if name in self.columns:
60            index = self.columns[name].index
61        else:
62            index = len(self.columns)
63        if isinstance(caption, tblColumn):
64            caption.index = index
65            self.columns[name] = caption
66            return caption
67        else:
68            col = tblColumn(caption, title, properties)
69            col.index = index
70            self.columns[name] = col
71            return col
72
73    def getColumn(self, name):
74        if isinstance(name, str):
75            return self.columns.get(name, None)
76        else:
77            vals = [v for v in self.columns.values() if v.index == name]
78            if vals:
79                return vals[0]
80        return None
81
82    def newCell(self, col_name, text, value = None, **properties):
83        if self.ridx < 0:
84            self.newRow()
85        col = self.getColumn(col_name)
86        row = self.rows[self.ridx]
87        if not col:
88            return None
89        if isinstance(text, tblCell):
90            cl = text
91        else:
92            cl = tblCell(text, value, properties)
93        row.cells[col.index] = cl
94        return cl
95
96    def layoutTable(self):
97        columns = self.columns.values()
98        columns.sort(key=lambda c: c.index)
99
100        colspanned = []
101        rowspanned = []
102
103        self.headerHeight = 1
104        rowsToAppend = 0
105
106        for col in columns:
107            self.measureCell(col)
108            if col.height > self.headerHeight:
109                self.headerHeight = col.height
110            col.minwidth = col.width
111            col.line = None
112
113        for r in range(len(self.rows)):
114            row = self.rows[r]
115            row.minheight = 1
116            for i in range(len(row.cells)):
117                cell = row.cells[i]
118                if row.cells[i] is None:
119                    continue
120                cell.line = None
121                self.measureCell(cell)
122                colspan = int(self.getValue("colspan", cell))
123                rowspan = int(self.getValue("rowspan", cell))
124                if colspan > 1:
125                    colspanned.append((r,i))
126                    if i + colspan > len(columns):
127                        colspan = len(columns) - i
128                    cell.colspan = colspan
129                    #clear spanned cells
130                    for j in range(i+1, min(len(row.cells), i + colspan)):
131                        row.cells[j] = None
132                elif columns[i].minwidth < cell.width:
133                    columns[i].minwidth = cell.width
134                if rowspan > 1:
135                    rowspanned.append((r,i))
136                    rowsToAppend2 = r + colspan - len(self.rows)
137                    if rowsToAppend2 > rowsToAppend:
138                        rowsToAppend = rowsToAppend2
139                    cell.rowspan = rowspan
140                    #clear spanned cells
141                    for j in range(r+1, min(len(self.rows), r + rowspan)):
142                        if len(self.rows[j].cells) > i:
143                            self.rows[j].cells[i] = None
144                elif row.minheight < cell.height:
145                    row.minheight = cell.height
146
147        self.ridx = len(self.rows) - 1
148        for r in range(rowsToAppend):
149            self.newRow()
150            self.rows[len(self.rows) - 1].minheight = 1
151
152        while colspanned:
153            colspanned_new = []
154            for r, c in colspanned:
155                cell = self.rows[r].cells[c]
156                sum([col.minwidth for col in columns[c:c + cell.colspan]])
157                cell.awailable = sum([col.minwidth for col in columns[c:c + cell.colspan]]) + cell.colspan - 1
158                if cell.awailable < cell.width:
159                    colspanned_new.append((r,c))
160            colspanned = colspanned_new
161            if colspanned:
162                r,c = colspanned[0]
163                cell = self.rows[r].cells[c]
164                cols = columns[c:c + cell.colspan]
165                total = cell.awailable - cell.colspan + 1
166                budget = cell.width - cell.awailable
167                spent = 0
168                s = 0
169                for col in cols:
170                    s += col.minwidth
171                    addition = s * budget / total - spent
172                    spent += addition
173                    col.minwidth += addition
174
175        while rowspanned:
176            rowspanned_new = []
177            for r, c in rowspanned:
178                cell = self.rows[r].cells[c]
179                cell.awailable = sum([row.minheight for row in self.rows[r:r + cell.rowspan]])
180                if cell.awailable < cell.height:
181                    rowspanned_new.append((r,c))
182            rowspanned = rowspanned_new
183            if rowspanned:
184                r,c = rowspanned[0]
185                cell = self.rows[r].cells[c]
186                rows = self.rows[r:r + cell.rowspan]
187                total = cell.awailable
188                budget = cell.height - cell.awailable
189                spent = 0
190                s = 0
191                for row in rows:
192                    s += row.minheight
193                    addition = s * budget / total - spent
194                    spent += addition
195                    row.minheight += addition
196
197        return columns
198
199    def measureCell(self, cell):
200        text = self.getValue("text", cell)
201        cell.text = self.reformatTextValue(text)
202        cell.height = len(cell.text)
203        cell.width = len(max(cell.text, key = lambda line: len(line)))
204
205    def reformatTextValue(self, value):
206        if isinstance(value, str):
207            vstr = value
208        elif isinstance(value, unicode):
209            vstr = str(value)
210        else:
211            try:
212                vstr = '\n'.join([str(v) for v in value])
213            except TypeError:
214                vstr = str(value)
215        return vstr.splitlines()
216
217    def adjustColWidth(self, cols, width):
218        total = sum([c.minWidth for c in cols])
219        if total + len(cols) - 1 >= width:
220            return
221        budget = width - len(cols) + 1 - total
222        spent = 0
223        s = 0
224        for col in cols:
225            s += col.minWidth
226            addition = s * budget / total - spent
227            spent += addition
228            col.minWidth += addition
229
230    def getValue(self, name, *elements):
231        for el in elements:
232            try:
233                return getattr(el, name)
234            except AttributeError:
235                pass
236            try:
237                val = el.props[name]
238                if val:
239                    return val
240            except AttributeError:
241                pass
242            except KeyError:
243                pass
244        try:
245            return getattr(self.__class__, "def_" + name)
246        except AttributeError:
247            return None
248
249    def consolePrintTable(self, out):
250        columns = self.layoutTable()
251        colrizer = getColorizer(out)
252
253        if self.caption:
254            out.write("%s%s%s" % ( os.linesep,  os.linesep.join(self.reformatTextValue(self.caption)), os.linesep * 2))
255
256        headerRow = tblRow(len(columns), {"align": "center", "valign": "top", "bold": True, "header": True})
257        headerRow.cells = columns
258        headerRow.minheight = self.headerHeight
259
260        self.consolePrintRow2(colrizer, headerRow, columns)
261
262        for i in range(0, len(self.rows)):
263            self.consolePrintRow2(colrizer, i, columns)
264
265    def consolePrintRow2(self, out, r, columns):
266        if isinstance(r, tblRow):
267            row = r
268            r = -1
269        else:
270            row = self.rows[r]
271
272        #evaluate initial values for line numbers
273        i = 0
274        while i < len(row.cells):
275            cell = row.cells[i]
276            colspan = self.getValue("colspan", cell)
277            if cell is not None:
278                cell.wspace = sum([col.minwidth for col in columns[i:i + colspan]]) + colspan - 1
279                if cell.line is None:
280                    if r < 0:
281                        rows = [row]
282                    else:
283                        rows = self.rows[r:r + self.getValue("rowspan", cell)]
284                    cell.line = self.evalLine(cell, rows, columns[i])
285                    if len(rows) > 1:
286                        for rw in rows:
287                            rw.cells[i] = cell
288            i += colspan
289
290        #print content
291        for ln in range(row.minheight):
292            i = 0
293            while i < len(row.cells):
294                if i > 0:
295                    out.write(" ")
296                cell = row.cells[i]
297                column = columns[i]
298                if cell is None:
299                    out.write(" " * column.minwidth)
300                    i += 1
301                else:
302                    self.consolePrintLine(cell, row, column, out)
303                    i += self.getValue("colspan", cell)
304            out.write(os.linesep)
305
306    def consolePrintLine(self, cell, row, column, out):
307        if cell.line < 0 or cell.line >= cell.height:
308            line = ""
309        else:
310            line = cell.text[cell.line]
311        width = cell.wspace
312        align = self.getValue("align", ((None, cell)[isinstance(cell, tblCell)]), row, column)
313
314        if align == "right":
315            pattern = "%" + str(width) + "s"
316        elif align == "center":
317            pattern = "%" + str((width - len(line)) / 2 + len(line)) + "s" + " " * (width - len(line) - (width - len(line)) / 2)
318        else:
319            pattern = "%-" + str(width) + "s"
320
321        out.write(pattern % line, color = self.getValue("color", cell, row, column))
322        cell.line += 1
323
324    def evalLine(self, cell, rows, column):
325        height = cell.height
326        valign = self.getValue("valign", cell, rows[0], column)
327        space = sum([row.minheight for row in rows])
328        if valign == "bottom":
329            return height - space
330        if valign == "middle":
331            return (height - space + 1) / 2
332        return 0
333
334    def htmlPrintTable(self, out, embeedcss = False):
335        columns = self.layoutTable()
336
337        if embeedcss:
338            out.write("<div style=\"font-family: Lucida Console, Courier New, Courier;font-size: 16px;color:#3e4758;\">\n<table style=\"background:none repeat scroll 0 0 #FFFFFF;border-collapse:collapse;font-family:'Lucida Sans Unicode','Lucida Grande',Sans-Serif;font-size:14px;margin:20px;text-align:left;width:480px;margin-left: auto;margin-right: auto;white-space:nowrap;\">\n")
339        else:
340            out.write("<div class=\"tableFormatter\">\n<table class=\"tbl\">\n")
341        if self.caption:
342            if embeedcss:
343                out.write(" <caption style=\"font:italic 16px 'Trebuchet MS',Verdana,Arial,Helvetica,sans-serif;padding:0 0 5px;text-align:right;white-space:normal;\">%s</caption>\n" % htmlEncode(self.reformatTextValue(self.caption)))
344            else:
345                out.write(" <caption>%s</caption>\n" % htmlEncode(self.reformatTextValue(self.caption)))
346        out.write(" <thead>\n")
347
348        headerRow = tblRow(len(columns), {"align": "center", "valign": "top", "bold": True, "header": True})
349        headerRow.cells = columns
350
351        header_rows = [headerRow]
352        header_rows.extend([row for row in self.rows if self.getValue("header")])
353        last_row = header_rows[len(header_rows) - 1]
354
355        for row in header_rows:
356            out.write("  <tr>\n")
357            for th in row.cells:
358                align = self.getValue("align", ((None, th)[isinstance(th, tblCell)]), row, row)
359                valign = self.getValue("valign", th, row)
360                cssclass = self.getValue("cssclass", th)
361                attr = ""
362                if align:
363                    attr += " align=\"%s\"" % align
364                if valign:
365                    attr += " valign=\"%s\"" % valign
366                if cssclass:
367                    attr += " class=\"%s\"" % cssclass
368                css = ""
369                if embeedcss:
370                    css = " style=\"border:none;color:#003399;font-size:16px;font-weight:normal;white-space:nowrap;padding:3px 10px;\""
371                    if row == last_row:
372                        css = css[:-1] + "padding-bottom:5px;\""
373                out.write("   <th%s%s>\n" % (attr, css))
374                if th is not None:
375                    out.write("    %s\n" % htmlEncode(th.text))
376                out.write("   </th>\n")
377            out.write("  </tr>\n")
378
379        out.write(" </thead>\n <tbody>\n")
380
381        rows = [row for row in self.rows if not self.getValue("header")]
382        for r in range(len(rows)):
383            row = rows[r]
384            rowattr = ""
385            cssclass = self.getValue("cssclass", row)
386            if cssclass:
387                rowattr += " class=\"%s\"" % cssclass
388            out.write("  <tr%s>\n" % (rowattr))
389            i = 0
390            while i < len(row.cells):
391                column = columns[i]
392                td = row.cells[i]
393                if isinstance(td, int):
394                    i += td
395                    continue
396                colspan = self.getValue("colspan", td)
397                rowspan = self.getValue("rowspan", td)
398                align = self.getValue("align", td, row, column)
399                valign = self.getValue("valign", td, row, column)
400                color = self.getValue("color", td, row, column)
401                bold = self.getValue("bold", td, row, column)
402                italic = self.getValue("italic", td, row, column)
403                style = ""
404                attr = ""
405                if color:
406                    style += "color:%s;" % color
407                if bold:
408                    style += "font-weight: bold;"
409                if italic:
410                    style += "font-style: italic;"
411                if align and align != "left":
412                    attr += " align=\"%s\"" % align
413                if valign and valign != "middle":
414                    attr += " valign=\"%s\"" % valign
415                if colspan > 1:
416                    attr += " colspan=\"%s\"" % colspan
417                if rowspan > 1:
418                    attr += " rowspan=\"%s\"" % rowspan
419                    for q in range(r+1, min(r+rowspan, len(rows))):
420                        rows[q].cells[i] = colspan
421                if style:
422                    attr += " style=\"%s\"" % style
423                css = ""
424                if embeedcss:
425                    css = " style=\"border:none;border-bottom:1px solid #CCCCCC;color:#666699;padding:6px 8px;white-space:nowrap;\""
426                    if r == 0:
427                        css = css[:-1] + "border-top:2px solid #6678B1;\""
428                out.write("   <td%s%s>\n" % (attr, css))
429                if td is not None:
430                    out.write("    %s\n" % htmlEncode(td.text))
431                out.write("   </td>\n")
432                i += colspan
433            out.write("  </tr>\n")
434
435        out.write(" </tbody>\n</table>\n</div>\n")
436
437def htmlPrintHeader(out, title = None):
438    if title:
439        titletag = "<title>%s</title>\n" % htmlEncode([str(title)])
440    else:
441        titletag = ""
442    out.write("""<!DOCTYPE HTML>
443<html>
444<head>
445<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
446%s<style type="text/css">
447html, body {font-family: Lucida Console, Courier New, Courier;font-size: 16px;color:#3e4758;}
448.tbl{background:none repeat scroll 0 0 #FFFFFF;border-collapse:collapse;font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;font-size:14px;margin:20px;text-align:left;width:480px;margin-left: auto;margin-right: auto;white-space:nowrap;}
449.tbl span{display:block;white-space:nowrap;}
450.tbl thead tr:last-child th {padding-bottom:5px;}
451.tbl tbody tr:first-child td {border-top:3px solid #6678B1;}
452.tbl th{border:none;color:#003399;font-size:16px;font-weight:normal;white-space:nowrap;padding:3px 10px;}
453.tbl td{border:none;border-bottom:1px solid #CCCCCC;color:#666699;padding:6px 8px;white-space:nowrap;}
454.tbl tbody tr:hover td{color:#000099;}
455.tbl caption{font:italic 16px "Trebuchet MS",Verdana,Arial,Helvetica,sans-serif;padding:0 0 5px;text-align:right;white-space:normal;}
456.firstingroup {border-top:2px solid #6678B1;}
457</style>
458<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
459<script type="text/javascript">
460function abs(val) { return val < 0 ? -val : val }
461$(function(){
462  //generate filter rows
463  $("div.tableFormatter table.tbl").each(function(tblIdx, tbl) {
464    var head = $("thead", tbl)
465    var filters = $("<tr></tr>")
466    var hasAny = false
467    $("tr:first th", head).each(function(colIdx, col) {
468      col = $(col)
469      var cell
470      var id = "t" + tblIdx + "r" + colIdx
471      if (col.hasClass("col_name")){
472        cell = $("<th><input id='" + id + "' name='" + id + "' type='text' style='width:100%%' class='filter_col_name' title='Regular expression for name filtering (&quot;resize.*640x480&quot; - resize tests on VGA resolution)'></input></th>")
473        hasAny = true
474      }
475      else if (col.hasClass("col_rel")){
476        cell = $("<th><input id='" + id + "' name='" + id + "' type='text' style='width:100%%' class='filter_col_rel' title='Filter out lines with a x-factor of acceleration less than Nx'></input></th>")
477        hasAny = true
478      }
479      else if (col.hasClass("col_cr")){
480        cell = $("<th><input id='" + id + "' name='" + id + "' type='text' style='width:100%%' class='filter_col_cr' title='Filter out lines with a percentage of acceleration less than N%%'></input></th>")
481        hasAny = true
482      }
483      else
484        cell = $("<th></th>")
485      cell.appendTo(filters)
486    })
487
488   if (hasAny){
489     $(tbl).wrap("<form id='form_t" + tblIdx + "' method='get' action=''></form>")
490     $("<input it='test' type='submit' value='Apply Filters' style='margin-left:10px;'></input>")
491       .appendTo($("th:last", filters.appendTo(head)))
492   }
493  })
494
495  //get filter values
496  var vars = []
497  var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&')
498  for(var i = 0; i < hashes.length; ++i)
499  {
500     hash = hashes[i].split('=')
501     vars.push(decodeURIComponent(hash[0]))
502     vars[decodeURIComponent(hash[0])] = decodeURIComponent(hash[1]);
503  }
504
505  //set filter values
506  for(var i = 0; i < vars.length; ++i)
507     $("#" + vars[i]).val(vars[vars[i]])
508
509  //apply filters
510  $("div.tableFormatter table.tbl").each(function(tblIdx, tbl) {
511      filters = $("input:text", tbl)
512      var predicate = function(row) {return true;}
513      var empty = true
514      $.each($("input:text", tbl), function(i, flt) {
515         flt = $(flt)
516         var val = flt.val()
517         var pred = predicate;
518         if(val) {
519           empty = false
520           var colIdx = parseInt(flt.attr("id").slice(flt.attr("id").indexOf('r') + 1))
521           if(flt.hasClass("filter_col_name")) {
522              var re = new RegExp(val);
523              predicate = function(row) {
524                if (re.exec($(row.get(colIdx)).text()) == null)
525                  return false
526                return pred(row)
527          }
528           } else if(flt.hasClass("filter_col_rel")) {
529              var percent = parseFloat(val)
530              if (percent < 0) {
531                predicate = function(row) {
532                  var val = parseFloat($(row.get(colIdx)).text())
533                  if (!val || val >= 1 || val > 1+percent)
534                    return false
535                  return pred(row)
536            }
537              } else {
538                predicate = function(row) {
539                  var val = parseFloat($(row.get(colIdx)).text())
540                  if (!val || val < percent)
541                    return false
542                  return pred(row)
543            }
544              }
545           } else if(flt.hasClass("filter_col_cr")) {
546              var percent = parseFloat(val)
547              predicate = function(row) {
548                var val = parseFloat($(row.get(colIdx)).text())
549                if (!val || val < percent)
550                  return false
551                return pred(row)
552          }
553           }
554         }
555      });
556      if (!empty){
557         $("tbody tr", tbl).each(function (i, tbl_row) {
558            if(!predicate($("td", tbl_row)))
559               $(tbl_row).remove()
560         })
561         if($("tbody tr", tbl).length == 0) {
562           $("<tr><td colspan='"+$("thead tr:first th", tbl).length+"'>No results mathing your search criteria</td></tr>")
563             .appendTo($("tbody", tbl))
564         }
565      }
566  })
567})
568</script>
569</head>
570<body>
571""" % titletag)
572
573def htmlPrintFooter(out):
574    out.write("</body>\n</html>")
575
576def getStdoutFilename():
577    try:
578        if os.name == "nt":
579            import msvcrt, ctypes
580            handle = msvcrt.get_osfhandle(sys.stdout.fileno())
581            size = ctypes.c_ulong(1024)
582            nameBuffer = ctypes.create_string_buffer(size.value)
583            ctypes.windll.kernel32.GetFinalPathNameByHandleA(handle, nameBuffer, size, 4)
584            return nameBuffer.value
585        else:
586            return os.readlink('/proc/self/fd/1')
587    except:
588        return ""
589
590def detectHtmlOutputType(requestedType):
591    if requestedType == "txt":
592        return False
593    elif requestedType in ["html", "moinwiki"]:
594        return True
595    else:
596        if sys.stdout.isatty():
597            return False
598        else:
599            outname = getStdoutFilename()
600            if outname:
601                if outname.endswith(".htm") or outname.endswith(".html"):
602                    return True
603                else:
604                    return False
605            else:
606                return False
607
608def getRelativeVal(test, test0, metric):
609    if not test or not test0:
610        return None
611    val0 = test0.get(metric, "s")
612    if not val0:
613        return None
614    val =  test.get(metric, "s")
615    if not val or val == 0:
616        return None
617    return float(val0)/val
618
619def getCycleReduction(test, test0, metric):
620    if not test or not test0:
621        return None
622    val0 = test0.get(metric, "s")
623    if not val0 or val0 == 0:
624        return None
625    val =  test.get(metric, "s")
626    if not val:
627        return None
628    return (1.0-float(val)/val0)*100
629
630def getScore(test, test0, metric):
631    if not test or not test0:
632        return None
633    m0 = float(test.get("gmean", None))
634    m1 = float(test0.get("gmean", None))
635    if m0 == 0 or m1 == 0:
636        return None
637    s0 = float(test.get("gstddev", None))
638    s1 = float(test0.get("gstddev", None))
639    s = math.sqrt(s0*s0 + s1*s1)
640    m0 = math.log(m0)
641    m1 = math.log(m1)
642    if s == 0:
643        return None
644    return (m0-m1)/s
645
646metrix_table = \
647{
648    "name": ("Name of Test", lambda test,test0,units: str(test)),
649
650    "samples": ("Number of\ncollected samples", lambda test,test0,units: test.get("samples", units)),
651    "outliers": ("Number of\noutliers", lambda test,test0,units: test.get("outliers", units)),
652
653    "gmean": ("Geometric mean", lambda test,test0,units: test.get("gmean", units)),
654    "mean": ("Mean", lambda test,test0,units: test.get("mean", units)),
655    "min": ("Min", lambda test,test0,units: test.get("min", units)),
656    "median": ("Median", lambda test,test0,units: test.get("median", units)),
657    "stddev": ("Standard deviation", lambda test,test0,units: test.get("stddev", units)),
658    "gstddev": ("Standard deviation of Ln(time)", lambda test,test0,units: test.get("gstddev")),
659
660    "gmean%": ("Geometric mean (relative)", lambda test,test0,units: getRelativeVal(test, test0, "gmean")),
661    "mean%": ("Mean (relative)", lambda test,test0,units: getRelativeVal(test, test0, "mean")),
662    "min%": ("Min (relative)", lambda test,test0,units: getRelativeVal(test, test0, "min")),
663    "median%": ("Median (relative)", lambda test,test0,units: getRelativeVal(test, test0, "median")),
664    "stddev%": ("Standard deviation (relative)", lambda test,test0,units: getRelativeVal(test, test0, "stddev")),
665    "gstddev%": ("Standard deviation of Ln(time) (relative)", lambda test,test0,units: getRelativeVal(test, test0, "gstddev")),
666
667    "gmean$": ("Geometric mean (cycle reduction)", lambda test,test0,units: getCycleReduction(test, test0, "gmean")),
668    "mean$": ("Mean (cycle reduction)", lambda test,test0,units: getCycleReduction(test, test0, "mean")),
669    "min$": ("Min (cycle reduction)", lambda test,test0,units: getCycleReduction(test, test0, "min")),
670    "median$": ("Median (cycle reduction)", lambda test,test0,units: getCycleReduction(test, test0, "median")),
671    "stddev$": ("Standard deviation (cycle reduction)", lambda test,test0,units: getCycleReduction(test, test0, "stddev")),
672    "gstddev$": ("Standard deviation of Ln(time) (cycle reduction)", lambda test,test0,units: getCycleReduction(test, test0, "gstddev")),
673
674    "score": ("SCORE", lambda test,test0,units: getScore(test, test0, "gstddev")),
675}
676
677def formatValue(val, metric, units = None):
678    if val is None:
679        return "-"
680    if metric.endswith("%"):
681        return "%.2f" % val
682    if metric.endswith("$"):
683        return "%.2f%%" % val
684    if metric.endswith("S"):
685        if val > 3.5:
686            return "SLOWER"
687        if val < -3.5:
688            return "FASTER"
689        if val > -1.5 and val < 1.5:
690            return " "
691        if val < 0:
692            return "faster"
693        if val > 0:
694            return "slower"
695        #return "%.4f" % val
696    return "%.3f %s" % (val, units)
697
698if __name__ == "__main__":
699    if len(sys.argv) < 2:
700        print "Usage:\n", os.path.basename(sys.argv[0]), "<log_name>.xml"
701        exit(0)
702
703    parser = OptionParser()
704    parser.add_option("-o", "--output", dest="format", help="output results in text format (can be 'txt', 'html' or 'auto' - default)", metavar="FMT", default="auto")
705    parser.add_option("-m", "--metric", dest="metric", help="output metric", metavar="NAME", default="gmean")
706    parser.add_option("-u", "--units", dest="units", help="units for output values (s, ms (default), mks, ns or ticks)", metavar="UNITS", default="ms")
707    (options, args) = parser.parse_args()
708
709    options.generateHtml = detectHtmlOutputType(options.format)
710    if options.metric not in metrix_table:
711        options.metric = "gmean"
712
713    #print options
714    #print args
715
716#    tbl = table()
717#    tbl.newColumn("first", "qqqq", align = "left")
718#    tbl.newColumn("second", "wwww\nz\nx\n")
719#    tbl.newColumn("third", "wwasdas")
720#
721#    tbl.newCell(0, "ccc111", align = "right")
722#    tbl.newCell(1, "dddd1")
723#    tbl.newCell(2, "8768756754")
724#    tbl.newRow()
725#    tbl.newCell(0, "1\n2\n3\n4\n5\n6\n7", align = "center", colspan = 2, rowspan = 2)
726#    tbl.newCell(2, "xxx\nqqq", align = "center", colspan = 1, valign = "middle")
727#    tbl.newRow()
728#    tbl.newCell(2, "+", align = "center", colspan = 1, valign = "middle")
729#    tbl.newRow()
730#    tbl.newCell(0, "vcvvbasdsadassdasdasv", align = "right", colspan = 2)
731#    tbl.newCell(2, "dddd1")
732#    tbl.newRow()
733#    tbl.newCell(0, "vcvvbv")
734#    tbl.newCell(1, "3445324", align = "right")
735#    tbl.newCell(2, None)
736#    tbl.newCell(1, "0000")
737#    if sys.stdout.isatty():
738#        tbl.consolePrintTable(sys.stdout)
739#    else:
740#        htmlPrintHeader(sys.stdout)
741#        tbl.htmlPrintTable(sys.stdout)
742#        htmlPrintFooter(sys.stdout)
743
744    import testlog_parser
745
746    if options.generateHtml:
747        htmlPrintHeader(sys.stdout, "Tables demo")
748
749    getter = metrix_table[options.metric][1]
750
751    for arg in args:
752        tests = testlog_parser.parseLogFile(arg)
753        tbl = table(arg)
754        tbl.newColumn("name", "Name of Test", align = "left")
755        tbl.newColumn("value", metrix_table[options.metric][0], align = "center", bold = "true")
756
757        for t in sorted(tests):
758            tbl.newRow()
759            tbl.newCell("name", str(t))
760
761            status = t.get("status")
762            if status != "run":
763                tbl.newCell("value", status)
764            else:
765                val = getter(t, None, options.units)
766                if val:
767                    if options.metric.endswith("%"):
768                        tbl.newCell("value", "%.2f" % val, val)
769                    else:
770                        tbl.newCell("value", "%.3f %s" % (val, options.units), val)
771                else:
772                    tbl.newCell("value", "-")
773
774        if options.generateHtml:
775            tbl.htmlPrintTable(sys.stdout)
776        else:
777            tbl.consolePrintTable(sys.stdout)
778
779    if options.generateHtml:
780        htmlPrintFooter(sys.stdout)
781