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

Unified Diff: gm/rebaseline_server/server.py

Issue 28903008: rebaseline_server: add tabs, and ability to submit new baselines to the server (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: more Created 7 years, 2 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « gm/rebaseline_server/results.py ('k') | gm/rebaseline_server/static/loader.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: gm/rebaseline_server/server.py
===================================================================
--- gm/rebaseline_server/server.py (revision 11913)
+++ gm/rebaseline_server/server.py (working copy)
@@ -60,6 +60,9 @@
DEFAULT_EXPECTATIONS_DIR = os.path.join(TRUNK_DIRECTORY, 'expectations', 'gm')
DEFAULT_PORT = 8888
+_HTTP_HEADER_CONTENT_LENGTH = 'Content-Length'
+_HTTP_HEADER_CONTENT_TYPE = 'Content-Type'
+
_SERVER = None # This gets filled in by main()
class Server(object):
@@ -103,54 +106,60 @@
results. """
return self._reload_seconds
- def _update_results(self):
+ def update_results(self):
""" Create or update self.results, based on the expectations in
self._expectations_dir and the latest actuals from skia-autogen.
"""
- logging.info('Updating actual GM results in %s from SVN repo %s ...' % (
- self._actuals_dir, ACTUALS_SVN_REPO))
- actuals_repo = svn.Svn(self._actuals_dir)
- if not os.path.isdir(self._actuals_dir):
- os.makedirs(self._actuals_dir)
- actuals_repo.Checkout(ACTUALS_SVN_REPO, '.')
- else:
- actuals_repo.Update('.')
-
- # We only update the expectations dir if the server was run with a nonzero
- # --reload argument; otherwise, we expect the user to maintain her own
- # expectations as she sees fit.
- #
- # TODO(epoger): Use git instead of svn to check out expectations, since
- # the Skia repo is moving to git.
- if self._reload_seconds:
- logging.info('Updating expected GM results in %s from SVN repo %s ...' % (
- self._expectations_dir, EXPECTATIONS_SVN_REPO))
- expectations_repo = svn.Svn(self._expectations_dir)
- if not os.path.isdir(self._expectations_dir):
- os.makedirs(self._expectations_dir)
- expectations_repo.Checkout(EXPECTATIONS_SVN_REPO, '.')
+ with self.results_lock:
+ # self.results_lock prevents us from updating the actual GM results
+ # in multiple threads simultaneously
+ logging.info('Updating actual GM results in %s from SVN repo %s ...' % (
+ self._actuals_dir, ACTUALS_SVN_REPO))
+ actuals_repo = svn.Svn(self._actuals_dir)
+ if not os.path.isdir(self._actuals_dir):
+ os.makedirs(self._actuals_dir)
+ actuals_repo.Checkout(ACTUALS_SVN_REPO, '.')
else:
- expectations_repo.Update('.')
+ actuals_repo.Update('.')
- logging.info(
- 'Parsing results from actuals in %s and expectations in %s ...' % (
- self._actuals_dir, self._expectations_dir))
- self.results = results.Results(
- actuals_root=self._actuals_dir,
- expected_root=self._expectations_dir)
+ # We only update the expectations dir if the server was run with a
+ # nonzero --reload argument; otherwise, we expect the user to maintain
+ # her own expectations as she sees fit.
+ #
+ # self.results_lock prevents us from updating the expected GM results
+ # in multiple threads simultaneously
+ #
+ # TODO(epoger): Use git instead of svn to check out expectations, since
+ # the Skia repo is moving to git.
+ if self._reload_seconds:
+ logging.info(
+ 'Updating expected GM results in %s from SVN repo %s ...' % (
+ self._expectations_dir, EXPECTATIONS_SVN_REPO))
+ expectations_repo = svn.Svn(self._expectations_dir)
+ if not os.path.isdir(self._expectations_dir):
+ os.makedirs(self._expectations_dir)
+ expectations_repo.Checkout(EXPECTATIONS_SVN_REPO, '.')
+ else:
+ expectations_repo.Update('.')
+ logging.info(
+ 'Parsing results from actuals in %s and expectations in %s ...' % (
+ self._actuals_dir, self._expectations_dir))
+ self.results = results.Results(
+ actuals_root=self._actuals_dir,
+ expected_root=self._expectations_dir)
+
def _result_reloader(self):
""" If --reload argument was specified, reload results at the appropriate
interval.
"""
while self._reload_seconds:
time.sleep(self._reload_seconds)
- with self.results_lock:
- self._update_results()
+ self.update_results()
def run(self):
- self._update_results()
self.results_lock = thread.allocate_lock()
+ self.update_results()
thread.start_new_thread(self._result_reloader, ())
if self._export:
@@ -224,6 +233,9 @@
(time_updated+_SERVER.reload_seconds()) if _SERVER.reload_seconds()
else None),
+ # The type we passed to get_results_of_type()
+ 'type': type,
+
# Hash of testData, which the client must return with any edits--
# this ensures that the edits were made to a particular dataset.
'dataHash': str(hash(repr(response_dict['testData']))),
@@ -261,6 +273,76 @@
% (full_path, static_dir))
self.send_error(404)
+ def do_POST(self):
+ """ Handles all POST requests, forwarding them to the appropriate
+ do_POST_* dispatcher. """
+ # All requests must be of this form:
+ # /dispatcher
+ # where 'dispatcher' indicates which do_POST_* dispatcher to run.
+ normpath = posixpath.normpath(self.path)
+ dispatchers = {
+ '/edits': self.do_POST_edits,
+ }
+ try:
+ dispatcher = dispatchers[normpath]
+ dispatcher()
+ self.send_response(200)
+ except:
+ self.send_error(404)
+ raise
+
+ def do_POST_edits(self):
+ """ Handle a POST request with modifications to GM expectations, in this
+ format:
+
+ {
+ 'oldResultsType': 'all', # type of results that the client loaded
+ # and then made modifications to
+ 'oldResultsHash': 39850913, # hash of results when the client loaded them
+ # (ensures that the client and server apply
+ # modifications to the same base)
+ 'modifications': [
+ {
+ 'builder': 'Test-Android-Nexus10-MaliT604-Arm7-Debug',
+ 'test': 'strokerect',
+ 'config': 'gpu',
+ 'expectedHashType': 'bitmap-64bitMD5',
+ 'expectedHashDigest': '1707359671708613629',
+ },
+ ...
+ ],
+ }
+
+ Raises an Exception if there were any problems.
+ """
+ if not _SERVER.is_editable():
+ raise Exception('this server is not running in --editable mode')
+
+ content_type = self.headers[_HTTP_HEADER_CONTENT_TYPE]
+ if content_type != 'application/json;charset=UTF-8':
+ raise Exception('unsupported %s [%s]' % (
+ _HTTP_HEADER_CONTENT_TYPE, content_type))
+
+ content_length = int(self.headers[_HTTP_HEADER_CONTENT_LENGTH])
+ json_data = self.rfile.read(content_length)
+ data = json.loads(json_data)
+ logging.debug('do_POST_edits: received new GM expectations data [%s]' %
+ data)
+
+ with _SERVER.results_lock:
+ oldResultsType = data['oldResultsType']
+ oldResults = _SERVER.results.get_results_of_type(oldResultsType)
+ oldResultsHash = str(hash(repr(oldResults['testData'])))
+ if oldResultsHash != data['oldResultsHash']:
+ raise Exception('results of type "%s" changed while the client was '
+ 'making modifications. The client should reload the '
+ 'results and submit the modifications again.' %
+ oldResultsType)
+ _SERVER.results.edit_expectations(data['modifications'])
+
+ # Now that the edits have been committed, update results to reflect them.
+ _SERVER.update_results()
+
def redirect_to(self, url):
""" Redirect the HTTP client to a different url.
@@ -318,8 +400,7 @@
'exist, it will be created. Defaults to %(default)s'),
default=DEFAULT_ACTUALS_DIR)
parser.add_argument('--editable', action='store_true',
- help=('TODO(epoger): NOT YET IMPLEMENTED. '
- 'Allow HTTP clients to submit new baselines.'))
+ help=('Allow HTTP clients to submit new baselines.'))
parser.add_argument('--expectations-dir',
help=('Directory under which to find GM expectations; '
'defaults to %(default)s'),
« no previous file with comments | « gm/rebaseline_server/results.py ('k') | gm/rebaseline_server/static/loader.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698