Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(948)

Side by Side Diff: Tools/Scripts/webkitpy/layout_tests/print_layout_test_times.py

Issue 23672050: Refactor print-layout-test-times and add unit tests. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: tweak a bit to minimize diff Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 #!/usr/bin/python 1 # Copyright (C) 2013 Google Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
5 # met:
6 #
7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the
12 # distribution.
13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
2 import json 29 import json
3 import optparse 30 import optparse
4 import os
5 import sys
6 31
7 from webkitpy.common.host import Host 32 from webkitpy.layout_tests.port import Port
8 33
9 ALL_TEST_TYPES = ['text', 'harness', 'pixel', 'ref', 'unknown']
10 34
11 def main(argv): 35 def main(host, argv):
12 parser = optparse.OptionParser(usage='%prog [times_ms.json]') 36 parser = optparse.OptionParser(usage='%prog [times_ms.json]')
13 parser.add_option('-f', '--forward', action='store', type='int', 37 parser.add_option('-f', '--forward', action='store', type='int',
14 help='group times by first N directories of test') 38 help='group times by first N directories of test')
15 parser.add_option('-b', '--backward', action='store', type='int', 39 parser.add_option('-b', '--backward', action='store', type='int',
16 help='group times by last N directories of test') 40 help='group times by last N directories of test')
17 parser.add_option('--fastest', action='store', type='float', 41 parser.add_option('--fastest', action='store', type='float',
18 help='print a list of tests that will take N % of the time ') 42 help='print a list of tests that will take N % of the time ')
19 parser.add_option('--type', action='append', default=[],
20 help='type of tests to filter for (%s)' % ALL_TEST_TYPES)
21 43
22 epilog = """ 44 epilog = """
23 You can print out aggregate times per directory using the -f and -b 45 You can print out aggregate times per directory using the -f and -b
24 flags. The value passed to each flag indicates the "depth" of the flag, 46 flags. The value passed to each flag indicates the "depth" of the flag,
25 similar to positive and negative arguments to python arrays. 47 similar to positive and negative arguments to python arrays.
26 48
27 For example, given fast/forms/week/week-input-type.html, -f 1 49 For example, given fast/forms/week/week-input-type.html, -f 1
28 truncates to 'fast', -f 2 and -b 2 truncates to 'fast/forms', and -b 1 50 truncates to 'fast', -f 2 and -b 2 truncates to 'fast/forms', and -b 1
29 truncates to fast/forms/week . -f 0 truncates to '', which can be used 51 truncates to fast/forms/week . -f 0 truncates to '', which can be used
30 to produce a single total time for the run.""" 52 to produce a single total time for the run."""
31 parser.epilog = '\n'.join(s.lstrip() for s in epilog.splitlines()) 53 parser.epilog = '\n'.join(s.lstrip() for s in epilog.splitlines())
32 54
33 options, args = parser.parse_args(argv) 55 options, args = parser.parse_args(argv)
34 options.type = options.type or ALL_TEST_TYPES
35 56
36 host = Host()
37 port = host.port_factory.get() 57 port = host.port_factory.get()
38 if args and args[0]: 58 if args and args[0]:
39 times_ms_path = args[0] 59 times_ms_path = args[0]
40 else: 60 else:
41 times_ms_path = host.filesystem.join(port.results_directory(), 'times_ms .json') 61 times_ms_path = host.filesystem.join(port.results_directory(), 'times_ms .json')
42 62
43 with open(times_ms_path, 'r') as fp: 63 times_trie = json.loads(host.filesystem.read_text_file(times_ms_path))
44 times_trie = json.load(fp)
45 64
46 times = convert_trie_to_flat_paths(times_trie) 65 times = convert_trie_to_flat_paths(times_trie)
47 66
48 if options.fastest: 67 if options.fastest:
49 print_fastest(port, options, times) 68 if options.forward is None and options.backward is None:
69 options.forward = 0
70 print_fastest(host, port, options, times)
50 else: 71 else:
51 print_times(options, times) 72 print_times(host, options, times)
52 73
53 74
54 def print_times(options, times): 75 def print_times(host, options, times):
55 by_key = times_by_key(times, options.forward, options.backward) 76 by_key = times_by_key(times, options.forward, options.backward)
56 for key in sorted(by_key): 77 for key in sorted(by_key):
57 print "%s %d" % (key, by_key[key]) 78 if key:
79 host.print_("%s %d" % (key, by_key[key]))
80 else:
81 host.print_("%d" % by_key[key])
58 82
59 83
60 def print_fastest(port, options, times): 84 def print_fastest(host, port, options, times):
61 total = times_by_key(times, 0, None)[''] 85 total = times_by_key(times, 0, None)['']
62 by_key = times_by_key(times, options.forward, options.backward) 86 by_key = times_by_key(times, options.forward, options.backward)
63 keys_by_time = sorted(by_key, key=lambda k: by_key[k]) 87 keys_by_time = sorted(by_key, key=lambda k: (by_key[k], k))
64 88
65 tests_by_key = {} 89 tests_by_key = {}
66 for test_name in times: 90 for test_name in sorted(times):
67 key = key_for(test_name, options.forward, options.backward) 91 key = key_for(test_name, options.forward, options.backward)
68 if key in tests_by_key: 92 if key in sorted(tests_by_key):
69 tests_by_key[key].append(test_name) 93 tests_by_key[key].append(test_name)
70 else: 94 else:
71 tests_by_key[key] = [test_name] 95 tests_by_key[key] = [test_name]
72 96
73 fast_tests_by_key = {} 97 fast_tests_by_key = {}
74 total_so_far = 0 98 total_so_far = 0
75 per_key = total * options.fastest / (len(keys_by_time) * 100.0) 99 per_key = total * options.fastest / (len(keys_by_time) * 100.0)
76 budget = 0 100 budget = 0
77 while keys_by_time: 101 while keys_by_time:
78 budget += per_key 102 budget += per_key
79 key = keys_by_time.pop(0) 103 key = keys_by_time.pop(0)
80 tests_by_time = sorted(tests_by_key[key], key=lambda t: times[t]) 104 tests_by_time = sorted(tests_by_key[key], key=lambda t: (times[t], t))
81 fast_tests_by_key[key] = [] 105 fast_tests_by_key[key] = []
82 while tests_by_time and total_so_far < budget: 106 while tests_by_time and total_so_far <= budget:
83 test = tests_by_time.pop(0) 107 test = tests_by_time.pop(0)
84 if options.type != ALL_TEST_TYPES and test_type(port, test) not in o ptions.type:
85 continue
86 test_time = times[test] 108 test_time = times[test]
87 if test_time and total_so_far + test_time < budget: # This test is a n optimization to not include tests that are Skipped. 109 # Make sure test time > 0 so we don't include tests that are skippe d.
110 if test_time and total_so_far + test_time <= budget:
88 fast_tests_by_key[key].append(test) 111 fast_tests_by_key[key].append(test)
89 total_so_far += test_time 112 total_so_far += test_time
90 113
91 for k in sorted(fast_tests_by_key): 114 for k in sorted(fast_tests_by_key):
92 for t in fast_tests_by_key[k]: 115 for t in fast_tests_by_key[k]:
93 print "%s %d" % (t, times[t]) 116 host.print_("%s %d" % (t, times[t]))
94 return 117 return
95 118
96 119
97 def test_type(port, test_name):
98 fs = port.host.filesystem
99 if fs.exists(port.expected_filename(test_name, '.png')):
100 return 'pixel'
101 if port.reference_files(test_name):
102 return 'ref'
103 txt = port.expected_text(test_name)
104 if txt:
105 if 'layer at (0,0) size 800x600' in txt:
106 return 'pixel'
107 for line in txt.splitlines():
108 if line.startswith('FAIL') or line.startswith('TIMEOUT') or line.sta rtswith('PASS'):
109 return 'harness'
110 return 'text'
111 return 'unknown'
112
113
114 def key_for(path, forward, backward): 120 def key_for(path, forward, backward):
121 sep = Port.TEST_PATH_SEPARATOR
115 if forward is not None: 122 if forward is not None:
116 return os.sep.join(path.split(os.sep)[:-1][:forward]) 123 return sep.join(path.split(sep)[:-1][:forward])
117 if backward is not None: 124 if backward is not None:
118 return os.sep.join(path.split(os.sep)[:-backward]) 125 return sep.join(path.split(sep)[:-backward])
119 return path 126 return path
120 127
121 128
122 def times_by_key(times, forward, backward): 129 def times_by_key(times, forward, backward):
123 by_key = {} 130 by_key = {}
124 for test_name in times: 131 for test_name in times:
125 key = key_for(test_name, forward, backward) 132 key = key_for(test_name, forward, backward)
126 if key in by_key: 133 if key in by_key:
127 by_key[key] += times[test_name] 134 by_key[key] += times[test_name]
128 else: 135 else:
129 by_key[key] = times[test_name] 136 by_key[key] = times[test_name]
130 return by_key 137 return by_key
131 138
132 139
133
134 def convert_trie_to_flat_paths(trie, prefix=None): 140 def convert_trie_to_flat_paths(trie, prefix=None):
135 # Cloned from webkitpy.layout_tests.layout_package.json_results_generator
136 # so that this code can stand alone.
137 result = {} 141 result = {}
138 for name, data in trie.iteritems(): 142 for name, data in trie.iteritems():
139 if prefix: 143 if prefix:
140 name = prefix + "/" + name 144 name = prefix + "/" + name
141 if isinstance(data, int): 145 if isinstance(data, int):
142 result[name] = data 146 result[name] = data
143 else: 147 else:
144 result.update(convert_trie_to_flat_paths(data, name)) 148 result.update(convert_trie_to_flat_paths(data, name))
145 149
146 return result 150 return result
147
148
149 if __name__ == '__main__':
150 sys.exit(main(sys.argv[1:]))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698