diff options
author | rmistry@google.com <rmistry@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-12-02 13:50:38 +0000 |
---|---|---|
committer | rmistry@google.com <rmistry@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-12-02 13:50:38 +0000 |
commit | d6bab0238655dbab24dfe92bd0b16b464310a8c7 (patch) | |
tree | 664de29dd3b91a1ed1be463f2cd797e871cabec7 /gm | |
parent | 5b39f5ba9c339d1e4dae391fee9ec1396feec180 (diff) |
Reverting r12427
git-svn-id: http://skia.googlecode.com/svn/trunk@12428 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'gm')
-rw-r--r-- | gm/bitmapcopy.cpp | 1 | ||||
-rw-r--r-- | gm/bitmapfilters.cpp | 1 | ||||
-rwxr-xr-x | gm/rebaseline_server/server.py | 161 |
3 files changed, 103 insertions, 60 deletions
diff --git a/gm/bitmapcopy.cpp b/gm/bitmapcopy.cpp index 5e88cfdd0f..928b4037c1 100644 --- a/gm/bitmapcopy.cpp +++ b/gm/bitmapcopy.cpp @@ -11,7 +11,6 @@ namespace skiagm { static const char* gConfigNames[] = { "unknown config", - "A1", "A8", "Index8", "565", diff --git a/gm/bitmapfilters.cpp b/gm/bitmapfilters.cpp index f0153e482b..5856f87f66 100644 --- a/gm/bitmapfilters.cpp +++ b/gm/bitmapfilters.cpp @@ -47,7 +47,6 @@ static SkScalar draw_set(SkCanvas* c, const SkBitmap& bm, SkScalar x, static const char* gConfigNames[] = { "unknown config", - "A1", "A8", "Index8", "565", diff --git a/gm/rebaseline_server/server.py b/gm/rebaseline_server/server.py index 47ee236f77..670c94de28 100755 --- a/gm/rebaseline_server/server.py +++ b/gm/rebaseline_server/server.py @@ -19,8 +19,10 @@ import posixpath import re import shutil import socket +import subprocess import sys import thread +import threading import time import urlparse @@ -42,8 +44,6 @@ import results ACTUALS_SVN_REPO = 'http://skia-autogen.googlecode.com/svn/gm-actual' PATHSPLIT_RE = re.compile('/([^/]+)/(.+)') -TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(os.path.dirname( - os.path.realpath(__file__)))) EXPECTATIONS_DIR = os.path.join(TRUNK_DIRECTORY, 'expectations', 'gm') GENERATED_IMAGES_ROOT = os.path.join(PARENT_DIRECTORY, 'static', 'generated-images') @@ -67,6 +67,29 @@ _HTTP_HEADER_CONTENT_TYPE = 'Content-Type' _SERVER = None # This gets filled in by main() + +def _run_command(args, directory): + """Runs a command and returns stdout as a single string. + + Args: + args: the command to run, as a list of arguments + directory: directory within which to run the command + + Returns: stdout, as a string + + Raises an Exception if the command failed (exited with nonzero return code). + """ + logging.debug('_run_command: %s in directory %s' % (args, directory)) + proc = subprocess.Popen(args, cwd=directory, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (stdout, stderr) = proc.communicate() + if proc.returncode is not 0: + raise Exception('command "%s" failed in dir "%s": %s' % + (args, directory, stderr)) + return stdout + + def _get_routable_ip_address(): """Returns routable IP address of this host (the IP address of its network interface that would be used for most traffic, not its localhost @@ -77,6 +100,7 @@ def _get_routable_ip_address(): sock.close() return host + def _create_svn_checkout(dir_path, repo_url): """Creates local checkout of an SVN repository at the specified directory path, returning an svn.Svn object referring to the local checkout. @@ -120,69 +144,87 @@ class Server(object): self._actuals_repo = _create_svn_checkout( dir_path=actuals_dir, repo_url=ACTUALS_SVN_REPO) - # 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 update the expectations dir, since - # the Skia repo is moving to git. - # When we make that change, we will have to update the entire workspace, - # not just the expectations dir, because git only operates on the repo - # as a whole. - # And since Skia uses depot_tools to manage its dependencies, we will have - # to run "gclient sync" rather than a raw "git pull". - if reload_seconds: - self._expectations_repo = svn.Svn(EXPECTATIONS_DIR) - else: - self._expectations_repo = None + # Reentrant lock that must be held whenever updating EITHER of: + # 1. self._results + # 2. the expected or actual results on local disk + self.results_rlock = threading.RLock() + # self._results will be filled in by calls to update_results() + self._results = None + @property + def results(self): + """ Returns the most recently generated results, or None if update_results() + has not been called yet. """ + return self._results + + @property def is_exported(self): """ Returns true iff HTTP clients on other hosts are allowed to access this server. """ return self._export + @property def is_editable(self): """ Returns true iff HTTP clients are allowed to submit new baselines. """ return self._editable + @property def reload_seconds(self): """ Returns the result reload period in seconds, or 0 if we don't reload results. """ return self._reload_seconds def update_results(self): - """ Create or update self.results, based on the expectations in + """ Create or update self._results, based on the expectations in EXPECTATIONS_DIR and the latest actuals from skia-autogen. + + We hold self.results_rlock while we do this, to guarantee that no other + thread attempts to update either self._results or the underlying files at + the same time. """ - logging.info('Updating actual GM results in %s from SVN repo %s ...' % ( - self._actuals_dir, ACTUALS_SVN_REPO)) - self._actuals_repo.Update('.') + with self.results_rlock: + logging.info('Updating actual GM results in %s from SVN repo %s ...' % ( + self._actuals_dir, ACTUALS_SVN_REPO)) + self._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. + # + # Because the Skia repo is moving from SVN to git, and git does not + # support updating a single directory tree, we have to update the entire + # repo checkout. + # + # Because Skia uses depot_tools, we have to update using "gclient sync" + # instead of raw git (or SVN) update. Happily, this will work whether + # the checkout was created using git or SVN. + if self._reload_seconds: + logging.info( + 'Updating expected GM results in %s by syncing Skia repo ...' % + EXPECTATIONS_DIR) + _run_command(['gclient', 'sync'], TRUNK_DIRECTORY) - if self._expectations_repo: logging.info( - 'Updating expected GM results in %s ...' % EXPECTATIONS_DIR) - self._expectations_repo.Update('.') - - logging.info( ('Parsing results from actuals in %s and expectations in %s, ' - + 'and generating pixel diffs (may take a while) ...') % ( - self._actuals_dir, EXPECTATIONS_DIR)) - self.results = results.Results( - actuals_root=self._actuals_dir, - expected_root=EXPECTATIONS_DIR, - generated_images_root=GENERATED_IMAGES_ROOT) + + 'and generating pixel diffs (may take a while) ...') % ( + self._actuals_dir, EXPECTATIONS_DIR)) + self._results = results.Results( + actuals_root=self._actuals_dir, + expected_root=EXPECTATIONS_DIR, + generated_images_root=GENERATED_IMAGES_ROOT) def _result_reloader(self): - """ If --reload argument was specified, reload results at the appropriate - interval. + """ Reload results at the appropriate interval. This never exits, so it + should be run in its own thread. """ - while self._reload_seconds: + while True: time.sleep(self._reload_seconds) self.update_results() def run(self): self.update_results() - thread.start_new_thread(self._result_reloader, ()) + if self._reload_seconds: + thread.start_new_thread(self._result_reloader, ()) if self._export: server_address = ('', self._port) @@ -256,9 +298,9 @@ class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): # We only return these timestamps if the --reload argument was passed; # otherwise, we have no idea when the expectations were last updated # (we allow the user to maintain her own expectations as she sees fit). - 'timeUpdated': time_updated if _SERVER.reload_seconds() else None, + 'timeUpdated': time_updated if _SERVER.reload_seconds else None, 'timeNextUpdateAvailable': ( - (time_updated+_SERVER.reload_seconds()) if _SERVER.reload_seconds() + (time_updated+_SERVER.reload_seconds) if _SERVER.reload_seconds else None), # The type we passed to get_results_of_type() @@ -269,10 +311,10 @@ class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): 'dataHash': str(hash(repr(response_dict['testData']))), # Whether the server will accept edits back. - 'isEditable': _SERVER.is_editable(), + 'isEditable': _SERVER.is_editable, # Whether the service is accessible from other hosts. - 'isExported': _SERVER.is_exported(), + 'isExported': _SERVER.is_exported, } self.send_json_dict(response_dict) except: @@ -343,7 +385,7 @@ class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): Raises an Exception if there were any problems. """ - if not _SERVER.is_editable(): + if not _SERVER.is_editable: raise Exception('this server is not running in --editable mode') content_type = self.headers[_HTTP_HEADER_CONTENT_TYPE] @@ -357,22 +399,23 @@ class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): logging.debug('do_POST_edits: received new GM expectations data [%s]' % data) - # Since we must make multiple calls to the Results object, grab a - # reference to it in case it is updated to point at a new Results - # object within another thread. - results_obj = _SERVER.results - oldResultsType = data['oldResultsType'] - oldResults = results_obj.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) - results_obj.edit_expectations(data['modifications']) - - # Now that the edits have been committed, update results to reflect them. - _SERVER.update_results() + # Update the results on disk with the information we received from the + # client. + # We must hold _SERVER.results_rlock while we do this, to guarantee that + # no other thread updates expectations (from the Skia repo) while we are + # updating them (using the info we received from the client). + with _SERVER.results_rlock: + 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']) + # Read the updated results back from disk. + _SERVER.update_results() def redirect_to(self, url): """ Redirect the HTTP client to a different url. @@ -445,7 +488,8 @@ def main(): parser.add_argument('--reload', type=int, help=('How often (a period in seconds) to update the ' 'results. If specified, both expected and actual ' - 'results will be updated. ' + 'results will be updated by running "gclient sync" ' + 'on your Skia checkout as a whole. ' 'By default, we do not reload at all, and you ' 'must restart the server to pick up new data.'), default=0) @@ -456,5 +500,6 @@ def main(): reload_seconds=args.reload) _SERVER.run() + if __name__ == '__main__': main() |