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

Side by Side Diff: sky/tools/webkitpy/tool/servers/rebaselineserver.py

Issue 675343003: Prune a bunch of webkitpy. (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 6 years, 1 month 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
OLDNEW
(Empty)
1 # Copyright (c) 2010 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
29 import fnmatch
30 import os
31 import os.path
32 import BaseHTTPServer
33
34 from webkitpy.common.host import Host # FIXME: This should not be needed!
35 from webkitpy.layout_tests.port.base import Port
36 from webkitpy.tool.servers.reflectionhandler import ReflectionHandler
37
38
39 STATE_NEEDS_REBASELINE = 'needs_rebaseline'
40 STATE_REBASELINE_FAILED = 'rebaseline_failed'
41 STATE_REBASELINE_SUCCEEDED = 'rebaseline_succeeded'
42
43
44 def _get_actual_result_files(test_file, test_config):
45 test_name, _ = os.path.splitext(test_file)
46 test_directory = os.path.dirname(test_file)
47
48 test_results_directory = test_config.filesystem.join(
49 test_config.results_directory, test_directory)
50 actual_pattern = os.path.basename(test_name) + '-actual.*'
51 actual_files = []
52 for filename in test_config.filesystem.listdir(test_results_directory):
53 if fnmatch.fnmatch(filename, actual_pattern):
54 actual_files.append(filename)
55 actual_files.sort()
56 return tuple(actual_files)
57
58
59 def _rebaseline_test(test_file, baseline_target, baseline_move_to, test_config, log):
60 test_name, _ = os.path.splitext(test_file)
61 test_directory = os.path.dirname(test_name)
62
63 log('Rebaselining %s...' % test_name)
64
65 actual_result_files = _get_actual_result_files(test_file, test_config)
66 filesystem = test_config.filesystem
67 scm = test_config.scm
68 layout_tests_directory = test_config.layout_tests_directory
69 results_directory = test_config.results_directory
70 target_expectations_directory = filesystem.join(
71 layout_tests_directory, 'platform', baseline_target, test_directory)
72 test_results_directory = test_config.filesystem.join(
73 test_config.results_directory, test_directory)
74
75 # If requested, move current baselines out
76 current_baselines = get_test_baselines(test_file, test_config)
77 if baseline_target in current_baselines and baseline_move_to != 'none':
78 log(' Moving current %s baselines to %s' %
79 (baseline_target, baseline_move_to))
80
81 # See which ones we need to move (only those that are about to be
82 # updated), and make sure we're not clobbering any files in the
83 # destination.
84 current_extensions = set(current_baselines[baseline_target].keys())
85 actual_result_extensions = [
86 os.path.splitext(f)[1] for f in actual_result_files]
87 extensions_to_move = current_extensions.intersection(
88 actual_result_extensions)
89
90 if extensions_to_move.intersection(
91 current_baselines.get(baseline_move_to, {}).keys()):
92 log(' Already had baselines in %s, could not move existing '
93 '%s ones' % (baseline_move_to, baseline_target))
94 return False
95
96 # Do the actual move.
97 if extensions_to_move:
98 if not _move_test_baselines(
99 test_file,
100 list(extensions_to_move),
101 baseline_target,
102 baseline_move_to,
103 test_config,
104 log):
105 return False
106 else:
107 log(' No current baselines to move')
108
109 log(' Updating baselines for %s' % baseline_target)
110 filesystem.maybe_make_directory(target_expectations_directory)
111 for source_file in actual_result_files:
112 source_path = filesystem.join(test_results_directory, source_file)
113 destination_file = source_file.replace('-actual', '-expected')
114 destination_path = filesystem.join(
115 target_expectations_directory, destination_file)
116 filesystem.copyfile(source_path, destination_path)
117 exit_code = scm.add(destination_path, return_exit_code=True)
118 if exit_code:
119 log(' Could not update %s in SCM, exit code %d' %
120 (destination_file, exit_code))
121 return False
122 else:
123 log(' Updated %s' % destination_file)
124
125 return True
126
127
128 def _move_test_baselines(test_file, extensions_to_move, source_platform, destina tion_platform, test_config, log):
129 test_file_name = os.path.splitext(os.path.basename(test_file))[0]
130 test_directory = os.path.dirname(test_file)
131 filesystem = test_config.filesystem
132
133 # Want predictable output order for unit tests.
134 extensions_to_move.sort()
135
136 source_directory = os.path.join(
137 test_config.layout_tests_directory,
138 'platform',
139 source_platform,
140 test_directory)
141 destination_directory = os.path.join(
142 test_config.layout_tests_directory,
143 'platform',
144 destination_platform,
145 test_directory)
146 filesystem.maybe_make_directory(destination_directory)
147
148 for extension in extensions_to_move:
149 file_name = test_file_name + '-expected' + extension
150 source_path = filesystem.join(source_directory, file_name)
151 destination_path = filesystem.join(destination_directory, file_name)
152 filesystem.copyfile(source_path, destination_path)
153 exit_code = test_config.scm.add(destination_path, return_exit_code=True)
154 if exit_code:
155 log(' Could not update %s in SCM, exit code %d' %
156 (file_name, exit_code))
157 return False
158 else:
159 log(' Moved %s' % file_name)
160
161 return True
162
163
164 def get_test_baselines(test_file, test_config):
165 # FIXME: This seems like a hack. This only seems used to access the Port.exp ected_baselines logic.
166 class AllPlatformsPort(Port):
167 def __init__(self, host):
168 super(AllPlatformsPort, self).__init__(host, 'mac')
169 self._platforms_by_directory = dict([(self._webkit_baseline_path(p), p) for p in test_config.platforms])
170
171 def baseline_search_path(self):
172 return self._platforms_by_directory.keys()
173
174 def platform_from_directory(self, directory):
175 return self._platforms_by_directory[directory]
176
177 test_path = test_config.filesystem.join(test_config.layout_tests_directory, test_file)
178
179 # FIXME: This should get the Host from the test_config to be mockable!
180 host = Host()
181 host.initialize_scm()
182 host.filesystem = test_config.filesystem
183 all_platforms_port = AllPlatformsPort(host)
184
185 all_test_baselines = {}
186 for baseline_extension in ('.txt', '.checksum', '.png'):
187 test_baselines = test_config.test_port.expected_baselines(test_file, bas eline_extension)
188 baselines = all_platforms_port.expected_baselines(test_file, baseline_ex tension, all_baselines=True)
189 for platform_directory, expected_filename in baselines:
190 if not platform_directory:
191 continue
192 if platform_directory == test_config.layout_tests_directory:
193 platform = 'base'
194 else:
195 platform = all_platforms_port.platform_from_directory(platform_d irectory)
196 platform_baselines = all_test_baselines.setdefault(platform, {})
197 was_used_for_test = (platform_directory, expected_filename) in test_ baselines
198 platform_baselines[baseline_extension] = was_used_for_test
199
200 return all_test_baselines
201
202
203 class RebaselineHTTPServer(BaseHTTPServer.HTTPServer):
204 def __init__(self, httpd_port, config):
205 server_name = ""
206 BaseHTTPServer.HTTPServer.__init__(self, (server_name, httpd_port), Reba selineHTTPRequestHandler)
207 self.test_config = config['test_config']
208 self.results_json = config['results_json']
209 self.platforms_json = config['platforms_json']
210
211
212 class RebaselineHTTPRequestHandler(ReflectionHandler):
213 STATIC_FILE_NAMES = frozenset([
214 "index.html",
215 "loupe.js",
216 "main.js",
217 "main.css",
218 "queue.js",
219 "util.js",
220 ])
221
222 STATIC_FILE_DIRECTORY = os.path.join(os.path.dirname(__file__), "data", "reb aselineserver")
223
224 def results_json(self):
225 self._serve_json(self.server.results_json)
226
227 def test_config(self):
228 self._serve_json(self.server.test_config)
229
230 def platforms_json(self):
231 self._serve_json(self.server.platforms_json)
232
233 def rebaseline(self):
234 test = self.query['test'][0]
235 baseline_target = self.query['baseline-target'][0]
236 baseline_move_to = self.query['baseline-move-to'][0]
237 test_json = self.server.results_json['tests'][test]
238
239 if test_json['state'] != STATE_NEEDS_REBASELINE:
240 self.send_error(400, "Test %s is in unexpected state: %s" % (test, t est_json["state"]))
241 return
242
243 log = []
244 success = _rebaseline_test(
245 test,
246 baseline_target,
247 baseline_move_to,
248 self.server.test_config,
249 log=lambda l: log.append(l))
250
251 if success:
252 test_json['state'] = STATE_REBASELINE_SUCCEEDED
253 self.send_response(200)
254 else:
255 test_json['state'] = STATE_REBASELINE_FAILED
256 self.send_response(500)
257
258 self.send_header('Content-type', 'text/plain')
259 self.end_headers()
260 self.wfile.write('\n'.join(log))
261
262 def test_result(self):
263 test_name, _ = os.path.splitext(self.query['test'][0])
264 mode = self.query['mode'][0]
265 if mode == 'expected-image':
266 file_name = test_name + '-expected.png'
267 elif mode == 'actual-image':
268 file_name = test_name + '-actual.png'
269 if mode == 'expected-checksum':
270 file_name = test_name + '-expected.checksum'
271 elif mode == 'actual-checksum':
272 file_name = test_name + '-actual.checksum'
273 elif mode == 'diff-image':
274 file_name = test_name + '-diff.png'
275 if mode == 'expected-text':
276 file_name = test_name + '-expected.txt'
277 elif mode == 'actual-text':
278 file_name = test_name + '-actual.txt'
279 elif mode == 'diff-text':
280 file_name = test_name + '-diff.txt'
281 elif mode == 'diff-text-pretty':
282 file_name = test_name + '-pretty-diff.html'
283
284 file_path = os.path.join(self.server.test_config.results_directory, file _name)
285
286 # Let results be cached for 60 seconds, so that they can be pre-fetched
287 # by the UI
288 self._serve_file(file_path, cacheable_seconds=60)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698