89adcf675997fe3ddd902d5c23564e5ca6070bb5
[WebKit-https.git] / Tools / Scripts / webkitpy / performance_tests / perftest.py
1 #!/usr/bin/env python
2 # Copyright (C) 2012 Google Inc. All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
8 #     * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 #     * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 #     * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31 import re
32
33
34 class PerfTest(object):
35     def __init__(self, test_name, dirname, path_or_url):
36         self._test_name = test_name
37         self._dirname = dirname
38         self._path_or_url = path_or_url
39
40     def test_name(self):
41         return self._test_name
42
43     def dirname(self):
44         return self._dirname
45
46     def path_or_url(self):
47         return self._path_or_url
48
49     _lines_to_ignore_in_parser_result = [
50         re.compile(r'^Running \d+ times$'),
51         re.compile(r'^Ignoring warm-up '),
52         re.compile(r'^Info:'),
53         re.compile(r'^\d+(.\d+)?$'),
54         # Following are for handle existing test like Dromaeo
55         re.compile(re.escape("""main frame - has 1 onunload handler(s)""")),
56         re.compile(re.escape("""frame "<!--framePath //<!--frame0-->-->" - has 1 onunload handler(s)""")),
57         re.compile(re.escape("""frame "<!--framePath //<!--frame0-->/<!--frame0-->-->" - has 1 onunload handler(s)"""))]
58
59     def _should_ignore_line_in_parser_test_result(self, line):
60         if not line:
61             return True
62         for regex in self._lines_to_ignore_in_parser_result:
63             if regex.search(line):
64                 return True
65         return False
66
67     def parse_output(self, output, printer, buildbot_output):
68         got_a_result = False
69         test_failed = False
70         results = {}
71         keys = ['avg', 'median', 'stdev', 'min', 'max']
72         score_regex = re.compile(r'^(?P<key>' + r'|'.join(keys) + r')\s+(?P<value>[0-9\.]+)\s*(?P<unit>.*)')
73         unit = "ms"
74
75         for line in re.split('\n', output.text):
76             score = score_regex.match(line)
77             if score:
78                 results[score.group('key')] = float(score.group('value'))
79                 if score.group('unit'):
80                     unit = score.group('unit')
81                 continue
82
83             if not self._should_ignore_line_in_parser_test_result(line):
84                 test_failed = True
85                 printer.write("%s" % line)
86
87         if test_failed or set(keys) != set(results.keys()):
88             return None
89
90         results['unit'] = unit
91
92         test_name = re.sub(r'\.\w+$', '', self._test_name)
93         buildbot_output.write('RESULT %s= %s %s\n' % (test_name.replace('/', ': '), results['avg'], unit))
94         buildbot_output.write(', '.join(['%s= %s %s' % (key, results[key], unit) for key in keys[1:]]) + '\n')
95
96         return {test_name: results}
97
98
99 class ChromiumStylePerfTest(PerfTest):
100     _chromium_style_result_regex = re.compile(r'^RESULT\s+(?P<name>[^=]+)\s*=\s+(?P<value>\d+(\.\d+)?)\s*(?P<unit>\w+)$')
101
102     def __init__(self, test_name, dirname, path_or_url):
103         super(ChromiumStylePerfTest, self).__init__(test_name, dirname, path_or_url)
104
105     def parse_output(self, output, printer, buildbot_output):
106         test_failed = False
107         got_a_result = False
108         results = {}
109         for line in re.split('\n', output.text):
110             resultLine = ChromiumStylePerfTest._chromium_style_result_regex.match(line)
111             if resultLine:
112                 # FIXME: Store the unit
113                 results[self.test_name() + ':' + resultLine.group('name').replace(' ', '')] = float(resultLine.group('value'))
114                 buildbot_output.write("%s\n" % line)
115             elif not len(line) == 0:
116                 test_failed = True
117                 printer.write("%s" % line)
118         return results if results and not test_failed else None