diff options
author | Kristian Monsen <kristianm@google.com> | 2010-05-21 16:53:46 +0100 |
---|---|---|
committer | Kristian Monsen <kristianm@google.com> | 2010-05-25 10:24:15 +0100 |
commit | 6c2af9490927c3c5959b5cb07461b646f8b32f6c (patch) | |
tree | f7111b9b22befab472616c1d50ec94eb50f1ec8c /WebKitTools/Scripts/webkitpy | |
parent | a149172322a9067c14e8b474a53e63649aa17cad (diff) | |
download | external_webkit-6c2af9490927c3c5959b5cb07461b646f8b32f6c.zip external_webkit-6c2af9490927c3c5959b5cb07461b646f8b32f6c.tar.gz external_webkit-6c2af9490927c3c5959b5cb07461b646f8b32f6c.tar.bz2 |
Merge WebKit at r59636: Initial merge by git
Change-Id: I59b289c4e6b18425f06ce41cc9d34c522515de91
Diffstat (limited to 'WebKitTools/Scripts/webkitpy')
84 files changed, 978 insertions, 4034 deletions
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/scm.py b/WebKitTools/Scripts/webkitpy/common/checkout/scm.py index 02e114a..ac9c42e 100644 --- a/WebKitTools/Scripts/webkitpy/common/checkout/scm.py +++ b/WebKitTools/Scripts/webkitpy/common/checkout/scm.py @@ -32,9 +32,6 @@ import os import re -# FIXME: Instead of using run_command directly, most places in this -# class would rather use an SCM.run method which automatically set -# cwd=self.checkout_root. from webkitpy.common.system.executive import Executive, run_command, ScriptError from webkitpy.common.system.user import User from webkitpy.common.system.deprecated_logging import error, log @@ -102,6 +99,18 @@ class SCM: self.checkout_root = self.find_checkout_root(self.cwd) self.dryrun = False + # A wrapper used by subclasses to create processes. + def run(self, args, cwd=None, input=None, error_handler=None, return_exit_code=False, return_stderr=True, decode_output=True): + # FIXME: We should set cwd appropriately. + # FIXME: We should use Executive. + return run_command(args, + cwd=cwd, + input=input, + error_handler=error_handler, + return_exit_code=return_exit_code, + return_stderr=return_stderr, + decode_output=decode_output) + # SCM always returns repository relative path, but sometimes we need # absolute paths to pass to rm, etc. def absolute_path(self, repository_relative_path): @@ -118,7 +127,7 @@ class SCM: def ensure_clean_working_directory(self, force_clean): if not force_clean and not self.working_directory_is_clean(): # FIXME: Shouldn't this use cwd=self.checkout_root? - print run_command(self.status_command(), error_handler=Executive.ignore_error) + print self.run(self.status_command(), error_handler=Executive.ignore_error) raise ScriptError(message="Working directory has modifications, pass --force-clean or --no-clean to continue.") log("Cleaning working directory") @@ -137,7 +146,7 @@ class SCM: def run_status_and_extract_filenames(self, status_command, status_regexp): filenames = [] # We run with cwd=self.checkout_root so that returned-paths are root-relative. - for line in run_command(status_command, cwd=self.checkout_root).splitlines(): + for line in self.run(status_command, cwd=self.checkout_root).splitlines(): match = re.search(status_regexp, line) if not match: continue @@ -297,17 +306,17 @@ class SVN(SCM): if not os.path.isdir(os.path.join(home_directory, ".subversion")): return False find_args = ["find", ".subversion", "-type", "f", "-exec", "grep", "-q", realm, "{}", ";", "-print"]; - find_output = run_command(find_args, cwd=home_directory, error_handler=Executive.ignore_error).rstrip() + find_output = self.run(find_args, cwd=home_directory, error_handler=Executive.ignore_error).rstrip() return find_output and os.path.isfile(os.path.join(home_directory, find_output)) def svn_version(self): if not self.cached_version: - self.cached_version = run_command(['svn', '--version', '--quiet']) + self.cached_version = self.run(['svn', '--version', '--quiet']) return self.cached_version def working_directory_is_clean(self): - return run_command(["svn", "diff"], cwd=self.checkout_root, decode_output=False) == "" + return self.run(["svn", "diff"], cwd=self.checkout_root, decode_output=False) == "" def clean_working_directory(self): # svn revert -R is not as awesome as git reset --hard. @@ -317,7 +326,7 @@ class SVN(SCM): added_files = reversed(sorted(self.added_files())) # added_files() returns directories for SVN, we walk the files in reverse path # length order so that we remove files before we try to remove the directories. - run_command(["svn", "revert", "-R", "."], cwd=self.checkout_root) + self.run(["svn", "revert", "-R", "."], cwd=self.checkout_root) for path in added_files: # This is robust against cwd != self.checkout_root absolute_path = self.absolute_path(path) @@ -336,14 +345,14 @@ class SVN(SCM): def add(self, path): # path is assumed to be cwd relative? - run_command(["svn", "add", path]) + self.run(["svn", "add", path]) def changed_files(self, git_commit=None, squash=None): return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("ACDMR")) def changed_files_for_revision(self, revision): # As far as I can tell svn diff --summarize output looks just like svn status output. - # No file contents printed, thus utf-8 auto-decoding in run_command is fine. + # No file contents printed, thus utf-8 auto-decoding in self.run is fine. status_command = ["svn", "diff", "--summarize", "-c", revision] return self.run_status_and_extract_filenames(status_command, self._status_regexp("ACDMR")) @@ -360,26 +369,27 @@ class SVN(SCM): def display_name(self): return "svn" + # FIXME: This method should be on Checkout. def create_patch(self, git_commit=None, squash=None): """Returns a byte array (str()) representing the patch file. Patch files are effectively binary since they may contain files of multiple different encodings.""" - return run_command([self.script_path("svn-create-patch")], + return self.run([self.script_path("svn-create-patch")], cwd=self.checkout_root, return_stderr=False, decode_output=False) def committer_email_for_revision(self, revision): - return run_command(["svn", "propget", "svn:author", "--revprop", "-r", revision]).rstrip() + return self.run(["svn", "propget", "svn:author", "--revprop", "-r", revision]).rstrip() def contents_at_revision(self, path, revision): """Returns a byte array (str()) containing the contents of path @ revision in the repository.""" remote_path = "%s/%s" % (self._repository_url(), path) - return run_command(["svn", "cat", "-r", revision, remote_path], decode_output=False) + return self.run(["svn", "cat", "-r", revision, remote_path], decode_output=False) def diff_for_revision(self, revision): # FIXME: This should probably use cwd=self.checkout_root - return run_command(['svn', 'diff', '-c', revision]) + return self.run(['svn', 'diff', '-c', revision]) def _repository_url(self): return self.value_from_svn_info(self.checkout_root, 'URL') @@ -390,11 +400,11 @@ class SVN(SCM): log("WARNING: svn merge has been known to take more than 10 minutes to complete. It is recommended you use git for rollouts.") log("Running '%s'" % " ".join(svn_merge_args)) # FIXME: Should this use cwd=self.checkout_root? - run_command(svn_merge_args) + self.run(svn_merge_args) def revert_files(self, file_paths): # FIXME: This should probably use cwd=self.checkout_root. - run_command(['svn', 'revert'] + file_paths) + self.run(['svn', 'revert'] + file_paths) def should_squash(self, squash): # SVN doesn't support the concept of squashing. @@ -414,11 +424,11 @@ class SVN(SCM): svn_commit_args.extend(["--username", username]) svn_commit_args.extend(["-m", message]) # FIXME: Should this use cwd=self.checkout_root? - return run_command(svn_commit_args, error_handler=commit_error_handler) + return self.run(svn_commit_args, error_handler=commit_error_handler) def svn_commit_log(self, svn_revision): svn_revision = self.strip_r_from_svn_revision(svn_revision) - return run_command(['svn', 'log', '--non-interactive', '--revision', svn_revision]); + return self.run(['svn', 'log', '--non-interactive', '--revision', svn_revision]) def last_svn_commit_log(self): # BASE is the checkout revision, HEAD is the remote repository revision @@ -455,30 +465,30 @@ class Git(SCM): def discard_local_commits(self): # FIXME: This should probably use cwd=self.checkout_root - run_command(['git', 'reset', '--hard', self.svn_branch_name()]) + self.run(['git', 'reset', '--hard', self.svn_branch_name()]) def local_commits(self): # FIXME: This should probably use cwd=self.checkout_root - return run_command(['git', 'log', '--pretty=oneline', 'HEAD...' + self.svn_branch_name()]).splitlines() + return self.run(['git', 'log', '--pretty=oneline', 'HEAD...' + self.svn_branch_name()]).splitlines() def rebase_in_progress(self): return os.path.exists(os.path.join(self.checkout_root, '.git/rebase-apply')) def working_directory_is_clean(self): # FIXME: This should probably use cwd=self.checkout_root - return run_command(['git', 'diff', 'HEAD', '--name-only']) == "" + return self.run(['git', 'diff', 'HEAD', '--name-only']) == "" def clean_working_directory(self): # FIXME: These should probably use cwd=self.checkout_root. # Could run git clean here too, but that wouldn't match working_directory_is_clean - run_command(['git', 'reset', '--hard', 'HEAD']) + self.run(['git', 'reset', '--hard', 'HEAD']) # Aborting rebase even though this does not match working_directory_is_clean if self.rebase_in_progress(): - run_command(['git', 'rebase', '--abort']) + self.run(['git', 'rebase', '--abort']) def status_command(self): # git status returns non-zero when there are changes, so we use git diff name --name-status HEAD instead. - # No file contents printed, thus utf-8 autodecoding in run_command is fine. + # No file contents printed, thus utf-8 autodecoding in self.run is fine. return ["git", "diff", "--name-status", "HEAD"] def _status_regexp(self, expected_types): @@ -486,7 +496,7 @@ class Git(SCM): def add(self, path): # path is assumed to be cwd relative? - run_command(["git", "add", path]) + self.run(["git", "add", path]) def _merge_base(self, git_commit, squash): if git_commit: @@ -510,7 +520,7 @@ class Git(SCM): def _changes_files_for_commit(self, git_commit): # --pretty="format:" makes git show not print the commit log header, - changed_files = run_command(["git", "show", "--pretty=format:", "--name-only", git_commit]).splitlines() + changed_files = self.run(["git", "show", "--pretty=format:", "--name-only", git_commit]).splitlines() # instead it just prints a blank line at the top, so we skip the blank line: return changed_files[1:] @@ -539,7 +549,7 @@ class Git(SCM): Patch files are effectively binary since they may contain files of multiple different encodings.""" # FIXME: This should probably use cwd=self.checkout_root - return run_command(['git', 'diff', '--binary', "--no-ext-diff", "--full-index", "-M", self._merge_base(git_commit, squash)], decode_output=False) + return self.run(['git', 'diff', '--binary', "--no-ext-diff", "--full-index", "-M", self._merge_base(git_commit, squash)], decode_output=False) @classmethod def git_commit_from_svn_revision(cls, revision): @@ -553,7 +563,7 @@ class Git(SCM): def contents_at_revision(self, path, revision): """Returns a byte array (str()) containing the contents of path @ revision in the repository.""" - return run_command(["git", "show", "%s:%s" % (self.git_commit_from_svn_revision(revision), path)], decode_output=False) + return self.run(["git", "show", "%s:%s" % (self.git_commit_from_svn_revision(revision), path)], decode_output=False) def diff_for_revision(self, revision): git_commit = self.git_commit_from_svn_revision(revision) @@ -561,7 +571,7 @@ class Git(SCM): def committer_email_for_revision(self, revision): git_commit = self.git_commit_from_svn_revision(revision) - committer_email = run_command(["git", "log", "-1", "--pretty=format:%ce", git_commit]) + committer_email = self.run(["git", "log", "-1", "--pretty=format:%ce", git_commit]) # Git adds an extra @repository_hash to the end of every committer email, remove it: return committer_email.rsplit("@", 1)[0] @@ -569,10 +579,10 @@ class Git(SCM): # Assume the revision is an svn revision. git_commit = self.git_commit_from_svn_revision(revision) # I think this will always fail due to ChangeLogs. - run_command(['git', 'revert', '--no-commit', git_commit], error_handler=Executive.ignore_error) + self.run(['git', 'revert', '--no-commit', git_commit], error_handler=Executive.ignore_error) def revert_files(self, file_paths): - run_command(['git', 'checkout', 'HEAD'] + file_paths) + self.run(['git', 'checkout', 'HEAD'] + file_paths) def should_squash(self, squash): if squash is not None: @@ -607,7 +617,7 @@ class Git(SCM): squash = self.should_squash(squash) if squash: - run_command(['git', 'reset', '--soft', self.svn_branch_name()]) + self.run(['git', 'reset', '--soft', self.svn_branch_name()]) self.commit_locally_with_message(message) elif not self.working_directory_is_clean(): if not len(self.local_commits()): @@ -627,7 +637,7 @@ class Git(SCM): return self.push_local_commits_to_server() def _commit_on_branch(self, message, git_commit): - branch_ref = run_command(['git', 'symbolic-ref', 'HEAD']).strip() + branch_ref = self.run(['git', 'symbolic-ref', 'HEAD']).strip() branch_name = branch_ref.replace('refs/heads/', '') commit_ids = self.commit_ids_from_commitish_arguments([git_commit]) @@ -645,16 +655,16 @@ class Git(SCM): # We wrap in a try...finally block so if anything goes wrong, we clean up the branches. commit_succeeded = True try: - run_command(['git', 'checkout', '-q', '-b', MERGE_BRANCH, self.svn_branch_name()]) + self.run(['git', 'checkout', '-q', '-b', MERGE_BRANCH, self.svn_branch_name()]) for commit in commit_ids: # We're on a different branch now, so convert "head" to the branch name. commit = re.sub(r'(?i)head', branch_name, commit) # FIXME: Once changed_files and create_patch are modified to separately handle each # commit in a commit range, commit each cherry pick so they'll get dcommitted separately. - run_command(['git', 'cherry-pick', '--no-commit', commit]) + self.run(['git', 'cherry-pick', '--no-commit', commit]) - run_command(['git', 'commit', '-m', message]) + self.run(['git', 'commit', '-m', message]) output = self.push_local_commits_to_server() except Exception, e: log("COMMIT FAILED: " + str(e)) @@ -663,26 +673,26 @@ class Git(SCM): finally: # And then swap back to the original branch and clean up. self.clean_working_directory() - run_command(['git', 'checkout', '-q', branch_name]) + self.run(['git', 'checkout', '-q', branch_name]) self.delete_branch(MERGE_BRANCH) return output def svn_commit_log(self, svn_revision): svn_revision = self.strip_r_from_svn_revision(svn_revision) - return run_command(['git', 'svn', 'log', '-r', svn_revision]) + return self.run(['git', 'svn', 'log', '-r', svn_revision]) def last_svn_commit_log(self): - return run_command(['git', 'svn', 'log', '--limit=1']) + return self.run(['git', 'svn', 'log', '--limit=1']) # Git-specific methods: def delete_branch(self, branch): - if run_command(['git', 'show-ref', '--quiet', '--verify', 'refs/heads/' + branch], return_exit_code=True) == 0: - run_command(['git', 'branch', '-D', branch]) + if self.run(['git', 'show-ref', '--quiet', '--verify', 'refs/heads/' + branch], return_exit_code=True) == 0: + self.run(['git', 'branch', '-D', branch]) def svn_merge_base(self): - return run_command(['git', 'merge-base', self.svn_branch_name(), 'HEAD']).strip() + return self.run(['git', 'merge-base', self.svn_branch_name(), 'HEAD']).strip() def svn_branch_name(self): # FIXME: This should so something like: Git.read_git_config('svn-remote.svn.fetch').split(':')[1] @@ -690,13 +700,13 @@ class Git(SCM): return 'trunk' def commit_locally_with_message(self, message): - run_command(['git', 'commit', '--all', '-F', '-'], input=message) + self.run(['git', 'commit', '--all', '-F', '-'], input=message) def push_local_commits_to_server(self): dcommit_command = ['git', 'svn', 'dcommit'] if self.dryrun: dcommit_command.append('--dry-run') - output = run_command(dcommit_command, error_handler=commit_error_handler) + output = self.run(dcommit_command, error_handler=commit_error_handler) # Return a string which looks like a commit so that things which parse this output will succeed. if self.dryrun: output += "\nCommitted r0" @@ -716,14 +726,14 @@ class Git(SCM): if '...' in commitish: raise ScriptError(message="'...' is not supported (found in '%s'). Did you mean '..'?" % commitish) elif '..' in commitish: - commit_ids += reversed(run_command(['git', 'rev-list', commitish]).splitlines()) + commit_ids += reversed(self.run(['git', 'rev-list', commitish]).splitlines()) else: # Turn single commits or branch or tag names into commit ids. - commit_ids += run_command(['git', 'rev-parse', '--revs-only', commitish]).splitlines() + commit_ids += self.run(['git', 'rev-parse', '--revs-only', commitish]).splitlines() return commit_ids def commit_message_for_local_commit(self, commit_id): - commit_lines = run_command(['git', 'cat-file', 'commit', commit_id]).splitlines() + commit_lines = self.run(['git', 'cat-file', 'commit', commit_id]).splitlines() # Skip the git headers. first_line_after_headers = 0 @@ -734,4 +744,4 @@ class Git(SCM): return CommitMessage(commit_lines[first_line_after_headers:]) def files_changed_summary_for_commit(self, commit_id): - return run_command(['git', 'diff-tree', '--shortstat', '--no-commit-id', commit_id]) + return self.run(['git', 'diff-tree', '--shortstat', '--no-commit-id', commit_id]) diff --git a/WebKitTools/Scripts/webkitpy/common/config/__init__.py b/WebKitTools/Scripts/webkitpy/common/config/__init__.py index 03f1bc7..62d129e 100644 --- a/WebKitTools/Scripts/webkitpy/common/config/__init__.py +++ b/WebKitTools/Scripts/webkitpy/common/config/__init__.py @@ -3,5 +3,4 @@ import re codereview_server_host = "wkrietveld.appspot.com" -codereview_server_regex = "https?://%s/" % re.sub('\.', '\\.', codereview_server_host) codereview_server_url = "https://%s/" % codereview_server_host diff --git a/WebKitTools/Scripts/webkitpy/common/config/committers.py b/WebKitTools/Scripts/webkitpy/common/config/committers.py index 56887ab..c33d2a6 100644 --- a/WebKitTools/Scripts/webkitpy/common/config/committers.py +++ b/WebKitTools/Scripts/webkitpy/common/config/committers.py @@ -116,6 +116,7 @@ committers_unable_to_review = [ Committer("James Hawkins", ["jhawkins@chromium.org", "jhawkins@google.com"], "jhawkins"), Committer("James Robinson", ["jamesr@chromium.org", "jamesr@google.com"]), Committer("Jens Alfke", ["snej@chromium.org", "jens@apple.com"]), + Committer("Jer Noble", "jer.noble@apple.com", "jernoble"), Committer("Jeremy Moskovich", ["playmobil@google.com", "jeremy@chromium.org"], "jeremymos"), Committer("Jessie Berlin", ["jberlin@webkit.org", "jberlin@apple.com"]), Committer("Jesus Sanchez-Palencia", ["jesus@webkit.org", "jesus.palencia@openbossa.org"], "jeez_"), @@ -128,7 +129,6 @@ committers_unable_to_review = [ Committer("Keishi Hattori", "keishi@webkit.org", "keishi"), Committer("Kelly Norton", "knorton@google.com"), Committer("Kenneth Russell", "kbr@google.com"), - Committer("Kent Tamura", "tkent@chromium.org", "tkent"), Committer("Kinuko Yasuda", "kinuko@chromium.org", "kinuko"), Committer("Krzysztof Kowalczyk", "kkowalczyk@gmail.com"), Committer("Levi Weintraub", "lweintraub@apple.com"), @@ -144,10 +144,9 @@ committers_unable_to_review = [ Committer("Mike Thole", ["mthole@mikethole.com", "mthole@apple.com"]), Committer("Mikhail Naganov", "mnaganov@chromium.org"), Committer("MORITA Hajime", "morrita@google.com", "morrita"), - Committer("Ojan Vafai", "ojan@chromium.org", "ojan"), Committer("Pam Greene", "pam@chromium.org", "pamg"), Committer("Peter Kasting", ["pkasting@google.com", "pkasting@chromium.org"], "pkasting"), - Committer("Philippe Normand", ["pnormand@igalia.com", "philn@webkit.org"], "pnormand"), + Committer("Philippe Normand", ["pnormand@igalia.com", "philn@webkit.org"], "philn-tp"), Committer("Pierre d'Herbemont", ["pdherbemont@free.fr", "pdherbemont@apple.com"], "pdherbemont"), Committer("Pierre-Olivier Latour", "pol@apple.com", "pol"), Committer("Robert Hogan", ["robert@webkit.org", "robert@roberthogan.net"], "mwenge"), @@ -222,6 +221,7 @@ reviewers_list = [ Reviewer("Justin Garcia", "justin.garcia@apple.com", "justing"), Reviewer("Ken Kocienda", "kocienda@apple.com"), Reviewer("Kenneth Rohde Christiansen", ["kenneth@webkit.org", "kenneth.christiansen@openbossa.org"], "kenne"), + Reviewer("Kent Tamura", "tkent@chromium.org", "tkent"), Reviewer("Kevin Decker", "kdecker@apple.com", "superkevin"), Reviewer("Kevin McCullough", "kmccullough@apple.com", "maculloch"), Reviewer("Kevin Ollivier", ["kevino@theolliviers.com", "kevino@webkit.org"], "kollivier"), @@ -231,6 +231,7 @@ reviewers_list = [ Reviewer("Mark Rowe", "mrowe@apple.com", "bdash"), Reviewer("Nate Chapin", "japhet@chromium.org", "japhet"), Reviewer("Nikolas Zimmermann", ["zimmermann@kde.org", "zimmermann@physik.rwth-aachen.de", "zimmermann@webkit.org"], "wildfox"), + Reviewer("Ojan Vafai", "ojan@chromium.org", "ojan"), Reviewer("Oliver Hunt", "oliver@apple.com", "olliej"), Reviewer("Pavel Feldman", "pfeldman@chromium.org", "pfeldman"), Reviewer("Richard Williamson", "rjw@apple.com", "rjw"), diff --git a/WebKitTools/Scripts/webkitpy/common/config/ports.py b/WebKitTools/Scripts/webkitpy/common/config/ports.py index a881a67..9d4ac3f 100644 --- a/WebKitTools/Scripts/webkitpy/common/config/ports.py +++ b/WebKitTools/Scripts/webkitpy/common/config/ports.py @@ -112,6 +112,16 @@ class MacPort(WebKitPort): def flag(cls): return "--port=mac" + @classmethod + def _system_version(cls): + version_string = platform.mac_ver()[0] # e.g. "10.5.6" + version_tuple = version_string.split('.') + return map(int, version_tuple) + + @classmethod + def is_leopard(cls): + return tuple(cls._system_version()[:2]) == (10, 5) + class WinPort(WebKitPort): diff --git a/WebKitTools/Scripts/webkitpy/common/config/ports_unittest.py b/WebKitTools/Scripts/webkitpy/common/config/ports_unittest.py index c42d2d0..42c4f2d 100644 --- a/WebKitTools/Scripts/webkitpy/common/config/ports_unittest.py +++ b/WebKitTools/Scripts/webkitpy/common/config/ports_unittest.py @@ -41,6 +41,12 @@ class WebKitPortTest(unittest.TestCase): self.assertEquals(MacPort.build_webkit_command(build_style="debug"), [WebKitPort.script_path("build-webkit"), "--debug"]) self.assertEquals(MacPort.build_webkit_command(build_style="release"), [WebKitPort.script_path("build-webkit"), "--release"]) + class TestIsLeopard(MacPort): + @classmethod + def _system_version(cls): + return [10, 5] + self.assertTrue(TestIsLeopard.is_leopard()) + def test_gtk_port(self): self.assertEquals(GtkPort.name(), "Gtk") self.assertEquals(GtkPort.flag(), "--port=gtk") diff --git a/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py b/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py index 4311a00..074a021 100644 --- a/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py +++ b/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py @@ -856,9 +856,13 @@ class Bugzilla(object): possible_bug_statuses = map(lambda item: item.name, bug_status.items) if "REOPENED" in possible_bug_statuses: bug_status.value = ["REOPENED"] + # If the bug was never confirmed it will not have a "REOPENED" + # state, but only an "UNCONFIRMED" state. + elif "UNCONFIRMED" in possible_bug_statuses: + bug_status.value = ["UNCONFIRMED"] else: - log("Did not reopen bug %s. " + - "It appears to already be open with status %s." % ( - bug_id, bug_status.value)) + # FIXME: This logic is slightly backwards. We won't print this + # message if the bug is already open with state "UNCONFIRMED". + log("Did not reopen bug %s, it appears to already be open with status %s." % (bug_id, bug_status.value)) self.browser['comment'] = comment_text self.browser.submit() diff --git a/WebKitTools/Scripts/webkitpy/common/net/bugzilla_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/bugzilla_unittest.py index 4c44cdf..62a0746 100644 --- a/WebKitTools/Scripts/webkitpy/common/net/bugzilla_unittest.py +++ b/WebKitTools/Scripts/webkitpy/common/net/bugzilla_unittest.py @@ -259,6 +259,35 @@ ZEZpbmlzaExvYWRXaXRoUmVhc29uOnJlYXNvbl07Cit9CisKIEBlbmQKIAogI2VuZGlmCg== expected_stderr = "Adding ['adam@example.com'] to the CC list for bug 42\n" OutputCapture().assert_outputs(self, bugzilla.add_cc_to_bug, [42, ["adam@example.com"]], expected_stderr=expected_stderr) + def _mock_control_item(self, name): + mock_item = Mock() + mock_item.name = name + return mock_item + + def _mock_find_control(self, item_names=[], selected_index=0): + mock_control = Mock() + mock_control.items = [self._mock_control_item(name) for name in item_names] + mock_control.value = [item_names[selected_index]] if item_names else None + return lambda name, type: mock_control + + def _assert_reopen(self, item_names=None, selected_index=None, extra_stderr=None): + bugzilla = Bugzilla() + bugzilla.browser = MockBrowser() + bugzilla.authenticate = lambda: None + + mock_find_control = self._mock_find_control(item_names, selected_index) + bugzilla.browser.find_control = mock_find_control + expected_stderr = "Re-opening bug 42\n['comment']\n" + if extra_stderr: + expected_stderr += extra_stderr + OutputCapture().assert_outputs(self, bugzilla.reopen_bug, [42, ["comment"]], expected_stderr=expected_stderr) + + def test_reopen_bug(self): + self._assert_reopen(item_names=["REOPENED", "RESOLVED", "CLOSED"], selected_index=1) + self._assert_reopen(item_names=["UNCONFIRMED", "RESOLVED", "CLOSED"], selected_index=1) + extra_stderr = "Did not reopen bug 42, it appears to already be open with status ['NEW'].\n" + self._assert_reopen(item_names=["NEW", "RESOLVED"], selected_index=0, extra_stderr=extra_stderr) + class BugzillaQueriesTest(unittest.TestCase): _sample_request_page = """ @@ -341,7 +370,3 @@ class BugzillaQueriesTest(unittest.TestCase): def test_load_query(self): queries = BugzillaQueries(Mock()) queries._load_query("request.cgi?action=queue&type=review&group=type") - - -if __name__ == '__main__': - unittest.main() diff --git a/WebKitTools/Scripts/webkitpy/common/net/rietveld.py b/WebKitTools/Scripts/webkitpy/common/net/rietveld.py index 9cc97f2..c0d6119 100644 --- a/WebKitTools/Scripts/webkitpy/common/net/rietveld.py +++ b/WebKitTools/Scripts/webkitpy/common/net/rietveld.py @@ -37,16 +37,6 @@ from webkitpy.common.system.executive import ScriptError import webkitpy.thirdparty.autoinstalled.rietveld.upload as upload -def parse_codereview_issue(message): - if not message: - return None - match = re.search(config.codereview_server_regex + - "(?P<codereview_issue>\d+)", - message) - if match: - return int(match.group('codereview_issue')) - - class Rietveld(object): def __init__(self, executive, dryrun=False): self.dryrun = dryrun diff --git a/WebKitTools/Scripts/webkitpy/common/system/executive.py b/WebKitTools/Scripts/webkitpy/common/system/executive.py index 11eb051..c7a7aec 100644 --- a/WebKitTools/Scripts/webkitpy/common/system/executive.py +++ b/WebKitTools/Scripts/webkitpy/common/system/executive.py @@ -33,6 +33,8 @@ try: except ImportError: multiprocessing = None +import errno +import logging import os import platform import StringIO @@ -43,6 +45,9 @@ import sys from webkitpy.common.system.deprecated_logging import tee +_log = logging.getLogger("webkitpy.common.system") + + class ScriptError(Exception): def __init__(self, @@ -165,28 +170,48 @@ class Executive(object): def kill_process(self, pid): """Attempts to kill the given pid. Will fail silently if pid does not exist or insufficient permisssions.""" - if platform.system() == "Windows": - # According to http://docs.python.org/library/os.html - # os.kill isn't available on Windows. However, when I tried it - # using Cygwin, it worked fine. We should investigate whether - # we need this platform specific code here. - command = ["taskkill.exe", "/f", "/pid", str(pid)] - # taskkill will exit 128 if the process is not found. + if sys.platform == "win32": + # We only use taskkill.exe on windows (not cygwin) because subprocess.pid + # is a CYGWIN pid and taskkill.exe expects a windows pid. + # Thankfully os.kill on CYGWIN handles either pid type. + command = ["taskkill.exe", "/f", "/pid", pid] + # taskkill will exit 128 if the process is not found. We should log. self.run_command(command, error_handler=self.ignore_error) return - try: - os.kill(pid, signal.SIGKILL) - except OSError, e: - # FIXME: We should make non-silent failure an option. - pass + + # According to http://docs.python.org/library/os.html + # os.kill isn't available on Windows. python 2.5.5 os.kill appears + # to work in cygwin, however it occasionally raises EAGAIN. + retries_left = 3 if sys.platform == "cygwin" else 1 + while retries_left > 0: + try: + retries_left -= 1 + os.kill(pid, signal.SIGKILL) + except OSError, e: + if e.errno == errno.EAGAIN: + if retries_left <= 0: + _log.warn("Failed to kill pid %s. Too many EAGAIN errors." % pid) + continue + if e.errno == errno.ESRCH: # The process does not exist. + _log.warn("Called kill_process with a non-existant pid %s" % pid) + return + raise + + def _windows_image_name(self, process_name): + name, extension = os.path.splitext(process_name) + if not extension: + # taskkill expects processes to end in .exe + # If necessary we could add a flag to disable appending .exe. + process_name = "%s.exe" % name + return process_name def kill_all(self, process_name): """Attempts to kill processes matching process_name. Will fail silently if no process are found.""" - if platform.system() == "Windows": - # We might want to automatically append .exe? - command = ["taskkill.exe", "/f", "/im", process_name] - # taskkill will exit 128 if the process is not found. + if sys.platform in ("win32", "cygwin"): + image_name = self._windows_image_name(process_name) + command = ["taskkill.exe", "/f", "/im", image_name] + # taskkill will exit 128 if the process is not found. We should log. self.run_command(command, error_handler=self.ignore_error) return @@ -195,6 +220,9 @@ class Executive(object): # We should pick one mode, or add support for switching between them. # Note: Mac OS X 10.6 requires -SIGNALNAME before -u USER command = ["killall", "-TERM", "-u", os.getenv("USER"), process_name] + # killall returns 1 if no process can be found and 2 on command error. + # FIXME: We should pass a custom error_handler to allow only exit_code 1. + # We should log in exit_code == 1 self.run_command(command, error_handler=self.ignore_error) # Error handlers do not need to be static methods once all callers are diff --git a/WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py index ce91269..30468ce 100644 --- a/WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py +++ b/WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py @@ -72,22 +72,44 @@ class ExecutiveTest(unittest.TestCase): def test_kill_process(self): executive = Executive() - # FIXME: This may need edits to work right on windows. # We use "yes" because it loops forever. process = subprocess.Popen(["yes"], stdout=subprocess.PIPE) self.assertEqual(process.poll(), None) # Process is running executive.kill_process(process.pid) - self.assertEqual(process.wait(), -signal.SIGKILL) + # Note: Can't use a ternary since signal.SIGKILL is undefined for sys.platform == "win32" + if sys.platform == "win32": + expected_exit_code = 0 # taskkill.exe results in exit(0) + else: + expected_exit_code = -signal.SIGKILL + self.assertEqual(process.wait(), expected_exit_code) # Killing again should fail silently. executive.kill_process(process.pid) + def _assert_windows_image_name(self, name, expected_windows_name): + executive = Executive() + windows_name = executive._windows_image_name(name) + self.assertEqual(windows_name, expected_windows_name) + + def test_windows_image_name(self): + self._assert_windows_image_name("foo", "foo.exe") + self._assert_windows_image_name("foo.exe", "foo.exe") + self._assert_windows_image_name("foo.com", "foo.com") + # If the name looks like an extension, even if it isn't + # supposed to, we have no choice but to return the original name. + self._assert_windows_image_name("foo.baz", "foo.baz") + self._assert_windows_image_name("foo.baz.exe", "foo.baz.exe") + def test_kill_all(self): executive = Executive() - # FIXME: This may need edits to work right on windows. # We use "yes" because it loops forever. process = subprocess.Popen(["yes"], stdout=subprocess.PIPE) self.assertEqual(process.poll(), None) # Process is running executive.kill_all("yes") - self.assertEqual(process.wait(), -signal.SIGTERM) + # Note: Can't use a ternary since signal.SIGTERM is undefined for sys.platform == "win32" + if sys.platform in ("win32", "cygwin"): + expected_exit_code = 0 # taskkill.exe results in exit(0) + else: + expected_exit_code = -signal.SIGTERM + self.assertEqual(process.wait(), expected_exit_code) # Killing again should fail silently. executive.kill_all("yes") diff --git a/WebKitTools/Scripts/webkitpy/common/system/user.py b/WebKitTools/Scripts/webkitpy/common/system/user.py index 64995bb..edce93d 100644 --- a/WebKitTools/Scripts/webkitpy/common/system/user.py +++ b/WebKitTools/Scripts/webkitpy/common/system/user.py @@ -26,17 +26,27 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import logging import os import shlex import subprocess import webbrowser + +_log = logging.getLogger("webkitpy.common.system.user") + + try: import readline except ImportError: - print "Unable to import readline. If you're using MacPorts, try running:" - print " sudo port install py25-readline" - exit(1) + if sys.platform != "win32": + # There is no readline module for win32, not much to do except cry. + _log.warn("Unable to import readline.") + # FIXME: We could give instructions for non-mac platforms. + # Lack of readline results in a very bad user experiance. + if sys.platform == "mac": + _log.warn("If you're using MacPorts, try running:") + _log.warn(" sudo port install py25-readline") class User(object): diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream.py index 9c42d73..20646a1 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream.py @@ -137,12 +137,10 @@ class MeteredStream: # Print the necessary number of backspaces to erase the previous # message. if len(self._last_update): - self._stream.write("\b" * len(self._last_update)) - if len(str): - self._stream.write(str) - num_remaining = len(self._last_update) - len(str) - if num_remaining > 0: - self._stream.write(" " * num_remaining + "\b" * num_remaining) + self._stream.write("\b" * len(self._last_update) + + " " * len(self._last_update) + + "\b" * len(self._last_update)) + self._stream.write(str) last_newline = str.rfind("\n") self._last_update = str[(last_newline + 1):] self._dirty = True diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream_unittest.py index 926f9b3..a9c6d5b 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream_unittest.py @@ -50,32 +50,36 @@ class TestMeteredStream(unittest.TestCase): # for coverage. m.write("foo") m.flush() - self.assertEquals(a.get(), ['foo']) + exp = ['foo'] + self.assertEquals(a.get(), exp) # now check that a second write() does not overwrite the first. m.write("bar") - self.assertEquals(a.get(), ['foo', 'bar']) + exp.append('bar') + self.assertEquals(a.get(), exp) m.update("batter") - self.assertEquals(a.get(), ['foo', 'bar', 'batter']) + exp.append('batter') + self.assertEquals(a.get(), exp) # The next update() should overwrite the laste update() but not the # other text. Note that the cursor is effectively positioned at the # end of 'foo', even though we had to erase three more characters. m.update("foo") - self.assertEquals(a.get(), ['foo', 'bar', 'batter', '\b\b\b\b\b\b', - 'foo', ' \b\b\b']) + exp.append('\b\b\b\b\b\b \b\b\b\b\b\b') + exp.append('foo') + self.assertEquals(a.get(), exp) m.progress("progress") - self.assertEquals(a.get(), ['foo', 'bar', 'batter', '\b\b\b\b\b\b', - 'foo', ' \b\b\b', '\b\b\b', 'progress']) + exp.append('\b\b\b \b\b\b') + exp.append('progress') + self.assertEquals(a.get(), exp) # now check that a write() does overwrite the progress bar m.write("foo") - self.assertEquals(a.get(), ['foo', 'bar', 'batter', '\b\b\b\b\b\b', - 'foo', ' \b\b\b', '\b\b\b', 'progress', - '\b\b\b\b\b\b\b\b', - 'foo', ' \b\b\b\b\b']) + exp.append('\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b') + exp.append('foo') + self.assertEquals(a.get(), exp) # Now test that we only back up to the most recent newline. @@ -84,7 +88,7 @@ class TestMeteredStream(unittest.TestCase): a.reset() m.update("foo\nbar") m.update("baz") - self.assertEquals(a.get(), ['foo\nbar', '\b\b\b', 'baz']) + self.assertEquals(a.get(), ['foo\nbar', '\b\b\b \b\b\b', 'baz']) def test_verbose(self): a = ArrayStream() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py index 91d49c6..77de2e0 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py @@ -56,6 +56,8 @@ the output at the beginning of the run, during the run, or at the end: Overall options: nothing don't print anything. This overrides every other option + default include the default options. This is useful for logging + the default options plus additional settings. everything print everything (except the trace-* options and the detailed-progress option, see below for the full list ) misc print miscellaneous things like blank lines @@ -101,15 +103,19 @@ Notes: --print 'everything' is equivalent to --print '%(everything)s'. -The default is to --print '%(default)s'. +The default (--print default) is equivalent to --print '%(default)s'. """ % {'slowest': NUM_SLOW_TESTS_TO_LOG, 'everything': PRINT_EVERYTHING, 'default': PRINT_DEFAULT} def print_options(): return [ - # Note: we use print_options rather than just 'print' because print + # Note: We use print_options rather than just 'print' because print # is a reserved word. + # Note: Also, we don't specify a default value so we can detect when + # no flag is specified on the command line and use different defaults + # based on whether or not --verbose is specified (since --print + # overrides --verbose). optparse.make_option("--print", dest="print_options", help=("controls print output of test run. " "Use --help-printing for more.")), @@ -171,6 +177,10 @@ def parse_print_options(print_options, verbose, child_processes, switches.discard('everything') switches.update(set(PRINT_EVERYTHING.split(','))) + if 'default' in switches: + switches.discard('default') + switches.update(set(PRINT_DEFAULT.split(','))) + if 'detailed-progress' in switches: switches.discard('one-line-progress') @@ -310,7 +320,7 @@ class Printer(object): png_file = self._port.expected_filename(filename, '.png') if os.path.exists(png_file): self._write(' png: %s' % - self._port.relative_test_filename(filename)) + self._port.relative_test_filename(png_file)) else: self._write(' png: <none>') self._write(' exp: %s' % exp_str) @@ -486,10 +496,8 @@ class Printer(object): # from the logger :(. if self._options.verbose: _log.info(msg) - elif msg == "": - self._meter.write("\n") else: - self._meter.write(msg) + self._meter.write("%s\n" % msg) # # Utility routines used by the Controller class diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing_unittest.py index 8e6aa8f..dba1194 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing_unittest.py @@ -98,6 +98,44 @@ class TestUtilityFunctions(unittest.TestCase): options, args = get_options([]) self.assertTrue(options is not None) + def test_parse_print_options(self): + def test_switches(args, verbose, child_processes, is_fully_parallel, + expected_switches_str): + options, args = get_options(args) + if expected_switches_str: + expected_switches = set(expected_switches_str.split(',')) + else: + expected_switches = set() + switches = printing.parse_print_options(options.print_options, + verbose, + child_processes, + is_fully_parallel) + self.assertEqual(expected_switches, switches) + + # test that we default to the default set of switches + test_switches([], False, 1, False, + printing.PRINT_DEFAULT) + + # test that verbose defaults to everything + test_switches([], True, 1, False, + printing.PRINT_EVERYTHING) + + # test that --print default does what it's supposed to + test_switches(['--print', 'default'], False, 1, False, + printing.PRINT_DEFAULT) + + # test that --print nothing does what it's supposed to + test_switches(['--print', 'nothing'], False, 1, False, + None) + + # test that --print everything does what it's supposed to + test_switches(['--print', 'everything'], False, 1, False, + printing.PRINT_EVERYTHING) + + # this tests that '--print X' overrides '--verbose' + test_switches(['--print', 'actual'], True, 1, False, + 'actual') + class Testprinter(unittest.TestCase): def get_printer(self, args=None, single_threaded=False, @@ -144,7 +182,7 @@ class Testprinter(unittest.TestCase): exp_bot = [message + "\n"] else: if exp_err is None: - exp_err = [message] + exp_err = [message + "\n"] if exp_bot is None: exp_bot = [] do_helper(method_name, 'nothing', 'hello', [], []) @@ -182,21 +220,21 @@ class Testprinter(unittest.TestCase): printer, err, out = self.get_printer(['--print', 'one-line-summary']) printer.print_one_line_summary(1, 1) - self.assertEquals(err.get(), ["All 1 tests ran as expected.", "\n"]) + self.assertEquals(err.get(), ["All 1 tests ran as expected.\n", "\n"]) printer, err, out = self.get_printer(['--print', 'everything']) printer.print_one_line_summary(1, 1) - self.assertEquals(err.get(), ["All 1 tests ran as expected.", "\n"]) + self.assertEquals(err.get(), ["All 1 tests ran as expected.\n", "\n"]) err.reset() printer.print_one_line_summary(2, 1) self.assertEquals(err.get(), - ["1 test ran as expected, 1 didn't:", "\n"]) + ["1 test ran as expected, 1 didn't:\n", "\n"]) err.reset() printer.print_one_line_summary(3, 2) self.assertEquals(err.get(), - ["2 tests ran as expected, 1 didn't:", "\n"]) + ["2 tests ran as expected, 1 didn't:\n", "\n"]) def test_print_test_result(self): result = get_result('foo.html') @@ -212,7 +250,7 @@ class Testprinter(unittest.TestCase): printer.print_test_result(result, expected=False, exp_str='', got_str='') self.assertEquals(err.get(), - [' foo.html -> unexpected pass']) + [' foo.html -> unexpected pass\n']) printer, err, out = self.get_printer(['--print', 'everything']) printer.print_test_result(result, expected=True, exp_str='', @@ -222,7 +260,7 @@ class Testprinter(unittest.TestCase): printer.print_test_result(result, expected=False, exp_str='', got_str='') self.assertEquals(err.get(), - [' foo.html -> unexpected pass']) + [' foo.html -> unexpected pass\n']) printer, err, out = self.get_printer(['--print', 'nothing']) printer.print_test_result(result, expected=False, exp_str='', @@ -318,7 +356,7 @@ class Testprinter(unittest.TestCase): err.reset() out.reset() printer.print_progress(rs, True, test_files) - self.assertEqual(err.get(), []) + self.assertEqual(err.get(), ['']) self.assertTrue(out.empty()) printer, err, out = self.get_printer( @@ -347,7 +385,7 @@ class Testprinter(unittest.TestCase): err.reset() out.reset() printer.print_progress(rs, True, test_files) - self.assertEqual(err.get(), []) + self.assertEqual(err.get(), ['']) self.assertTrue(out.empty()) def test_write(self): diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_memorizingfile.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results_uploader.py index 2de77ba..680b848 100644 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_memorizingfile.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results_uploader.py @@ -1,7 +1,5 @@ #!/usr/bin/env python -# -# Copyright 2009, Google Inc. -# All rights reserved. +# Copyright (C) 2010 Google Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -29,44 +27,45 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import mimetypes +import socket -"""Tests for memorizingfile module.""" - - -import StringIO -import unittest - -import config # This must be imported before mod_pywebsocket. -from mod_pywebsocket import memorizingfile +from webkitpy.common.net.networktransaction import NetworkTransaction +from webkitpy.thirdparty.autoinstalled.mechanize import Browser -class UtilTest(unittest.TestCase): - def check(self, memorizing_file, num_read, expected_list): - for unused in range(num_read): - memorizing_file.readline() - actual_list = memorizing_file.get_memorized_lines() - self.assertEqual(len(expected_list), len(actual_list)) - for expected, actual in zip(expected_list, actual_list): - self.assertEqual(expected, actual) +def get_mime_type(filename): + return mimetypes.guess_type(filename)[0] or "text/plain" - def test_get_memorized_lines(self): - memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO( - 'Hello\nWorld\nWelcome')) - self.check(memorizing_file, 3, ['Hello\n', 'World\n', 'Welcome']) - def test_get_memorized_lines_limit_memorized_lines(self): - memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO( - 'Hello\nWorld\nWelcome'), 2) - self.check(memorizing_file, 3, ['Hello\n', 'World\n']) +class TestResultsUploader: + def __init__(self, host): + self._host = host + self._browser = Browser() - def test_get_memorized_lines_empty_file(self): - memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO( - '')) - self.check(memorizing_file, 10, []) + def _upload_files(self, attrs, file_objs): + self._browser.open("http://%s/testfile/uploadform" % self._host) + self._browser.select_form("test_result_upload") + for (name, data) in attrs: + self._browser[name] = str(data) + for (filename, handle) in file_objs: + self._browser.add_file(handle, get_mime_type(filename), filename, + "file") -if __name__ == '__main__': - unittest.main() + self._browser.submit() + def upload(self, params, files, timeout_seconds): + orig_timeout = socket.getdefaulttimeout() + file_objs = [] + try: + file_objs = [(filename, open(path, "rb")) for (filename, path) + in files] -# vi:sts=4 sw=4 et + socket.setdefaulttimeout(timeout_seconds) + NetworkTransaction(timeout_seconds=timeout_seconds).run( + lambda: self._upload_files(params, file_objs)) + finally: + socket.setdefaulttimeout(orig_timeout) + for (filename, handle) in file_objs: + handle.close() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py index 25946af..a4cbe42 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py @@ -414,10 +414,17 @@ class Port(object): raise NotImplemented('Port.results_directory') def setup_test_run(self): - """This routine can be overridden to perform any port-specific - work that shouuld be done at the beginning of a test run.""" + """Perform port-specific work at the beginning of a test run.""" pass + def setup_environ_for_server(self): + """Perform port-specific work at the beginning of a server launch. + + Returns: + Operating-system's environment. + """ + return os.environ + def show_html_results_file(self, results_filename): """This routine should display the HTML file pointed at by results_filename in a users' browser.""" diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py index a01bd14..979e225 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py @@ -49,11 +49,8 @@ class ChromiumLinuxPort(chromium.ChromiumPort): chromium.ChromiumPort.__init__(self, port_name, options) def baseline_search_path(self): - return [self._webkit_baseline_path('chromium-linux'), - self._webkit_baseline_path('chromium-win'), - self._webkit_baseline_path('chromium'), - self._webkit_baseline_path('win'), - self._webkit_baseline_path('mac')] + port_names = ["chromium-linux", "chromium-win", "chromium", "win", "mac"] + return map(self._webkit_baseline_path, port_names) def check_build(self, needs_http): result = chromium.ChromiumPort.check_build(self, needs_http) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py index 4ead26f..ba67a3e 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py @@ -52,10 +52,8 @@ class ChromiumMacPort(chromium.ChromiumPort): chromium.ChromiumPort.__init__(self, port_name, options) def baseline_search_path(self): - return [self._webkit_baseline_path('chromium-mac'), - self._webkit_baseline_path('chromium'), - self._webkit_baseline_path('mac' + self.version()), - self._webkit_baseline_path('mac')] + port_names = ["chromium-mac", "chromium", "mac" + self.version(), "mac"] + return map(self._webkit_baseline_path, port_names) def check_build(self, needs_http): result = chromium.ChromiumPort.check_build(self, needs_http) @@ -87,6 +85,7 @@ class ChromiumMacPort(chromium.ChromiumPort): return 'mac' def version(self): + # FIXME: It's strange that this string is -version, not just version. os_version_string = platform.mac_ver()[0] # e.g. "10.5.6" if not os_version_string: return '-leopard' diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py index 302af86..ad78e61 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py @@ -43,22 +43,39 @@ class ChromiumWinPort(chromium.ChromiumPort): def __init__(self, port_name=None, options=None): if port_name is None: - port_name = 'chromium-win' + self.version() - if options and not hasattr(options, 'configuration'): - options.configuration = 'Release' + port_name = "chromium-win" + self.version() + if options and not hasattr(options, "configuration"): + options.configuration = "Release" chromium.ChromiumPort.__init__(self, port_name, options) + def setup_environ_for_server(self): + env = chromium.ChromiumPort.setup_environ_for_server(self) + # Put the cygwin directory first in the path to find cygwin1.dll. + env["PATH"] = "%s;%s" % ( + self.path_from_chromium_base("third_party", "cygwin", "bin"), + env["PATH"]) + # Configure the cygwin directory so that pywebsocket finds proper + # python executable to run cgi program. + env["CYGWIN_PATH"] = self.path_from_chromium_base( + "third_party", "cygwin", "bin") + if (sys.platform == "win32" and self._options and + hasattr(self._options, "register_cygwin") and + self._options.register_cygwin): + setup_mount = self.path_from_chromium_base("third_party", + "cygwin", + "setup_mount.bat") + self._executive.run_command(setup_mount) + return env + def baseline_search_path(self): - dirs = [] + port_names = [] if self._name == 'chromium-win-xp': - dirs.append(self._webkit_baseline_path('chromium-win-xp')) + port_names.append("chromium-win-xp") if self._name in ('chromium-win-xp', 'chromium-win-vista'): - dirs.append(self._webkit_baseline_path('chromium-win-vista')) - dirs.append(self._webkit_baseline_path('chromium-win')) - dirs.append(self._webkit_baseline_path('chromium')) - dirs.append(self._webkit_baseline_path('win')) - dirs.append(self._webkit_baseline_path('mac')) - return dirs + port_names.append("chromium-win-vista") + # FIXME: This may need to include mac-snowleopard like win.py. + port_names.extend(["chromium-win", "chromium", "win", "mac"]) + return map(self._webkit_baseline_path, port_names) def check_build(self, needs_http): result = chromium.ChromiumPort.check_build(self, needs_http) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py index 2cbb1b9..2c92865 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py @@ -46,6 +46,8 @@ from __future__ import with_statement +import sys + import base import factory @@ -172,7 +174,11 @@ class DryrunDriver(base.Driver): test = uri if uri.startswith("file:///"): - test = test.replace('file://', '') + if sys.platform == 'win32': + test = test.replace('file:///', '') + test = test.replace('/', '\\') + else: + test = test.replace('file://', '') return test elif uri.startswith("http://127.0.0.1:8880/"): # websocket tests diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py new file mode 100644 index 0000000..d8dffdf --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py @@ -0,0 +1,138 @@ +# Copyright (C) 2010 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import sys +import unittest + +import chromium_linux +import chromium_mac +import chromium_win +import dryrun +import factory +import gtk +import mac +import qt +import test +import win + + +class FactoryTest(unittest.TestCase): + """Test factory creates proper port object for the target. + + Target is specified by port_name, sys.platform and options. + + """ + # FIXME: The ports themselves should expose what options they require, + # instead of passing generic "options". + + class WebKitOptions(object): + """Represents the minimum options for WebKit port.""" + def __init__(self): + self.pixel_tests = False + + class ChromiumOptions(WebKitOptions): + """Represents minimum options for Chromium port.""" + def __init__(self): + FactoryTest.WebKitOptions.__init__(self) + self.chromium = True + + def setUp(self): + self.real_sys_platform = sys.platform + self.webkit_options = FactoryTest.WebKitOptions() + self.chromium_options = FactoryTest.ChromiumOptions() + + def tearDown(self): + sys.platform = self.real_sys_platform + + def assert_port(self, port_name, expected_port): + """Helper assert for port_name. + + Args: + port_name: port name to get port object. + expected_port: class of expected port object. + + """ + self.assertTrue(isinstance(factory.get(port_name=port_name), + expected_port)) + + def assert_platform_port(self, platform, options, expected_port): + """Helper assert for platform and options. + + Args: + platform: sys.platform. + options: options to get port object. + expected_port: class of expected port object. + + """ + orig_platform = sys.platform + sys.platform = platform + self.assertTrue(isinstance(factory.get(options=options), + expected_port)) + sys.platform = orig_platform + + def test_test(self): + self.assert_port("test", test.TestPort) + + def test_dryrun(self): + self.assert_port("dryrun-test", dryrun.DryRunPort) + self.assert_port("dryrun-mac", dryrun.DryRunPort) + + def test_mac(self): + self.assert_port("mac", mac.MacPort) + self.assert_platform_port("darwin", None, mac.MacPort) + self.assert_platform_port("darwin", self.webkit_options, mac.MacPort) + + def test_win(self): + self.assert_port("win", win.WinPort) + self.assert_platform_port("win32", None, win.WinPort) + self.assert_platform_port("win32", self.webkit_options, win.WinPort) + self.assert_platform_port("cygwin", None, win.WinPort) + self.assert_platform_port("cygwin", self.webkit_options, win.WinPort) + + def test_gtk(self): + self.assert_port("gtk", gtk.GtkPort) + + def test_qt(self): + self.assert_port("qt", qt.QtPort) + + def test_chromium_mac(self): + self.assert_port("chromium-mac", chromium_mac.ChromiumMacPort) + self.assert_platform_port("darwin", self.chromium_options, + chromium_mac.ChromiumMacPort) + + def test_chromium_linux(self): + self.assert_port("chromium-linux", chromium_linux.ChromiumLinuxPort) + self.assert_platform_port("linux2", self.chromium_options, + chromium_linux.ChromiumLinuxPort) + + def test_chromium_win(self): + self.assert_port("chromium-win", chromium_win.ChromiumWinPort) + self.assert_platform_port("win32", self.chromium_options, + chromium_win.ChromiumWinPort) + self.assert_platform_port("cygwin", self.chromium_options, + chromium_win.ChromiumWinPort) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server.py index fbe47e3..0f8a21e 100755 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server.py @@ -55,7 +55,7 @@ class HttpdNotStarted(Exception): class Lighttpd(http_server_base.HttpServerBase): def __init__(self, port_obj, output_dir, background=False, port=None, - root=None, register_cygwin=None, run_background=None): + root=None, run_background=None): """Args: output_dir: the absolute path to the layout test result directory """ @@ -65,7 +65,6 @@ class Lighttpd(http_server_base.HttpServerBase): self._process = None self._port = port self._root = root - self._register_cygwin = register_cygwin self._run_background = run_background if self._port: self._port = int(self._port) @@ -199,20 +198,7 @@ class Lighttpd(http_server_base.HttpServerBase): shutil.copyfile(os.path.join(module_path, lib_file), os.path.join(tmp_module_path, lib_file)) - # Put the cygwin directory first in the path to find cygwin1.dll - env = os.environ - if sys.platform in ('cygwin', 'win32'): - env['PATH'] = '%s;%s' % ( - self._port_obj.path_from_chromium_base('third_party', - 'cygwin', 'bin'), - env['PATH']) - - if sys.platform == 'win32' and self._register_cygwin: - setup_mount = self._port_obj.path_from_chromium_base('third_party', - 'cygwin', 'setup_mount.bat') - # FIXME: Should use Executive.run_command - subprocess.Popen(setup_mount).wait() - + env = self._port_obj.setup_environ_for_server() _log.debug('Starting http server') # FIXME: Should use Executive.run_command self._process = subprocess.Popen(start_cmd, env=env) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/mac.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/mac.py index 350b088..413b5f2 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/mac.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/mac.py @@ -58,15 +58,15 @@ class MacPort(WebKitPort): return child_processes def baseline_search_path(self): - dirs = [] + port_names = [] if self._name == 'mac-tiger': - dirs.append(self._webkit_baseline_path(self._name)) + port_names.append("mac-tiger") if self._name in ('mac-tiger', 'mac-leopard'): - dirs.append(self._webkit_baseline_path('mac-leopard')) + port_names.append("mac-leopard") if self._name in ('mac-tiger', 'mac-leopard', 'mac-snowleopard'): - dirs.append(self._webkit_baseline_path('mac-snowleopard')) - dirs.append(self._webkit_baseline_path('mac')) - return dirs + port_names.append("mac-snowleopard") + port_names.append("mac") + return map(self._webkit_baseline_path, port_names) def path_to_test_expectations_file(self): return self.path_from_webkit_base('LayoutTests', 'platform', diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py index ada83ce..2097ce7 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py @@ -294,13 +294,33 @@ class WebKitPort(base.Port): return self.test_base_platform_names() + ( 'mac-tiger', 'mac-leopard', 'mac-snowleopard') + def _configuration_file_path(self): + build_root = self._webkit_build_directory(["--top-level"]) + return os.path.join(build_root, "Configuration") + + # Easy override for unit tests + def _open_configuration_file(self): + configuration_path = self._configuration_file_path() + return codecs.open(configuration_path, "r", "utf-8") + + def _read_configuration(self): + try: + with self._open_configuration_file() as file: + return file.readline().rstrip() + except IOError, e: + return None + + # FIXME: This list may be incomplete as Apple has some sekret configs. + _RECOGNIZED_CONFIGURATIONS = ("Debug", "Release") + def default_configuration(self): - # This is a bit of a hack. This state exists in a much nicer form in - # perl-land. - configuration = ospath.relpath( - self._webkit_build_directory(["--configuration"]), - self._webkit_build_directory(["--top-level"])) - assert(configuration == "Debug" or configuration == "Release") + # FIXME: Unify this with webkitdir.pm configuration reading code. + configuration = self._read_configuration() + if not configuration: + configuration = "Release" + if configuration not in self._RECOGNIZED_CONFIGURATIONS: + _log.warn("Configuration \"%s\" found in %s is not a recognized value.\n" % (configuration, self._configuration_file_path())) + _log.warn("Scripts may fail. See 'set-webkit-configuration --help'.") return configuration def _webkit_build_directory(self, args): diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py index ad557bd..22ae780 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py @@ -46,6 +46,8 @@ import factory import http_server from webkitpy.common.system.executive import Executive +from webkitpy.thirdparty.autoinstalled.pywebsocket import mod_pywebsocket + _log = logging.getLogger("webkitpy.layout_tests.port.websocket_server") @@ -95,15 +97,13 @@ class PyWebSocket(http_server.Lighttpd): def __init__(self, port_obj, output_dir, port=_DEFAULT_WS_PORT, root=None, use_tls=False, - register_cygwin=True, pidfile=None): """Args: output_dir: the absolute path to the layout test result directory """ http_server.Lighttpd.__init__(self, port_obj, output_dir, port=_DEFAULT_WS_PORT, - root=root, - register_cygwin=register_cygwin) + root=root) self._output_dir = output_dir self._process = None self._port = port @@ -159,7 +159,8 @@ class PyWebSocket(http_server.Lighttpd): python_interp = sys.executable pywebsocket_base = os.path.join( os.path.dirname(os.path.dirname(os.path.dirname( - os.path.abspath(__file__)))), 'thirdparty', 'pywebsocket') + os.path.abspath(__file__)))), 'thirdparty', + 'autoinstalled', 'pywebsocket') pywebsocket_script = os.path.join(pywebsocket_base, 'mod_pywebsocket', 'standalone.py') start_cmd = [ @@ -185,21 +186,7 @@ class PyWebSocket(http_server.Lighttpd): start_cmd.extend(['-t', '-k', self._private_key, '-c', self._certificate]) - # Put the cygwin directory first in the path to find cygwin1.dll - env = os.environ - if sys.platform in ('cygwin', 'win32'): - env['PATH'] = '%s;%s' % ( - self._port_obj.path_from_chromium_base('third_party', - 'cygwin', 'bin'), - env['PATH']) - env['CYGWIN_PATH'] = self._port_obj.path_from_chromium_base( - 'third_party', 'cygwin', 'bin') - - if sys.platform == 'win32' and self._register_cygwin: - setup_mount = self._port_obj.path_from_chromium_base( - 'third_party', 'cygwin', 'setup_mount.bat') - subprocess.Popen(setup_mount).wait() - + env = self._port_obj.setup_environ_for_server() env['PYTHONPATH'] = (pywebsocket_base + os.path.pathsep + env.get('PYTHONPATH', '')) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/win.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/win.py index 3b7a817..e05a69d 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/win.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/win.py @@ -44,6 +44,11 @@ class WinPort(WebKitPort): port_name = 'win' WebKitPort.__init__(self, port_name, options) + def baseline_search_path(self): + # Based on code from old-run-webkit-tests expectedDirectoryForTest() + port_names = ["win", "mac-snowleopard", "mac"] + return map(self._webkit_baseline_path, port_names) + def _tests_for_other_platforms(self): # FIXME: This list could be dynamic based on platform name and # pushed into base.Port. diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py index 211ce93..f11b8a9 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py @@ -970,6 +970,28 @@ class HtmlGenerator(object): return 'Other' +def get_host_port_object(options): + """Return a port object for the platform we're running on.""" + # The only thing we really need on the host is a way to diff + # text files and image files, which means we need to check that some + # version of ImageDiff has been built. We will look for either Debug + # or Release versions of the default port on the platform. + options.configuration = "Release" + port_obj = port.get(None, options) + if not port_obj.check_image_diff(override_step=None, logging=False): + _log.debug('No release version of the image diff binary was found.') + options.configuration = "Debug" + port_obj = port.get(None, options) + if not port_obj.check_image_diff(override_step=None, logging=False): + _log.error('No version of image diff was found. Check your build.') + return None + else: + _log.debug('Found the debug version of the image diff binary.') + else: + _log.debug('Found the release version of the image diff binary.') + return port_obj + + def main(): """Main function to produce new baselines.""" @@ -1034,19 +1056,11 @@ def main(): '%(levelname)s %(message)s'), datefmt='%y%m%d %H:%M:%S') - # options.configuration is used by port to locate image_diff binary. - # Check the imgage_diff release binary, if it does not exist, - # fallback to debug. - options.configuration = "Release" - port_obj = port.get(None, options) - if not port_obj.check_image_diff(override_step=None, logging=False): - _log.debug('No release version image diff binary found.') - options.configuration = "Debug" - port_obj = port.get(None, options) - else: - _log.debug('Found release version image diff binary.') + host_port_obj = get_host_port_object(options) + if not host_port_obj: + sys.exit(1) - # Verify 'platforms' option is valid + # Verify 'platforms' option is valid. if not options.platforms: _log.error('Invalid "platforms" option. --platforms must be ' 'specified in order to rebaseline.') @@ -1071,7 +1085,8 @@ def main(): rebaselining_tests = set() backup = options.backup for platform in rebaseline_platforms: - rebaseliner = Rebaseliner(port_obj, target_port_obj, platform, options) + rebaseliner = Rebaseliner(host_port_obj, target_port_obj, + platform, options) _log.info('') log_dashed_string('Rebaseline started', platform) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests_unittest.py new file mode 100644 index 0000000..fa03238 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests_unittest.py @@ -0,0 +1,88 @@ +#!/usr/bin/python +# Copyright (C) 2010 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Unit tests for rebaseline_chromium_webkit_tests.py.""" + +import unittest + +from webkitpy.layout_tests import port +from webkitpy.layout_tests import rebaseline_chromium_webkit_tests + + +class MockPort(object): + def __init__(self, image_diff_exists): + self.image_diff_exists = image_diff_exists + + def check_image_diff(self, override_step, logging): + return self.image_diff_exists + + +class MockOptions(object): + def __init__(self): + self.configuration = None + + +def get_mock_get(config_expectations): + def mock_get(port_name, options): + return MockPort(config_expectations[options.configuration]) + return mock_get + + +class TestGetHostPortObject(unittest.TestCase): + def assert_result(self, release_present, debug_present, valid_port_obj): + # Tests whether we get a valid port object returned when we claim + # that Image diff is (or isn't) present in the two configs. + port.get = get_mock_get({'Release': release_present, + 'Debug': debug_present}) + options = MockOptions() + port_obj = rebaseline_chromium_webkit_tests.get_host_port_object( + options) + if valid_port_obj: + self.assertNotEqual(port_obj, None) + else: + self.assertEqual(port_obj, None) + + def test_get_host_port_object(self): + # Save the normal port.get() function for future testing. + old_get = port.get + + # Test whether we get a valid port object back for the four + # possible cases of having ImageDiffs built. It should work when + # there is at least one binary present. + self.assert_result(False, False, False) + self.assert_result(True, False, True) + self.assert_result(False, True, True) + self.assert_result(True, True, True) + + # Restore the normal port.get() function. + port.get = old_get + + +if __name__ == '__main__': + unittest.main() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py index 456c6f3..148174d 100755 --- a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py @@ -62,16 +62,17 @@ import sys import time import traceback -from layout_package import test_expectations +from layout_package import dump_render_tree_thread from layout_package import json_layout_results_generator from layout_package import printing +from layout_package import test_expectations from layout_package import test_failures -from layout_package import dump_render_tree_thread from layout_package import test_files +from layout_package import test_results_uploader from test_types import fuzzy_image_diff from test_types import image_diff -from test_types import test_type_base from test_types import text_diff +from test_types import test_type_base from webkitpy.common.system.executive import Executive from webkitpy.thirdparty import simplejson @@ -790,6 +791,9 @@ class TestRunner: self._write_json_files(unexpected_results, result_summary, individual_test_timings) + # Upload generated JSON files to appengine server. + self._upload_json_files() + # Write the summary to disk (results.html) and display it if requested. wrote_results = self._write_results_html_file(result_summary) if self._options.show_results and wrote_results: @@ -878,6 +882,31 @@ class TestRunner: _log.debug("Finished writing JSON files.") + def _upload_json_files(self): + if not self._options.test_results_server: + return + + _log.info("Uploading JSON files for builder: %s", + self._options.builder_name) + + attrs = [('builder', self._options.builder_name)] + json_files = ["expectations.json", "results.json"] + + files = [(file, os.path.join(self._options.results_directory, file)) + for file in json_files] + + uploader = test_results_uploader.TestResultsUploader( + self._options.test_results_server) + try: + # Set uploading timeout in case appengine server is having problem. + # 120 seconds are more than enough to upload test results. + uploader.upload(attrs, files, 120) + except Exception, err: + _log.error("Upload failed: %s" % err) + return + + _log.info("JSON files uploaded.") + def _print_expected_results_of_type(self, result_summary, result_type, result_type_str): """Print the number of the tests in a given result class. @@ -1289,11 +1318,11 @@ def run(port_obj, options, args, regular_output=sys.stderr, if options.print_last_failures or options.retest_last_failures: unexpected_results_filename = os.path.join( options.results_directory, "unexpected_results.json") - with open(unexpected_results_filename, "r", "utf-8") as file: + with codecs.open(unexpected_results_filename, "r", "utf-8") as file: results = simplejson.load(file) last_unexpected_results = results['tests'].keys() if options.print_last_failures: - print "\n".join(last_unexpected_results) + "\n" + printer.write("\n".join(last_unexpected_results) + "\n") return 0 if options.clobber_old_results: @@ -1608,6 +1637,9 @@ def parse_args(args=None): "webkit-rel.")), optparse.make_option("--build-number", default="DUMMY_BUILD_NUMBER", help=("The build number of the builder running this script.")), + optparse.make_option("--test-results-server", default="", + help=("If specified, upload results json files to this appengine " + "server.")), ] option_list = (configuration_options + print_options + diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py index cd72fa3..e050cba 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py @@ -78,15 +78,21 @@ class MainTest(unittest.TestCase): (res, buildbot_output, regular_output) = logging_run( ['--platform', 'test', '--print', 'config', '--child-processes', '1', 'fast/html']) - self.assertTrue('Running one DumpRenderTree' + self.assertTrue('Running one DumpRenderTree\n' in regular_output.get()) (res, buildbot_output, regular_output) = logging_run( ['--platform', 'test', '--print', 'config', '--child-processes', '2', 'fast/html']) - self.assertTrue('Running 2 DumpRenderTrees in parallel' + self.assertTrue('Running 2 DumpRenderTrees in parallel\n' in regular_output.get()) + def test_last_results(self): + passing_run(['--platform', 'test', 'fast/html']) + (res, buildbot_output, regular_output) = logging_run( + ['--platform', 'test', '--print-last-failures']) + self.assertEqual(regular_output.get(), ['\n\n']) + self.assertEqual(buildbot_output.get(), []) class TestRunnerTest(unittest.TestCase): @@ -112,16 +118,17 @@ class TestRunnerTest(unittest.TestCase): class DryrunTest(unittest.TestCase): def test_basics(self): + # FIXME: it's hard to know which platforms are safe to test; the + # chromium platforms require a chromium checkout, and the mac platform + # requires fcntl, so it can't be tested on win32, etc. There is + # probably a better way of handling this. + if sys.platform != "mac": + return self.assertTrue(passing_run(['--platform', 'dryrun', 'fast/html'])) - #self.assertTrue(passing_run(['--platform', 'dryrun-mac', - # 'fast/html'])) - #self.assertTrue(passing_run(['--platform', 'dryrun-chromium-mac', - # 'fast/html'])) - #self.assertTrue(passing_run(['--platform', 'dryrun-chromium-win', - # 'fast/html'])) - #self.assertTrue(passing_run(['--platform', 'dryrun-chromium-linux', - # 'fast/html'])) + self.assertTrue(passing_run(['--platform', 'dryrun-mac', + 'fast/html'])) + if __name__ == '__main__': unittest.main() diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/__init__.py b/WebKitTools/Scripts/webkitpy/thirdparty/__init__.py index c052f00..3642286 100644 --- a/WebKitTools/Scripts/webkitpy/thirdparty/__init__.py +++ b/WebKitTools/Scripts/webkitpy/thirdparty/__init__.py @@ -89,6 +89,11 @@ installer.install(url="http://iweb.dl.sourceforge.net/project/python-irclib/pyth installer.install(url="http://iweb.dl.sourceforge.net/project/python-irclib/python-irclib/0.4.8/python-irclib-0.4.8.zip", url_subpath="ircbot.py") +pywebsocket_dir = os.path.join(autoinstalled_dir, "pywebsocket") +installer = AutoInstaller(target_dir=pywebsocket_dir) +installer.install(url="http://pywebsocket.googlecode.com/files/mod_pywebsocket-0.5.tar.gz", + url_subpath="pywebsocket-0.5/src/mod_pywebsocket") + readme_path = os.path.join(autoinstalled_dir, "README") if not os.path.exists(readme_path): with codecs.open(readme_path, "w", "ascii") as file: diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/COPYING b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/COPYING deleted file mode 100644 index ab9d52d..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/COPYING +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2009, Google Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/MANIFEST.in b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/MANIFEST.in deleted file mode 100644 index 1925688..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include COPYING -include MANIFEST.in -include README -recursive-include example *.py -recursive-include mod_pywebsocket *.py -recursive-include test *.py diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README deleted file mode 100644 index 1f9f05f..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README +++ /dev/null @@ -1,6 +0,0 @@ -Install this package by: -$ python setup.py build -$ sudo python setup.py install - -Then read document by: -$ pydoc mod_pywebsocket diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README.webkit b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README.webkit deleted file mode 100644 index 83e3cee..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/README.webkit +++ /dev/null @@ -1,14 +0,0 @@ -This directory contains a copy of the pywebsocket Python module obtained -from the following location: - -http://code.google.com/p/pywebsocket/ - -This directory currently contains the following version: -0.4.9.2 - -The following modifications have been made to this local version: -minor updates in WebSocketRequestHandler.is_cgi - -More information on these local modifications can be found here: - -http://trac.webkit.org/browser/trunk/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py deleted file mode 100644 index 2b976e1..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_client.py +++ /dev/null @@ -1,209 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Web Socket Echo client. - -This is an example Web Socket client that talks with echo_wsh.py. -This may be useful for checking mod_pywebsocket installation. - -Note: -This code is far from robust, e.g., we cut corners in handshake. -""" - - -import codecs -from optparse import OptionParser -import socket -import sys - - -_TIMEOUT_SEC = 10 - -_DEFAULT_PORT = 80 -_DEFAULT_SECURE_PORT = 443 -_UNDEFINED_PORT = -1 - -_UPGRADE_HEADER = 'Upgrade: WebSocket\r\n' -_CONNECTION_HEADER = 'Connection: Upgrade\r\n' -_EXPECTED_RESPONSE = ( - 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + - _UPGRADE_HEADER + - _CONNECTION_HEADER) - -_GOODBYE_MESSAGE = 'Goodbye' - - -def _method_line(resource): - return 'GET %s HTTP/1.1\r\n' % resource - - -def _origin_header(origin): - return 'Origin: %s\r\n' % origin - - -class _TLSSocket(object): - """Wrapper for a TLS connection.""" - - def __init__(self, raw_socket): - self._ssl = socket.ssl(raw_socket) - - def send(self, bytes): - return self._ssl.write(bytes) - - def recv(self, size=-1): - return self._ssl.read(size) - - def close(self): - # Nothing to do. - pass - - -class EchoClient(object): - """Web Socket echo client.""" - - def __init__(self, options): - self._options = options - self._socket = None - - def run(self): - """Run the client. - - Shake hands and then repeat sending message and receiving its echo. - """ - self._socket = socket.socket() - self._socket.settimeout(self._options.socket_timeout) - try: - self._socket.connect((self._options.server_host, - self._options.server_port)) - if self._options.use_tls: - self._socket = _TLSSocket(self._socket) - self._handshake() - for line in self._options.message.split(',') + [_GOODBYE_MESSAGE]: - frame = '\x00' + line.encode('utf-8') + '\xff' - self._socket.send(frame) - if self._options.verbose: - print 'Send: %s' % line - received = self._socket.recv(len(frame)) - if received != frame: - raise Exception('Incorrect echo: %r' % received) - if self._options.verbose: - print 'Recv: %s' % received[1:-1].decode('utf-8', - 'replace') - finally: - self._socket.close() - - def _handshake(self): - self._socket.send(_method_line(self._options.resource)) - self._socket.send(_UPGRADE_HEADER) - self._socket.send(_CONNECTION_HEADER) - self._socket.send(self._format_host_header()) - self._socket.send(_origin_header(self._options.origin)) - self._socket.send('\r\n') - - for expected_char in _EXPECTED_RESPONSE: - received = self._socket.recv(1)[0] - if expected_char != received: - raise Exception('Handshake failure') - # We cut corners and skip other headers. - self._skip_headers() - - def _skip_headers(self): - terminator = '\r\n\r\n' - pos = 0 - while pos < len(terminator): - received = self._socket.recv(1)[0] - if received == terminator[pos]: - pos += 1 - elif received == terminator[0]: - pos = 1 - else: - pos = 0 - - def _format_host_header(self): - host = 'Host: ' + self._options.server_host - if ((not self._options.use_tls and - self._options.server_port != _DEFAULT_PORT) or - (self._options.use_tls and - self._options.server_port != _DEFAULT_SECURE_PORT)): - host += ':' + str(self._options.server_port) - host += '\r\n' - return host - - -def main(): - sys.stdout = codecs.getwriter('utf-8')(sys.stdout) - - parser = OptionParser() - parser.add_option('-s', '--server-host', '--server_host', - dest='server_host', type='string', - default='localhost', help='server host') - parser.add_option('-p', '--server-port', '--server_port', - dest='server_port', type='int', - default=_UNDEFINED_PORT, help='server port') - parser.add_option('-o', '--origin', dest='origin', type='string', - default='http://localhost/', help='origin') - parser.add_option('-r', '--resource', dest='resource', type='string', - default='/echo', help='resource path') - parser.add_option('-m', '--message', dest='message', type='string', - help=('comma-separated messages to send excluding "%s" ' - 'that is always sent at the end' % - _GOODBYE_MESSAGE)) - parser.add_option('-q', '--quiet', dest='verbose', action='store_false', - default=True, help='suppress messages') - parser.add_option('-t', '--tls', dest='use_tls', action='store_true', - default=False, help='use TLS (wss://)') - parser.add_option('-k', '--socket-timeout', '--socket_timeout', - dest='socket_timeout', type='int', default=_TIMEOUT_SEC, - help='Timeout(sec) for sockets') - - (options, unused_args) = parser.parse_args() - - # Default port number depends on whether TLS is used. - if options.server_port == _UNDEFINED_PORT: - if options.use_tls: - options.server_port = _DEFAULT_SECURE_PORT - else: - options.server_port = _DEFAULT_PORT - - # optparse doesn't seem to handle non-ascii default values. - # Set default message here. - if not options.message: - options.message = u'Hello,\u65e5\u672c' # "Japan" in Japanese - - EchoClient(options).run() - - -if __name__ == '__main__': - main() - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_wsh.py deleted file mode 100644 index 50cad31..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_wsh.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -from mod_pywebsocket import msgutil - - -_GOODBYE_MESSAGE = 'Goodbye' - - -def web_socket_do_extra_handshake(request): - pass # Always accept. - - -def web_socket_transfer_data(request): - while True: - line = msgutil.receive_message(request) - msgutil.send_message(request, line) - if line == _GOODBYE_MESSAGE: - return - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/handler_map.txt b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/handler_map.txt deleted file mode 100644 index 21c4c09..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/handler_map.txt +++ /dev/null @@ -1,11 +0,0 @@ -# websocket handler map file, used by standalone.py -m option. -# A line starting with '#' is a comment line. -# Each line consists of 'alias_resource_path' and 'existing_resource_path' -# separated by spaces. -# Aliasing is processed from the top to the bottom of the line, and -# 'existing_resource_path' must exist before it is aliased. -# For example, -# / /echo -# means that a request to '/' will be handled by handlers for '/echo'. -/ /echo - diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/__init__.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/__init__.py deleted file mode 100644 index 05e80e8..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/__init__.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Web Socket extension for Apache HTTP Server. - -mod_pywebsocket is a Web Socket extension for Apache HTTP Server -intended for testing or experimental purposes. mod_python is required. - -Installation: - -0. Prepare an Apache HTTP Server for which mod_python is enabled. - -1. Specify the following Apache HTTP Server directives to suit your - configuration. - - If mod_pywebsocket is not in the Python path, specify the following. - <websock_lib> is the directory where mod_pywebsocket is installed. - - PythonPath "sys.path+['<websock_lib>']" - - Always specify the following. <websock_handlers> is the directory where - user-written Web Socket handlers are placed. - - PythonOption mod_pywebsocket.handler_root <websock_handlers> - PythonHeaderParserHandler mod_pywebsocket.headerparserhandler - - To limit the search for Web Socket handlers to a directory <scan_dir> - under <websock_handlers>, configure as follows: - - PythonOption mod_pywebsocket.handler_scan <scan_dir> - - <scan_dir> is useful in saving scan time when <websock_handlers> - contains many non-Web Socket handler files. - - Example snippet of httpd.conf: - (mod_pywebsocket is in /websock_lib, Web Socket handlers are in - /websock_handlers, port is 80 for ws, 443 for wss.) - - <IfModule python_module> - PythonPath "sys.path+['/websock_lib']" - PythonOption mod_pywebsocket.handler_root /websock_handlers - PythonHeaderParserHandler mod_pywebsocket.headerparserhandler - </IfModule> - -Writing Web Socket handlers: - -When a Web Socket request comes in, the resource name -specified in the handshake is considered as if it is a file path under -<websock_handlers> and the handler defined in -<websock_handlers>/<resource_name>_wsh.py is invoked. - -For example, if the resource name is /example/chat, the handler defined in -<websock_handlers>/example/chat_wsh.py is invoked. - -A Web Socket handler is composed of the following two functions: - - web_socket_do_extra_handshake(request) - web_socket_transfer_data(request) - -where: - request: mod_python request. - -web_socket_do_extra_handshake is called during the handshake after the -headers are successfully parsed and Web Socket properties (ws_location, -ws_origin, ws_protocol, and ws_resource) are added to request. A handler -can reject the request by raising an exception. - -web_socket_transfer_data is called after the handshake completed -successfully. A handler can receive/send messages from/to the client -using request. mod_pywebsocket.msgutil module provides utilities -for data transfer. - -A Web Socket handler must be thread-safe if the server (Apache or -standalone.py) is configured to use threads. -""" - - -# vi:sts=4 sw=4 et tw=72 diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/dispatch.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/dispatch.py deleted file mode 100644 index c52e9eb..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/dispatch.py +++ /dev/null @@ -1,231 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Dispatch Web Socket request. -""" - - -import os -import re - -import util - - -_SOURCE_PATH_PATTERN = re.compile(r'(?i)_wsh\.py$') -_SOURCE_SUFFIX = '_wsh.py' -_DO_EXTRA_HANDSHAKE_HANDLER_NAME = 'web_socket_do_extra_handshake' -_TRANSFER_DATA_HANDLER_NAME = 'web_socket_transfer_data' - - -class DispatchError(Exception): - """Exception in dispatching Web Socket request.""" - - pass - - -def _normalize_path(path): - """Normalize path. - - Args: - path: the path to normalize. - - Path is converted to the absolute path. - The input path can use either '\\' or '/' as the separator. - The normalized path always uses '/' regardless of the platform. - """ - - path = path.replace('\\', os.path.sep) - path = os.path.realpath(path) - path = path.replace('\\', '/') - return path - - -def _path_to_resource_converter(base_dir): - base_dir = _normalize_path(base_dir) - base_len = len(base_dir) - suffix_len = len(_SOURCE_SUFFIX) - def converter(path): - if not path.endswith(_SOURCE_SUFFIX): - return None - path = _normalize_path(path) - if not path.startswith(base_dir): - return None - return path[base_len:-suffix_len] - return converter - - -def _source_file_paths(directory): - """Yield Web Socket Handler source file names in the given directory.""" - - for root, unused_dirs, files in os.walk(directory): - for base in files: - path = os.path.join(root, base) - if _SOURCE_PATH_PATTERN.search(path): - yield path - - -def _source(source_str): - """Source a handler definition string.""" - - global_dic = {} - try: - exec source_str in global_dic - except Exception: - raise DispatchError('Error in sourcing handler:' + - util.get_stack_trace()) - return (_extract_handler(global_dic, _DO_EXTRA_HANDSHAKE_HANDLER_NAME), - _extract_handler(global_dic, _TRANSFER_DATA_HANDLER_NAME)) - - -def _extract_handler(dic, name): - if name not in dic: - raise DispatchError('%s is not defined.' % name) - handler = dic[name] - if not callable(handler): - raise DispatchError('%s is not callable.' % name) - return handler - - -class Dispatcher(object): - """Dispatches Web Socket requests. - - This class maintains a map from resource name to handlers. - """ - - def __init__(self, root_dir, scan_dir=None): - """Construct an instance. - - Args: - root_dir: The directory where handler definition files are - placed. - scan_dir: The directory where handler definition files are - searched. scan_dir must be a directory under root_dir, - including root_dir itself. If scan_dir is None, root_dir - is used as scan_dir. scan_dir can be useful in saving - scan time when root_dir contains many subdirectories. - """ - - self._handlers = {} - self._source_warnings = [] - if scan_dir is None: - scan_dir = root_dir - if not os.path.realpath(scan_dir).startswith( - os.path.realpath(root_dir)): - raise DispatchError('scan_dir:%s must be a directory under ' - 'root_dir:%s.' % (scan_dir, root_dir)) - self._source_files_in_dir(root_dir, scan_dir) - - def add_resource_path_alias(self, - alias_resource_path, existing_resource_path): - """Add resource path alias. - - Once added, request to alias_resource_path would be handled by - handler registered for existing_resource_path. - - Args: - alias_resource_path: alias resource path - existing_resource_path: existing resource path - """ - try: - handler = self._handlers[existing_resource_path] - self._handlers[alias_resource_path] = handler - except KeyError: - raise DispatchError('No handler for: %r' % existing_resource_path) - - def source_warnings(self): - """Return warnings in sourcing handlers.""" - - return self._source_warnings - - def do_extra_handshake(self, request): - """Do extra checking in Web Socket handshake. - - Select a handler based on request.uri and call its - web_socket_do_extra_handshake function. - - Args: - request: mod_python request. - """ - - do_extra_handshake_, unused_transfer_data = self._handler(request) - try: - do_extra_handshake_(request) - except Exception, e: - util.prepend_message_to_exception( - '%s raised exception for %s: ' % ( - _DO_EXTRA_HANDSHAKE_HANDLER_NAME, - request.ws_resource), - e) - raise - - def transfer_data(self, request): - """Let a handler transfer_data with a Web Socket client. - - Select a handler based on request.ws_resource and call its - web_socket_transfer_data function. - - Args: - request: mod_python request. - """ - - unused_do_extra_handshake, transfer_data_ = self._handler(request) - try: - transfer_data_(request) - except Exception, e: - util.prepend_message_to_exception( - '%s raised exception for %s: ' % ( - _TRANSFER_DATA_HANDLER_NAME, request.ws_resource), - e) - raise - - def _handler(self, request): - try: - ws_resource_path = request.ws_resource.split('?', 1)[0] - return self._handlers[ws_resource_path] - except KeyError: - raise DispatchError('No handler for: %r' % request.ws_resource) - - def _source_files_in_dir(self, root_dir, scan_dir): - """Source all the handler source files in the scan_dir directory. - - The resource path is determined relative to root_dir. - """ - - to_resource = _path_to_resource_converter(root_dir) - for path in _source_file_paths(scan_dir): - try: - handlers = _source(open(path).read()) - except DispatchError, e: - self._source_warnings.append('%s: %s' % (path, e)) - continue - self._handlers[to_resource(path)] = handlers - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/handshake.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/handshake.py deleted file mode 100644 index b86278e..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/handshake.py +++ /dev/null @@ -1,220 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Web Socket handshaking. - -Note: request.connection.write/read are used in this module, even though -mod_python document says that they should be used only in connection handlers. -Unfortunately, we have no other options. For example, request.write/read are -not suitable because they don't allow direct raw bytes writing/reading. -""" - - -import re - -import util - - -_DEFAULT_WEB_SOCKET_PORT = 80 -_DEFAULT_WEB_SOCKET_SECURE_PORT = 443 -_WEB_SOCKET_SCHEME = 'ws' -_WEB_SOCKET_SECURE_SCHEME = 'wss' - -_MANDATORY_HEADERS = [ - # key, expected value or None - ['Upgrade', 'WebSocket'], - ['Connection', 'Upgrade'], - ['Host', None], - ['Origin', None], -] - -_FIRST_FIVE_LINES = map(re.compile, [ - r'^GET /[\S]* HTTP/1.1\r\n$', - r'^Upgrade: WebSocket\r\n$', - r'^Connection: Upgrade\r\n$', - r'^Host: [\S]+\r\n$', - r'^Origin: [\S]+\r\n$', -]) - -_SIXTH_AND_LATER = re.compile( - r'^' - r'(WebSocket-Protocol: [\x20-\x7e]+\r\n)?' - r'(Cookie: [^\r]*\r\n)*' - r'(Cookie2: [^\r]*\r\n)?' - r'(Cookie: [^\r]*\r\n)*' - r'\r\n') - - -def _default_port(is_secure): - if is_secure: - return _DEFAULT_WEB_SOCKET_SECURE_PORT - else: - return _DEFAULT_WEB_SOCKET_PORT - - -class HandshakeError(Exception): - """Exception in Web Socket Handshake.""" - - pass - - -def _validate_protocol(protocol): - """Validate WebSocket-Protocol string.""" - - if not protocol: - raise HandshakeError('Invalid WebSocket-Protocol: empty') - for c in protocol: - if not 0x20 <= ord(c) <= 0x7e: - raise HandshakeError('Illegal character in protocol: %r' % c) - - -class Handshaker(object): - """This class performs Web Socket handshake.""" - - def __init__(self, request, dispatcher, strict=False): - """Construct an instance. - - Args: - request: mod_python request. - dispatcher: Dispatcher (dispatch.Dispatcher). - strict: Strictly check handshake request. Default: False. - If True, request.connection must provide get_memorized_lines - method. - - Handshaker will add attributes such as ws_resource in performing - handshake. - """ - - self._request = request - self._dispatcher = dispatcher - self._strict = strict - - def do_handshake(self): - """Perform Web Socket Handshake.""" - - self._check_header_lines() - self._set_resource() - self._set_origin() - self._set_location() - self._set_protocol() - self._dispatcher.do_extra_handshake(self._request) - self._send_handshake() - - def _set_resource(self): - self._request.ws_resource = self._request.uri - - def _set_origin(self): - self._request.ws_origin = self._request.headers_in['Origin'] - - def _set_location(self): - location_parts = [] - if self._request.is_https(): - location_parts.append(_WEB_SOCKET_SECURE_SCHEME) - else: - location_parts.append(_WEB_SOCKET_SCHEME) - location_parts.append('://') - host, port = self._parse_host_header() - connection_port = self._request.connection.local_addr[1] - if port != connection_port: - raise HandshakeError('Header/connection port mismatch: %d/%d' % - (port, connection_port)) - location_parts.append(host) - if (port != _default_port(self._request.is_https())): - location_parts.append(':') - location_parts.append(str(port)) - location_parts.append(self._request.uri) - self._request.ws_location = ''.join(location_parts) - - def _parse_host_header(self): - fields = self._request.headers_in['Host'].split(':', 1) - if len(fields) == 1: - return fields[0], _default_port(self._request.is_https()) - try: - return fields[0], int(fields[1]) - except ValueError, e: - raise HandshakeError('Invalid port number format: %r' % e) - - def _set_protocol(self): - protocol = self._request.headers_in.get('WebSocket-Protocol') - if protocol is not None: - _validate_protocol(protocol) - self._request.ws_protocol = protocol - - def _send_handshake(self): - self._request.connection.write( - 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n') - self._request.connection.write('Upgrade: WebSocket\r\n') - self._request.connection.write('Connection: Upgrade\r\n') - self._request.connection.write('WebSocket-Origin: ') - self._request.connection.write(self._request.ws_origin) - self._request.connection.write('\r\n') - self._request.connection.write('WebSocket-Location: ') - self._request.connection.write(self._request.ws_location) - self._request.connection.write('\r\n') - if self._request.ws_protocol: - self._request.connection.write('WebSocket-Protocol: ') - self._request.connection.write(self._request.ws_protocol) - self._request.connection.write('\r\n') - self._request.connection.write('\r\n') - - def _check_header_lines(self): - for key, expected_value in _MANDATORY_HEADERS: - actual_value = self._request.headers_in.get(key) - if not actual_value: - raise HandshakeError('Header %s is not defined' % key) - if expected_value: - if actual_value != expected_value: - raise HandshakeError('Illegal value for header %s: %s' % - (key, actual_value)) - if self._strict: - try: - lines = self._request.connection.get_memorized_lines() - except AttributeError, e: - util.prepend_message_to_exception( - 'Strict handshake is specified but the connection ' - 'doesn\'t provide get_memorized_lines()', e) - raise - self._check_first_lines(lines) - - def _check_first_lines(self, lines): - if len(lines) < len(_FIRST_FIVE_LINES): - raise HandshakeError('Too few header lines: %d' % len(lines)) - for line, regexp in zip(lines, _FIRST_FIVE_LINES): - if not regexp.search(line): - raise HandshakeError('Unexpected header: %r doesn\'t match %r' - % (line, regexp.pattern)) - sixth_and_later = ''.join(lines[5:]) - if not _SIXTH_AND_LATER.search(sixth_and_later): - raise HandshakeError('Unexpected header: %r doesn\'t match %r' - % (sixth_and_later, - _SIXTH_AND_LATER.pattern)) - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/headerparserhandler.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/headerparserhandler.py deleted file mode 100644 index 124b9f1..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/headerparserhandler.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""PythonHeaderParserHandler for mod_pywebsocket. - -Apache HTTP Server and mod_python must be configured such that this -function is called to handle Web Socket request. -""" - - -from mod_python import apache - -import dispatch -import handshake -import util - - -# PythonOption to specify the handler root directory. -_PYOPT_HANDLER_ROOT = 'mod_pywebsocket.handler_root' - -# PythonOption to specify the handler scan directory. -# This must be a directory under the root directory. -# The default is the root directory. -_PYOPT_HANDLER_SCAN = 'mod_pywebsocket.handler_scan' - - -def _create_dispatcher(): - _HANDLER_ROOT = apache.main_server.get_options().get( - _PYOPT_HANDLER_ROOT, None) - if not _HANDLER_ROOT: - raise Exception('PythonOption %s is not defined' % _PYOPT_HANDLER_ROOT, - apache.APLOG_ERR) - _HANDLER_SCAN = apache.main_server.get_options().get( - _PYOPT_HANDLER_SCAN, _HANDLER_ROOT) - dispatcher = dispatch.Dispatcher(_HANDLER_ROOT, _HANDLER_SCAN) - for warning in dispatcher.source_warnings(): - apache.log_error('mod_pywebsocket: %s' % warning, apache.APLOG_WARNING) - return dispatcher - - -# Initialize -_dispatcher = _create_dispatcher() - - -def headerparserhandler(request): - """Handle request. - - Args: - request: mod_python request. - - This function is named headerparserhandler because it is the default name - for a PythonHeaderParserHandler. - """ - - try: - handshaker = handshake.Handshaker(request, _dispatcher) - handshaker.do_handshake() - request.log_error('mod_pywebsocket: resource: %r' % request.ws_resource, - apache.APLOG_DEBUG) - _dispatcher.transfer_data(request) - except handshake.HandshakeError, e: - # Handshake for ws/wss failed. - # But the request can be valid http/https request. - request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO) - return apache.DECLINED - except dispatch.DispatchError, e: - request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_WARNING) - return apache.DECLINED - return apache.DONE # Return DONE such that no other handlers are invoked. - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/memorizingfile.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/memorizingfile.py deleted file mode 100644 index 2f8a54e..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/memorizingfile.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Memorizing file. - -A memorizing file wraps a file and memorizes lines read by readline. -""" - - -import sys - - -class MemorizingFile(object): - """MemorizingFile wraps a file and memorizes lines read by readline. - - Note that data read by other methods are not memorized. This behavior - is good enough for memorizing lines SimpleHTTPServer reads before - the control reaches WebSocketRequestHandler. - """ - def __init__(self, file_, max_memorized_lines=sys.maxint): - """Construct an instance. - - Args: - file_: the file object to wrap. - max_memorized_lines: the maximum number of lines to memorize. - Only the first max_memorized_lines are memorized. - Default: sys.maxint. - """ - self._file = file_ - self._memorized_lines = [] - self._max_memorized_lines = max_memorized_lines - - def __getattribute__(self, name): - if name in ('_file', '_memorized_lines', '_max_memorized_lines', - 'readline', 'get_memorized_lines'): - return object.__getattribute__(self, name) - return self._file.__getattribute__(name) - - def readline(self): - """Override file.readline and memorize the line read.""" - - line = self._file.readline() - if line and len(self._memorized_lines) < self._max_memorized_lines: - self._memorized_lines.append(line) - return line - - def get_memorized_lines(self): - """Get lines memorized so far.""" - return self._memorized_lines - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/msgutil.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/msgutil.py deleted file mode 100644 index 90ae715..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/msgutil.py +++ /dev/null @@ -1,250 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Message related utilities. - -Note: request.connection.write/read are used in this module, even though -mod_python document says that they should be used only in connection handlers. -Unfortunately, we have no other options. For example, request.write/read are -not suitable because they don't allow direct raw bytes writing/reading. -""" - - -import Queue -import threading -import util - - -class MsgUtilException(Exception): - pass - - -def _read(request, length): - bytes = request.connection.read(length) - if not bytes: - raise MsgUtilException( - 'Failed to receive message from %r' % - (request.connection.remote_addr,)) - return bytes - - -def _write(request, bytes): - try: - request.connection.write(bytes) - except Exception, e: - util.prepend_message_to_exception( - 'Failed to send message to %r: ' % - (request.connection.remote_addr,), - e) - raise - - -def send_message(request, message): - """Send message. - - Args: - request: mod_python request. - message: unicode string to send. - """ - - _write(request, '\x00' + message.encode('utf-8') + '\xff') - - -def receive_message(request): - """Receive a Web Socket frame and return its payload as unicode string. - - Args: - request: mod_python request. - """ - - while True: - # Read 1 byte. - # mp_conn.read will block if no bytes are available. - # Timeout is controlled by TimeOut directive of Apache. - frame_type_str = _read(request, 1) - frame_type = ord(frame_type_str[0]) - if (frame_type & 0x80) == 0x80: - # The payload length is specified in the frame. - # Read and discard. - length = _payload_length(request) - _receive_bytes(request, length) - else: - # The payload is delimited with \xff. - bytes = _read_until(request, '\xff') - # The Web Socket protocol section 4.4 specifies that invalid - # characters must be replaced with U+fffd REPLACEMENT CHARACTER. - message = bytes.decode('utf-8', 'replace') - if frame_type == 0x00: - return message - # Discard data of other types. - - -def _payload_length(request): - length = 0 - while True: - b_str = _read(request, 1) - b = ord(b_str[0]) - length = length * 128 + (b & 0x7f) - if (b & 0x80) == 0: - break - return length - - -def _receive_bytes(request, length): - bytes = [] - while length > 0: - new_bytes = _read(request, length) - bytes.append(new_bytes) - length -= len(new_bytes) - return ''.join(bytes) - - -def _read_until(request, delim_char): - bytes = [] - while True: - ch = _read(request, 1) - if ch == delim_char: - break - bytes.append(ch) - return ''.join(bytes) - - -class MessageReceiver(threading.Thread): - """This class receives messages from the client. - - This class provides three ways to receive messages: blocking, non-blocking, - and via callback. Callback has the highest precedence. - - Note: This class should not be used with the standalone server for wss - because pyOpenSSL used by the server raises a fatal error if the socket - is accessed from multiple threads. - """ - def __init__(self, request, onmessage=None): - """Construct an instance. - - Args: - request: mod_python request. - onmessage: a function to be called when a message is received. - May be None. If not None, the function is called on - another thread. In that case, MessageReceiver.receive - and MessageReceiver.receive_nowait are useless because - they will never return any messages. - """ - threading.Thread.__init__(self) - self._request = request - self._queue = Queue.Queue() - self._onmessage = onmessage - self._stop_requested = False - self.setDaemon(True) - self.start() - - def run(self): - while not self._stop_requested: - message = receive_message(self._request) - if self._onmessage: - self._onmessage(message) - else: - self._queue.put(message) - - def receive(self): - """ Receive a message from the channel, blocking. - - Returns: - message as a unicode string. - """ - return self._queue.get() - - def receive_nowait(self): - """ Receive a message from the channel, non-blocking. - - Returns: - message as a unicode string if available. None otherwise. - """ - try: - message = self._queue.get_nowait() - except Queue.Empty: - message = None - return message - - def stop(self): - """Request to stop this instance. - - The instance will be stopped after receiving the next message. - This method may not be very useful, but there is no clean way - in Python to forcefully stop a running thread. - """ - self._stop_requested = True - - -class MessageSender(threading.Thread): - """This class sends messages to the client. - - This class provides both synchronous and asynchronous ways to send - messages. - - Note: This class should not be used with the standalone server for wss - because pyOpenSSL used by the server raises a fatal error if the socket - is accessed from multiple threads. - """ - def __init__(self, request): - """Construct an instance. - - Args: - request: mod_python request. - """ - threading.Thread.__init__(self) - self._request = request - self._queue = Queue.Queue() - self.setDaemon(True) - self.start() - - def run(self): - while True: - message, condition = self._queue.get() - condition.acquire() - send_message(self._request, message) - condition.notify() - condition.release() - - def send(self, message): - """Send a message, blocking.""" - - condition = threading.Condition() - condition.acquire() - self._queue.put((message, condition)) - condition.wait() - - def send_nowait(self, message): - """Send a message, non-blocking.""" - - self._queue.put((message, threading.Condition())) - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py deleted file mode 100644 index f411910..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py +++ /dev/null @@ -1,453 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Standalone Web Socket server. - -Use this server to run mod_pywebsocket without Apache HTTP Server. - -Usage: - python standalone.py [-p <ws_port>] [-w <websock_handlers>] - [-s <scan_dir>] - [-d <document_root>] - [-m <websock_handlers_map_file>] - ... for other options, see _main below ... - -<ws_port> is the port number to use for ws:// connection. - -<document_root> is the path to the root directory of HTML files. - -<websock_handlers> is the path to the root directory of Web Socket handlers. -See __init__.py for details of <websock_handlers> and how to write Web Socket -handlers. If this path is relative, <document_root> is used as the base. - -<scan_dir> is a path under the root directory. If specified, only the handlers -under scan_dir are scanned. This is useful in saving scan time. - -Note: -This server is derived from SocketServer.ThreadingMixIn. Hence a thread is -used for each request. -""" - -import BaseHTTPServer -import CGIHTTPServer -import SimpleHTTPServer -import SocketServer -import logging -import logging.handlers -import optparse -import os -import re -import socket -import sys - -_HAS_OPEN_SSL = False -try: - import OpenSSL.SSL - _HAS_OPEN_SSL = True -except ImportError: - pass - -import dispatch -import handshake -import memorizingfile -import util - - -_LOG_LEVELS = { - 'debug': logging.DEBUG, - 'info': logging.INFO, - 'warn': logging.WARN, - 'error': logging.ERROR, - 'critical': logging.CRITICAL}; - -_DEFAULT_LOG_MAX_BYTES = 1024 * 256 -_DEFAULT_LOG_BACKUP_COUNT = 5 - -_DEFAULT_REQUEST_QUEUE_SIZE = 128 - -# 1024 is practically large enough to contain WebSocket handshake lines. -_MAX_MEMORIZED_LINES = 1024 - -def _print_warnings_if_any(dispatcher): - warnings = dispatcher.source_warnings() - if warnings: - for warning in warnings: - logging.warning('mod_pywebsocket: %s' % warning) - - -class _StandaloneConnection(object): - """Mimic mod_python mp_conn.""" - - def __init__(self, request_handler): - """Construct an instance. - - Args: - request_handler: A WebSocketRequestHandler instance. - """ - self._request_handler = request_handler - - def get_local_addr(self): - """Getter to mimic mp_conn.local_addr.""" - return (self._request_handler.server.server_name, - self._request_handler.server.server_port) - local_addr = property(get_local_addr) - - def get_remote_addr(self): - """Getter to mimic mp_conn.remote_addr. - - Setting the property in __init__ won't work because the request - handler is not initialized yet there.""" - return self._request_handler.client_address - remote_addr = property(get_remote_addr) - - def write(self, data): - """Mimic mp_conn.write().""" - return self._request_handler.wfile.write(data) - - def read(self, length): - """Mimic mp_conn.read().""" - return self._request_handler.rfile.read(length) - - def get_memorized_lines(self): - """Get memorized lines.""" - return self._request_handler.rfile.get_memorized_lines() - - -class _StandaloneRequest(object): - """Mimic mod_python request.""" - - def __init__(self, request_handler, use_tls): - """Construct an instance. - - Args: - request_handler: A WebSocketRequestHandler instance. - """ - self._request_handler = request_handler - self.connection = _StandaloneConnection(request_handler) - self._use_tls = use_tls - - def get_uri(self): - """Getter to mimic request.uri.""" - return self._request_handler.path - uri = property(get_uri) - - def get_headers_in(self): - """Getter to mimic request.headers_in.""" - return self._request_handler.headers - headers_in = property(get_headers_in) - - def is_https(self): - """Mimic request.is_https().""" - return self._use_tls - - -class WebSocketServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): - """HTTPServer specialized for Web Socket.""" - - SocketServer.ThreadingMixIn.daemon_threads = True - - def __init__(self, server_address, RequestHandlerClass): - """Override SocketServer.BaseServer.__init__.""" - - SocketServer.BaseServer.__init__( - self, server_address, RequestHandlerClass) - self.socket = self._create_socket() - self.server_bind() - self.server_activate() - - def _create_socket(self): - socket_ = socket.socket(self.address_family, self.socket_type) - if WebSocketServer.options.use_tls: - ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) - ctx.use_privatekey_file(WebSocketServer.options.private_key) - ctx.use_certificate_file(WebSocketServer.options.certificate) - socket_ = OpenSSL.SSL.Connection(ctx, socket_) - return socket_ - - def handle_error(self, rquest, client_address): - """Override SocketServer.handle_error.""" - - logging.error( - ('Exception in processing request from: %r' % (client_address,)) + - '\n' + util.get_stack_trace()) - # Note: client_address is a tuple. To match it against %r, we need the - # trailing comma. - - -class WebSocketRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler): - """CGIHTTPRequestHandler specialized for Web Socket.""" - - def setup(self): - """Override SocketServer.StreamRequestHandler.setup.""" - - self.connection = self.request - self.rfile = memorizingfile.MemorizingFile( - socket._fileobject(self.request, 'rb', self.rbufsize), - max_memorized_lines=_MAX_MEMORIZED_LINES) - self.wfile = socket._fileobject(self.request, 'wb', self.wbufsize) - - def __init__(self, *args, **keywords): - self._request = _StandaloneRequest( - self, WebSocketRequestHandler.options.use_tls) - self._dispatcher = WebSocketRequestHandler.options.dispatcher - self._print_warnings_if_any() - self._handshaker = handshake.Handshaker( - self._request, self._dispatcher, - WebSocketRequestHandler.options.strict) - CGIHTTPServer.CGIHTTPRequestHandler.__init__( - self, *args, **keywords) - - def _print_warnings_if_any(self): - warnings = self._dispatcher.source_warnings() - if warnings: - for warning in warnings: - logging.warning('mod_pywebsocket: %s' % warning) - - def parse_request(self): - """Override BaseHTTPServer.BaseHTTPRequestHandler.parse_request. - - Return True to continue processing for HTTP(S), False otherwise. - """ - result = CGIHTTPServer.CGIHTTPRequestHandler.parse_request(self) - if result: - try: - self._handshaker.do_handshake() - self._dispatcher.transfer_data(self._request) - return False - except handshake.HandshakeError, e: - # Handshake for ws(s) failed. Assume http(s). - logging.info('mod_pywebsocket: %s' % e) - return True - except dispatch.DispatchError, e: - logging.warning('mod_pywebsocket: %s' % e) - return False - except Exception, e: - logging.warning('mod_pywebsocket: %s' % e) - logging.info('mod_pywebsocket: %s' % util.get_stack_trace()) - return False - return result - - def log_request(self, code='-', size='-'): - """Override BaseHTTPServer.log_request.""" - - logging.info('"%s" %s %s', - self.requestline, str(code), str(size)) - - def log_error(self, *args): - """Override BaseHTTPServer.log_error.""" - - # Despite the name, this method is for warnings than for errors. - # For example, HTTP status code is logged by this method. - logging.warn('%s - %s' % (self.address_string(), (args[0] % args[1:]))) - - def is_cgi(self): - """Test whether self.path corresponds to a CGI script. - - Add extra check that self.path doesn't contains .. - Also check if the file is a executable file or not. - If the file is not executable, it is handled as static file or dir - rather than a CGI script. - """ - if CGIHTTPServer.CGIHTTPRequestHandler.is_cgi(self): - if '..' in self.path: - return False - # strip query parameter from request path - resource_name = self.path.split('?', 2)[0] - # convert resource_name into real path name in filesystem. - scriptfile = self.translate_path(resource_name) - if not os.path.isfile(scriptfile): - return False - if not self.is_executable(scriptfile): - return False - return True - return False - - -def _configure_logging(options): - logger = logging.getLogger() - logger.setLevel(_LOG_LEVELS[options.log_level]) - if options.log_file: - handler = logging.handlers.RotatingFileHandler( - options.log_file, 'a', options.log_max, options.log_count) - else: - handler = logging.StreamHandler() - formatter = logging.Formatter( - "[%(asctime)s] [%(levelname)s] %(name)s: %(message)s") - handler.setFormatter(formatter) - logger.addHandler(handler) - -def _alias_handlers(dispatcher, websock_handlers_map_file): - """Set aliases specified in websock_handler_map_file in dispatcher. - - Args: - dispatcher: dispatch.Dispatcher instance - websock_handler_map_file: alias map file - """ - fp = open(websock_handlers_map_file) - try: - for line in fp: - if line[0] == '#' or line.isspace(): - continue - m = re.match('(\S+)\s+(\S+)', line) - if not m: - logging.warning('Wrong format in map file:' + line) - continue - try: - dispatcher.add_resource_path_alias( - m.group(1), m.group(2)) - except dispatch.DispatchError, e: - logging.error(str(e)) - finally: - fp.close() - - - -def _main(): - parser = optparse.OptionParser() - parser.add_option('-H', '--server-host', '--server_host', - dest='server_host', - default='', - help='server hostname to listen to') - parser.add_option('-p', '--port', dest='port', type='int', - default=handshake._DEFAULT_WEB_SOCKET_PORT, - help='port to listen to') - parser.add_option('-w', '--websock-handlers', '--websock_handlers', - dest='websock_handlers', - default='.', - help='Web Socket handlers root directory.') - parser.add_option('-m', '--websock-handlers-map-file', - '--websock_handlers_map_file', - dest='websock_handlers_map_file', - default=None, - help=('Web Socket handlers map file. ' - 'Each line consists of alias_resource_path and ' - 'existing_resource_path, separated by spaces.')) - parser.add_option('-s', '--scan-dir', '--scan_dir', dest='scan_dir', - default=None, - help=('Web Socket handlers scan directory. ' - 'Must be a directory under websock_handlers.')) - parser.add_option('-d', '--document-root', '--document_root', - dest='document_root', default='.', - help='Document root directory.') - parser.add_option('-x', '--cgi-paths', '--cgi_paths', dest='cgi_paths', - default=None, - help=('CGI paths relative to document_root.' - 'Comma-separated. (e.g -x /cgi,/htbin) ' - 'Files under document_root/cgi_path are handled ' - 'as CGI programs. Must be executable.')) - parser.add_option('-t', '--tls', dest='use_tls', action='store_true', - default=False, help='use TLS (wss://)') - parser.add_option('-k', '--private-key', '--private_key', - dest='private_key', - default='', help='TLS private key file.') - parser.add_option('-c', '--certificate', dest='certificate', - default='', help='TLS certificate file.') - parser.add_option('-l', '--log-file', '--log_file', dest='log_file', - default='', help='Log file.') - parser.add_option('--log-level', '--log_level', type='choice', - dest='log_level', default='warn', - choices=['debug', 'info', 'warn', 'error', 'critical'], - help='Log level.') - parser.add_option('--log-max', '--log_max', dest='log_max', type='int', - default=_DEFAULT_LOG_MAX_BYTES, - help='Log maximum bytes') - parser.add_option('--log-count', '--log_count', dest='log_count', - type='int', default=_DEFAULT_LOG_BACKUP_COUNT, - help='Log backup count') - parser.add_option('--strict', dest='strict', action='store_true', - default=False, help='Strictly check handshake request') - parser.add_option('-q', '--queue', dest='request_queue_size', type='int', - default=_DEFAULT_REQUEST_QUEUE_SIZE, - help='request queue size') - options = parser.parse_args()[0] - - os.chdir(options.document_root) - - _configure_logging(options) - - SocketServer.TCPServer.request_queue_size = options.request_queue_size - CGIHTTPServer.CGIHTTPRequestHandler.cgi_directories = [] - - if options.cgi_paths: - CGIHTTPServer.CGIHTTPRequestHandler.cgi_directories = \ - options.cgi_paths.split(',') - if sys.platform in ('cygwin', 'win32'): - cygwin_path = None - # For Win32 Python, it is expected that CYGWIN_PATH - # is set to a directory of cygwin binaries. - # For example, websocket_server.py in Chromium sets CYGWIN_PATH to - # full path of third_party/cygwin/bin. - if 'CYGWIN_PATH' in os.environ: - cygwin_path = os.environ['CYGWIN_PATH'] - util.wrap_popen3_for_win(cygwin_path) - def __check_script(scriptpath): - return util.get_script_interp(scriptpath, cygwin_path) - CGIHTTPServer.executable = __check_script - - if options.use_tls: - if not _HAS_OPEN_SSL: - logging.critical('To use TLS, install pyOpenSSL.') - sys.exit(1) - if not options.private_key or not options.certificate: - logging.critical( - 'To use TLS, specify private_key and certificate.') - sys.exit(1) - - if not options.scan_dir: - options.scan_dir = options.websock_handlers - - try: - # Share a Dispatcher among request handlers to save time for - # instantiation. Dispatcher can be shared because it is thread-safe. - options.dispatcher = dispatch.Dispatcher(options.websock_handlers, - options.scan_dir) - if options.websock_handlers_map_file: - _alias_handlers(options.dispatcher, - options.websock_handlers_map_file) - _print_warnings_if_any(options.dispatcher) - - WebSocketRequestHandler.options = options - WebSocketServer.options = options - - server = WebSocketServer((options.server_host, options.port), - WebSocketRequestHandler) - server.serve_forever() - except Exception, e: - logging.critical(str(e)) - sys.exit(1) - - -if __name__ == '__main__': - _main() - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/util.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/util.py deleted file mode 100644 index 8ec9dca..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/util.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Web Sockets utilities. -""" - - -import StringIO -import os -import re -import traceback - - -def get_stack_trace(): - """Get the current stack trace as string. - - This is needed to support Python 2.3. - TODO: Remove this when we only support Python 2.4 and above. - Use traceback.format_exc instead. - """ - - out = StringIO.StringIO() - traceback.print_exc(file=out) - return out.getvalue() - - -def prepend_message_to_exception(message, exc): - """Prepend message to the exception.""" - - exc.args = (message + str(exc),) - return - - -def __translate_interp(interp, cygwin_path): - """Translate interp program path for Win32 python to run cygwin program - (e.g. perl). Note that it doesn't support path that contains space, - which is typically true for Unix, where #!-script is written. - For Win32 python, cygwin_path is a directory of cygwin binaries. - - Args: - interp: interp command line - cygwin_path: directory name of cygwin binary, or None - Returns: - translated interp command line. - """ - if not cygwin_path: - return interp - m = re.match("^[^ ]*/([^ ]+)( .*)?", interp) - if m: - cmd = os.path.join(cygwin_path, m.group(1)) - return cmd + m.group(2) - return interp - - -def get_script_interp(script_path, cygwin_path=None): - """Gets #!-interpreter command line from the script. - - It also fixes command path. When Cygwin Python is used, e.g. in WebKit, - it could run "/usr/bin/perl -wT hello.pl". - When Win32 Python is used, e.g. in Chromium, it couldn't. So, fix - "/usr/bin/perl" to "<cygwin_path>\perl.exe". - - Args: - script_path: pathname of the script - cygwin_path: directory name of cygwin binary, or None - Returns: - #!-interpreter command line, or None if it is not #!-script. - """ - fp = open(script_path) - line = fp.readline() - fp.close() - m = re.match("^#!(.*)", line) - if m: - return __translate_interp(m.group(1), cygwin_path) - return None - -def wrap_popen3_for_win(cygwin_path): - """Wrap popen3 to support #!-script on Windows. - - Args: - cygwin_path: path for cygwin binary if command path is needed to be - translated. None if no translation required. - """ - __orig_popen3 = os.popen3 - def __wrap_popen3(cmd, mode='t', bufsize=-1): - cmdline = cmd.split(' ') - interp = get_script_interp(cmdline[0], cygwin_path) - if interp: - cmd = interp + " " + cmd - return __orig_popen3(cmd, mode, bufsize) - os.popen3 = __wrap_popen3 - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/setup.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/setup.py deleted file mode 100644 index a34a83b..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/setup.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Set up script for mod_pywebsocket. -""" - - -from distutils.core import setup -import sys - - -_PACKAGE_NAME = 'mod_pywebsocket' - -if sys.version < '2.3': - print >>sys.stderr, '%s requires Python 2.3 or later.' % _PACKAGE_NAME - sys.exit(1) - -setup(author='Yuzo Fujishima', - author_email='yuzo@chromium.org', - description='Web Socket extension for Apache HTTP Server.', - long_description=( - 'mod_pywebsocket is an Apache HTTP Server extension for ' - 'Web Socket (http://tools.ietf.org/html/' - 'draft-hixie-thewebsocketprotocol). ' - 'See mod_pywebsocket/__init__.py for more detail.'), - license='See COPYING', - name=_PACKAGE_NAME, - packages=[_PACKAGE_NAME], - url='http://code.google.com/p/pywebsocket/', - version='0.4.9.2', - ) - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/config.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/config.py deleted file mode 100644 index 5aaab8c..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/config.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Configuration for testing. - -Test files should import this module before mod_pywebsocket. -""" - - -import os -import sys - - -# Add the parent directory to sys.path to enable importing mod_pywebsocket. -sys.path += [os.path.join(os.path.split(__file__)[0], '..')] - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/mock.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/mock.py deleted file mode 100644 index 3b85d64..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/mock.py +++ /dev/null @@ -1,205 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Mocks for testing. -""" - - -import Queue -import threading - - -class _MockConnBase(object): - """Base class of mocks for mod_python.apache.mp_conn. - - This enables tests to check what is written to a (mock) mp_conn. - """ - - def __init__(self): - self._write_data = [] - - def write(self, data): - """Override mod_python.apache.mp_conn.write.""" - - self._write_data.append(data) - - def written_data(self): - """Get bytes written to this mock.""" - - return ''.join(self._write_data) - - -class MockConn(_MockConnBase): - """Mock for mod_python.apache.mp_conn. - - This enables tests to specify what should be read from a (mock) mp_conn as - well as to check what is written to it. - """ - - def __init__(self, read_data): - """Constructs an instance. - - Args: - read_data: bytes that should be returned when read* methods are - called. - """ - - _MockConnBase.__init__(self) - self._read_data = read_data - self._read_pos = 0 - - def readline(self): - """Override mod_python.apache.mp_conn.readline.""" - - if self._read_pos >= len(self._read_data): - return '' - end_index = self._read_data.find('\n', self._read_pos) + 1 - if not end_index: - end_index = len(self._read_data) - return self._read_up_to(end_index) - - def read(self, length): - """Override mod_python.apache.mp_conn.read.""" - - if self._read_pos >= len(self._read_data): - return '' - end_index = min(len(self._read_data), self._read_pos + length) - return self._read_up_to(end_index) - - def _read_up_to(self, end_index): - line = self._read_data[self._read_pos:end_index] - self._read_pos = end_index - return line - - -class MockBlockingConn(_MockConnBase): - """Blocking mock for mod_python.apache.mp_conn. - - This enables tests to specify what should be read from a (mock) mp_conn as - well as to check what is written to it. - Callers of read* methods will block if there is no bytes available. - """ - - def __init__(self): - _MockConnBase.__init__(self) - self._queue = Queue.Queue() - - def readline(self): - """Override mod_python.apache.mp_conn.readline.""" - line = '' - while True: - c = self._queue.get() - line += c - if c == '\n': - return line - - def read(self, length): - """Override mod_python.apache.mp_conn.read.""" - - data = '' - for unused in range(length): - data += self._queue.get() - return data - - def put_bytes(self, bytes): - """Put bytes to be read from this mock. - - Args: - bytes: bytes to be read. - """ - - for byte in bytes: - self._queue.put(byte) - - -class MockTable(dict): - """Mock table. - - This mimics mod_python mp_table. Note that only the methods used by - tests are overridden. - """ - - def __init__(self, copy_from={}): - if isinstance(copy_from, dict): - copy_from = copy_from.items() - for key, value in copy_from: - self.__setitem__(key, value) - - def __getitem__(self, key): - return super(MockTable, self).__getitem__(key.lower()) - - def __setitem__(self, key, value): - super(MockTable, self).__setitem__(key.lower(), value) - - def get(self, key, def_value=None): - return super(MockTable, self).get(key.lower(), def_value) - - -class MockRequest(object): - """Mock request. - - This mimics mod_python request. - """ - - def __init__(self, uri=None, headers_in={}, connection=None, - is_https=False): - """Construct an instance. - - Arguments: - uri: URI of the request. - headers_in: Request headers. - connection: Connection used for the request. - is_https: Whether this request is over SSL. - - See the document of mod_python Request for details. - """ - self.uri = uri - self.connection = connection - self.headers_in = MockTable(headers_in) - # self.is_https_ needs to be accessible from tests. To avoid name - # conflict with self.is_https(), it is named as such. - self.is_https_ = is_https - - def is_https(self): - """Return whether this request is over SSL.""" - return self.is_https_ - - -class MockDispatcher(object): - """Mock for dispatch.Dispatcher.""" - - def do_extra_handshake(self, conn_context): - pass - - def transfer_data(self, conn_context): - pass - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_dispatch.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_dispatch.py deleted file mode 100644 index 5403228..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_dispatch.py +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Tests for dispatch module.""" - - - -import os -import unittest - -import config # This must be imported before mod_pywebsocket. -from mod_pywebsocket import dispatch - -import mock - - -_TEST_HANDLERS_DIR = os.path.join( - os.path.split(__file__)[0], 'testdata', 'handlers') - -_TEST_HANDLERS_SUB_DIR = os.path.join(_TEST_HANDLERS_DIR, 'sub') - -class DispatcherTest(unittest.TestCase): - def test_normalize_path(self): - self.assertEqual(os.path.abspath('/a/b').replace('\\', '/'), - dispatch._normalize_path('/a/b')) - self.assertEqual(os.path.abspath('/a/b').replace('\\', '/'), - dispatch._normalize_path('\\a\\b')) - self.assertEqual(os.path.abspath('/a/b').replace('\\', '/'), - dispatch._normalize_path('/a/c/../b')) - self.assertEqual(os.path.abspath('abc').replace('\\', '/'), - dispatch._normalize_path('abc')) - - def test_converter(self): - converter = dispatch._path_to_resource_converter('/a/b') - self.assertEqual('/h', converter('/a/b/h_wsh.py')) - self.assertEqual('/c/h', converter('/a/b/c/h_wsh.py')) - self.assertEqual(None, converter('/a/b/h.py')) - self.assertEqual(None, converter('a/b/h_wsh.py')) - - converter = dispatch._path_to_resource_converter('a/b') - self.assertEqual('/h', converter('a/b/h_wsh.py')) - - converter = dispatch._path_to_resource_converter('/a/b///') - self.assertEqual('/h', converter('/a/b/h_wsh.py')) - self.assertEqual('/h', converter('/a/b/../b/h_wsh.py')) - - converter = dispatch._path_to_resource_converter('/a/../a/b/../b/') - self.assertEqual('/h', converter('/a/b/h_wsh.py')) - - converter = dispatch._path_to_resource_converter(r'\a\b') - self.assertEqual('/h', converter(r'\a\b\h_wsh.py')) - self.assertEqual('/h', converter(r'/a/b/h_wsh.py')) - - def test_source_file_paths(self): - paths = list(dispatch._source_file_paths(_TEST_HANDLERS_DIR)) - paths.sort() - self.assertEqual(7, len(paths)) - expected_paths = [ - os.path.join(_TEST_HANDLERS_DIR, 'blank_wsh.py'), - os.path.join(_TEST_HANDLERS_DIR, 'origin_check_wsh.py'), - os.path.join(_TEST_HANDLERS_DIR, 'sub', - 'exception_in_transfer_wsh.py'), - os.path.join(_TEST_HANDLERS_DIR, 'sub', 'non_callable_wsh.py'), - os.path.join(_TEST_HANDLERS_DIR, 'sub', 'plain_wsh.py'), - os.path.join(_TEST_HANDLERS_DIR, 'sub', - 'wrong_handshake_sig_wsh.py'), - os.path.join(_TEST_HANDLERS_DIR, 'sub', - 'wrong_transfer_sig_wsh.py'), - ] - for expected, actual in zip(expected_paths, paths): - self.assertEqual(expected, actual) - - def test_source(self): - self.assertRaises(dispatch.DispatchError, dispatch._source, '') - self.assertRaises(dispatch.DispatchError, dispatch._source, 'def') - self.assertRaises(dispatch.DispatchError, dispatch._source, '1/0') - self.failUnless(dispatch._source( - 'def web_socket_do_extra_handshake(request):pass\n' - 'def web_socket_transfer_data(request):pass\n')) - - def test_source_warnings(self): - dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) - warnings = dispatcher.source_warnings() - warnings.sort() - expected_warnings = [ - (os.path.join(_TEST_HANDLERS_DIR, 'blank_wsh.py') + - ': web_socket_do_extra_handshake is not defined.'), - (os.path.join(_TEST_HANDLERS_DIR, 'sub', - 'non_callable_wsh.py') + - ': web_socket_do_extra_handshake is not callable.'), - (os.path.join(_TEST_HANDLERS_DIR, 'sub', - 'wrong_handshake_sig_wsh.py') + - ': web_socket_do_extra_handshake is not defined.'), - (os.path.join(_TEST_HANDLERS_DIR, 'sub', - 'wrong_transfer_sig_wsh.py') + - ': web_socket_transfer_data is not defined.'), - ] - self.assertEquals(4, len(warnings)) - for expected, actual in zip(expected_warnings, warnings): - self.assertEquals(expected, actual) - - def test_do_extra_handshake(self): - dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) - request = mock.MockRequest() - request.ws_resource = '/origin_check' - request.ws_origin = 'http://example.com' - dispatcher.do_extra_handshake(request) # Must not raise exception. - - request.ws_origin = 'http://bad.example.com' - self.assertRaises(Exception, dispatcher.do_extra_handshake, request) - - def test_transfer_data(self): - dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) - request = mock.MockRequest(connection=mock.MockConn('')) - request.ws_resource = '/origin_check' - request.ws_protocol = 'p1' - - dispatcher.transfer_data(request) - self.assertEqual('origin_check_wsh.py is called for /origin_check, p1', - request.connection.written_data()) - - request = mock.MockRequest(connection=mock.MockConn('')) - request.ws_resource = '/sub/plain' - request.ws_protocol = None - dispatcher.transfer_data(request) - self.assertEqual('sub/plain_wsh.py is called for /sub/plain, None', - request.connection.written_data()) - - request = mock.MockRequest(connection=mock.MockConn('')) - request.ws_resource = '/sub/plain?' - request.ws_protocol = None - dispatcher.transfer_data(request) - self.assertEqual('sub/plain_wsh.py is called for /sub/plain?, None', - request.connection.written_data()) - - request = mock.MockRequest(connection=mock.MockConn('')) - request.ws_resource = '/sub/plain?q=v' - request.ws_protocol = None - dispatcher.transfer_data(request) - self.assertEqual('sub/plain_wsh.py is called for /sub/plain?q=v, None', - request.connection.written_data()) - - def test_transfer_data_no_handler(self): - dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) - for resource in ['/blank', '/sub/non_callable', - '/sub/no_wsh_at_the_end', '/does/not/exist']: - request = mock.MockRequest(connection=mock.MockConn('')) - request.ws_resource = resource - request.ws_protocol = 'p2' - try: - dispatcher.transfer_data(request) - self.fail() - except dispatch.DispatchError, e: - self.failUnless(str(e).find('No handler') != -1) - except Exception: - self.fail() - - def test_transfer_data_handler_exception(self): - dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) - request = mock.MockRequest(connection=mock.MockConn('')) - request.ws_resource = '/sub/exception_in_transfer' - request.ws_protocol = 'p3' - try: - dispatcher.transfer_data(request) - self.fail() - except Exception, e: - self.failUnless(str(e).find('Intentional') != -1) - - def test_scan_dir(self): - disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) - self.assertEqual(3, len(disp._handlers)) - self.failUnless(disp._handlers.has_key('/origin_check')) - self.failUnless(disp._handlers.has_key('/sub/exception_in_transfer')) - self.failUnless(disp._handlers.has_key('/sub/plain')) - - def test_scan_sub_dir(self): - disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, _TEST_HANDLERS_SUB_DIR) - self.assertEqual(2, len(disp._handlers)) - self.failIf(disp._handlers.has_key('/origin_check')) - self.failUnless(disp._handlers.has_key('/sub/exception_in_transfer')) - self.failUnless(disp._handlers.has_key('/sub/plain')) - - def test_scan_sub_dir_as_root(self): - disp = dispatch.Dispatcher(_TEST_HANDLERS_SUB_DIR, - _TEST_HANDLERS_SUB_DIR) - self.assertEqual(2, len(disp._handlers)) - self.failIf(disp._handlers.has_key('/origin_check')) - self.failIf(disp._handlers.has_key('/sub/exception_in_transfer')) - self.failIf(disp._handlers.has_key('/sub/plain')) - self.failUnless(disp._handlers.has_key('/exception_in_transfer')) - self.failUnless(disp._handlers.has_key('/plain')) - - def test_scan_dir_must_under_root(self): - dispatch.Dispatcher('a/b', 'a/b/c') # OK - dispatch.Dispatcher('a/b///', 'a/b') # OK - self.assertRaises(dispatch.DispatchError, - dispatch.Dispatcher, 'a/b/c', 'a/b') - - def test_resource_path_alias(self): - disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None) - disp.add_resource_path_alias('/', '/origin_check') - self.assertEqual(4, len(disp._handlers)) - self.failUnless(disp._handlers.has_key('/origin_check')) - self.failUnless(disp._handlers.has_key('/sub/exception_in_transfer')) - self.failUnless(disp._handlers.has_key('/sub/plain')) - self.failUnless(disp._handlers.has_key('/')) - self.assertRaises(dispatch.DispatchError, - disp.add_resource_path_alias, '/alias', '/not-exist') - - -if __name__ == '__main__': - unittest.main() - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_handshake.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_handshake.py deleted file mode 100644 index 8bf07be..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_handshake.py +++ /dev/null @@ -1,513 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Tests for handshake module.""" - - -import unittest - -import config # This must be imported before mod_pywebsocket. -from mod_pywebsocket import handshake - -import mock - - -_GOOD_REQUEST = ( - 80, - '/demo', - { - 'Upgrade':'WebSocket', - 'Connection':'Upgrade', - 'Host':'example.com', - 'Origin':'http://example.com', - 'WebSocket-Protocol':'sample', - } -) - -_GOOD_RESPONSE_DEFAULT_PORT = ( - 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' - 'Upgrade: WebSocket\r\n' - 'Connection: Upgrade\r\n' - 'WebSocket-Origin: http://example.com\r\n' - 'WebSocket-Location: ws://example.com/demo\r\n' - 'WebSocket-Protocol: sample\r\n' - '\r\n') - -_GOOD_RESPONSE_SECURE = ( - 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' - 'Upgrade: WebSocket\r\n' - 'Connection: Upgrade\r\n' - 'WebSocket-Origin: http://example.com\r\n' - 'WebSocket-Location: wss://example.com/demo\r\n' - 'WebSocket-Protocol: sample\r\n' - '\r\n') - -_GOOD_REQUEST_NONDEFAULT_PORT = ( - 8081, - '/demo', - { - 'Upgrade':'WebSocket', - 'Connection':'Upgrade', - 'Host':'example.com:8081', - 'Origin':'http://example.com', - 'WebSocket-Protocol':'sample', - } -) - -_GOOD_RESPONSE_NONDEFAULT_PORT = ( - 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' - 'Upgrade: WebSocket\r\n' - 'Connection: Upgrade\r\n' - 'WebSocket-Origin: http://example.com\r\n' - 'WebSocket-Location: ws://example.com:8081/demo\r\n' - 'WebSocket-Protocol: sample\r\n' - '\r\n') - -_GOOD_RESPONSE_SECURE_NONDEF = ( - 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' - 'Upgrade: WebSocket\r\n' - 'Connection: Upgrade\r\n' - 'WebSocket-Origin: http://example.com\r\n' - 'WebSocket-Location: wss://example.com:8081/demo\r\n' - 'WebSocket-Protocol: sample\r\n' - '\r\n') - -_GOOD_REQUEST_NO_PROTOCOL = ( - 80, - '/demo', - { - 'Upgrade':'WebSocket', - 'Connection':'Upgrade', - 'Host':'example.com', - 'Origin':'http://example.com', - } -) - -_GOOD_RESPONSE_NO_PROTOCOL = ( - 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' - 'Upgrade: WebSocket\r\n' - 'Connection: Upgrade\r\n' - 'WebSocket-Origin: http://example.com\r\n' - 'WebSocket-Location: ws://example.com/demo\r\n' - '\r\n') - -_GOOD_REQUEST_WITH_OPTIONAL_HEADERS = ( - 80, - '/demo', - { - 'Upgrade':'WebSocket', - 'Connection':'Upgrade', - 'Host':'example.com', - 'Origin':'http://example.com', - 'WebSocket-Protocol':'sample', - 'AKey':'AValue', - 'EmptyValue':'', - } -) - -_BAD_REQUESTS = ( - ( # HTTP request - 80, - '/demo', - { - 'Host':'www.google.com', - 'User-Agent':'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5;' - ' en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3' - ' GTB6 GTBA', - 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,' - '*/*;q=0.8', - 'Accept-Language':'en-us,en;q=0.5', - 'Accept-Encoding':'gzip,deflate', - 'Accept-Charset':'ISO-8859-1,utf-8;q=0.7,*;q=0.7', - 'Keep-Alive':'300', - 'Connection':'keep-alive', - } - ), - ( # Missing Upgrade - 80, - '/demo', - { - 'Connection':'Upgrade', - 'Host':'example.com', - 'Origin':'http://example.com', - 'WebSocket-Protocol':'sample', - } - ), - ( # Wrong Upgrade - 80, - '/demo', - { - 'Upgrade':'NonWebSocket', - 'Connection':'Upgrade', - 'Host':'example.com', - 'Origin':'http://example.com', - 'WebSocket-Protocol':'sample', - } - ), - ( # Empty WebSocket-Protocol - 80, - '/demo', - { - 'Upgrade':'WebSocket', - 'Connection':'Upgrade', - 'Host':'example.com', - 'Origin':'http://example.com', - 'WebSocket-Protocol':'', - } - ), - ( # Wrong port number format - 80, - '/demo', - { - 'Upgrade':'WebSocket', - 'Connection':'Upgrade', - 'Host':'example.com:0x50', - 'Origin':'http://example.com', - 'WebSocket-Protocol':'sample', - } - ), - ( # Header/connection port mismatch - 8080, - '/demo', - { - 'Upgrade':'WebSocket', - 'Connection':'Upgrade', - 'Host':'example.com', - 'Origin':'http://example.com', - 'WebSocket-Protocol':'sample', - } - ), - ( # Illegal WebSocket-Protocol - 80, - '/demo', - { - 'Upgrade':'WebSocket', - 'Connection':'Upgrade', - 'Host':'example.com', - 'Origin':'http://example.com', - 'WebSocket-Protocol':'illegal\x09protocol', - } - ), -) - -_STRICTLY_GOOD_REQUESTS = ( - ( - 'GET /demo HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - '\r\n', - ), - ( # WebSocket-Protocol - 'GET /demo HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - 'WebSocket-Protocol: sample\r\n', - '\r\n', - ), - ( # WebSocket-Protocol and Cookie - 'GET /demo HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - 'WebSocket-Protocol: sample\r\n', - 'Cookie: xyz\r\n' - '\r\n', - ), - ( # Cookie - 'GET /demo HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - 'Cookie: abc/xyz\r\n' - 'Cookie2: $Version=1\r\n' - 'Cookie: abc\r\n' - '\r\n', - ), - ( - 'GET / HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - '\r\n', - ), -) - -_NOT_STRICTLY_GOOD_REQUESTS = ( - ( # Extra space after GET - 'GET /demo HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - '\r\n', - ), - ( # Resource name doesn't stat with '/' - 'GET demo HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - '\r\n', - ), - ( # No space after : - 'GET /demo HTTP/1.1\r\n', - 'Upgrade:WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - '\r\n', - ), - ( # Lower case Upgrade header - 'GET /demo HTTP/1.1\r\n', - 'upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - '\r\n', - ), - ( # Connection comes before Upgrade - 'GET /demo HTTP/1.1\r\n', - 'Connection: Upgrade\r\n', - 'Upgrade: WebSocket\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - '\r\n', - ), - ( # Origin comes before Host - 'GET /demo HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Origin: http://example.com\r\n', - 'Host: example.com\r\n', - '\r\n', - ), - ( # Host continued to the next line - 'GET /demo HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example\r\n', - ' .com\r\n', - 'Origin: http://example.com\r\n', - '\r\n', - ), - ( # Cookie comes before WebSocket-Protocol - 'GET /demo HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - 'Cookie: xyz\r\n' - 'WebSocket-Protocol: sample\r\n', - '\r\n', - ), - ( # Unknown header - 'GET /demo HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - 'Content-Type: text/html\r\n' - '\r\n', - ), - ( # Cookie with continuation lines - 'GET /demo HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - 'Cookie: xyz\r\n', - ' abc\r\n', - ' defg\r\n', - '\r\n', - ), - ( # Wrong-case cookie - 'GET /demo HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - 'cookie: abc/xyz\r\n' - '\r\n', - ), - ( # Cookie, no space after colon - 'GET /demo HTTP/1.1\r\n', - 'Upgrade: WebSocket\r\n', - 'Connection: Upgrade\r\n', - 'Host: example.com\r\n', - 'Origin: http://example.com\r\n', - 'Cookie:abc/xyz\r\n' - '\r\n', - ), -) - - -def _create_request(request_def): - conn = mock.MockConn('') - conn.local_addr = ('0.0.0.0', request_def[0]) - return mock.MockRequest( - uri=request_def[1], - headers_in=request_def[2], - connection=conn) - - -def _create_get_memorized_lines(lines): - def get_memorized_lines(): - return lines - return get_memorized_lines - - -def _create_requests_with_lines(request_lines_set): - requests = [] - for lines in request_lines_set: - request = _create_request(_GOOD_REQUEST) - request.connection.get_memorized_lines = _create_get_memorized_lines( - lines) - requests.append(request) - return requests - - -class HandshakerTest(unittest.TestCase): - def test_validate_protocol(self): - handshake._validate_protocol('sample') # should succeed. - handshake._validate_protocol('Sample') # should succeed. - handshake._validate_protocol('sample\x20protocol') # should succeed. - handshake._validate_protocol('sample\x7eprotocol') # should succeed. - self.assertRaises(handshake.HandshakeError, - handshake._validate_protocol, - '') - self.assertRaises(handshake.HandshakeError, - handshake._validate_protocol, - 'sample\x19protocol') - self.assertRaises(handshake.HandshakeError, - handshake._validate_protocol, - 'sample\x7fprotocol') - self.assertRaises(handshake.HandshakeError, - handshake._validate_protocol, - # "Japan" in Japanese - u'\u65e5\u672c') - - def test_good_request_default_port(self): - request = _create_request(_GOOD_REQUEST) - handshaker = handshake.Handshaker(request, - mock.MockDispatcher()) - handshaker.do_handshake() - self.assertEqual(_GOOD_RESPONSE_DEFAULT_PORT, - request.connection.written_data()) - self.assertEqual('/demo', request.ws_resource) - self.assertEqual('http://example.com', request.ws_origin) - self.assertEqual('ws://example.com/demo', request.ws_location) - self.assertEqual('sample', request.ws_protocol) - - def test_good_request_secure_default_port(self): - request = _create_request(_GOOD_REQUEST) - request.connection.local_addr = ('0.0.0.0', 443) - request.is_https_ = True - handshaker = handshake.Handshaker(request, - mock.MockDispatcher()) - handshaker.do_handshake() - self.assertEqual(_GOOD_RESPONSE_SECURE, - request.connection.written_data()) - self.assertEqual('sample', request.ws_protocol) - - def test_good_request_nondefault_port(self): - request = _create_request(_GOOD_REQUEST_NONDEFAULT_PORT) - handshaker = handshake.Handshaker(request, - mock.MockDispatcher()) - handshaker.do_handshake() - self.assertEqual(_GOOD_RESPONSE_NONDEFAULT_PORT, - request.connection.written_data()) - self.assertEqual('sample', request.ws_protocol) - - def test_good_request_secure_non_default_port(self): - request = _create_request(_GOOD_REQUEST_NONDEFAULT_PORT) - request.is_https_ = True - handshaker = handshake.Handshaker(request, - mock.MockDispatcher()) - handshaker.do_handshake() - self.assertEqual(_GOOD_RESPONSE_SECURE_NONDEF, - request.connection.written_data()) - self.assertEqual('sample', request.ws_protocol) - - def test_good_request_default_no_protocol(self): - request = _create_request(_GOOD_REQUEST_NO_PROTOCOL) - handshaker = handshake.Handshaker(request, - mock.MockDispatcher()) - handshaker.do_handshake() - self.assertEqual(_GOOD_RESPONSE_NO_PROTOCOL, - request.connection.written_data()) - self.assertEqual(None, request.ws_protocol) - - def test_good_request_optional_headers(self): - request = _create_request(_GOOD_REQUEST_WITH_OPTIONAL_HEADERS) - handshaker = handshake.Handshaker(request, - mock.MockDispatcher()) - handshaker.do_handshake() - self.assertEqual('AValue', - request.headers_in['AKey']) - self.assertEqual('', - request.headers_in['EmptyValue']) - - def test_bad_requests(self): - for request in map(_create_request, _BAD_REQUESTS): - handshaker = handshake.Handshaker(request, - mock.MockDispatcher()) - self.assertRaises(handshake.HandshakeError, handshaker.do_handshake) - - def test_strictly_good_requests(self): - for request in _create_requests_with_lines(_STRICTLY_GOOD_REQUESTS): - strict_handshaker = handshake.Handshaker(request, - mock.MockDispatcher(), - True) - strict_handshaker.do_handshake() - - def test_not_strictly_good_requests(self): - for request in _create_requests_with_lines(_NOT_STRICTLY_GOOD_REQUESTS): - strict_handshaker = handshake.Handshaker(request, - mock.MockDispatcher(), - True) - self.assertRaises(handshake.HandshakeError, - strict_handshaker.do_handshake) - - - -if __name__ == '__main__': - unittest.main() - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_mock.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_mock.py deleted file mode 100644 index 8b137d1..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_mock.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Tests for mock module.""" - - -import Queue -import threading -import unittest - -import mock - - -class MockConnTest(unittest.TestCase): - def setUp(self): - self._conn = mock.MockConn('ABC\r\nDEFG\r\n\r\nHIJK') - - def test_readline(self): - self.assertEqual('ABC\r\n', self._conn.readline()) - self.assertEqual('DEFG\r\n', self._conn.readline()) - self.assertEqual('\r\n', self._conn.readline()) - self.assertEqual('HIJK', self._conn.readline()) - self.assertEqual('', self._conn.readline()) - - def test_read(self): - self.assertEqual('ABC\r\nD', self._conn.read(6)) - self.assertEqual('EFG\r\n\r\nHI', self._conn.read(9)) - self.assertEqual('JK', self._conn.read(10)) - self.assertEqual('', self._conn.read(10)) - - def test_read_and_readline(self): - self.assertEqual('ABC\r\nD', self._conn.read(6)) - self.assertEqual('EFG\r\n', self._conn.readline()) - self.assertEqual('\r\nHIJK', self._conn.read(9)) - self.assertEqual('', self._conn.readline()) - - def test_write(self): - self._conn.write('Hello\r\n') - self._conn.write('World\r\n') - self.assertEqual('Hello\r\nWorld\r\n', self._conn.written_data()) - - -class MockBlockingConnTest(unittest.TestCase): - def test_read(self): - class LineReader(threading.Thread): - def __init__(self, conn, queue): - threading.Thread.__init__(self) - self._queue = queue - self._conn = conn - self.setDaemon(True) - self.start() - def run(self): - while True: - data = self._conn.readline() - self._queue.put(data) - conn = mock.MockBlockingConn() - queue = Queue.Queue() - reader = LineReader(conn, queue) - self.failUnless(queue.empty()) - conn.put_bytes('Foo bar\r\n') - read = queue.get() - self.assertEqual('Foo bar\r\n', read) - - -class MockTableTest(unittest.TestCase): - def test_create_from_dict(self): - table = mock.MockTable({'Key':'Value'}) - self.assertEqual('Value', table.get('KEY')) - self.assertEqual('Value', table['key']) - - def test_create_from_list(self): - table = mock.MockTable([('Key', 'Value')]) - self.assertEqual('Value', table.get('KEY')) - self.assertEqual('Value', table['key']) - - def test_create_from_tuple(self): - table = mock.MockTable((('Key', 'Value'),)) - self.assertEqual('Value', table.get('KEY')) - self.assertEqual('Value', table['key']) - - def test_set_and_get(self): - table = mock.MockTable() - self.assertEqual(None, table.get('Key')) - table['Key'] = 'Value' - self.assertEqual('Value', table.get('Key')) - self.assertEqual('Value', table.get('key')) - self.assertEqual('Value', table.get('KEY')) - self.assertEqual('Value', table['Key']) - self.assertEqual('Value', table['key']) - self.assertEqual('Value', table['KEY']) - - -if __name__ == '__main__': - unittest.main() - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_msgutil.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_msgutil.py deleted file mode 100644 index 16b88e0..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_msgutil.py +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Tests for msgutil module.""" - - -import Queue -import unittest - -import config # This must be imported before mod_pywebsocket. -from mod_pywebsocket import msgutil - -import mock - - -def _create_request(read_data): - return mock.MockRequest(connection=mock.MockConn(read_data)) - -def _create_blocking_request(): - return mock.MockRequest(connection=mock.MockBlockingConn()) - -class MessageTest(unittest.TestCase): - def test_send_message(self): - request = _create_request('') - msgutil.send_message(request, 'Hello') - self.assertEqual('\x00Hello\xff', request.connection.written_data()) - - def test_send_message_unicode(self): - request = _create_request('') - msgutil.send_message(request, u'\u65e5') - # U+65e5 is encoded as e6,97,a5 in UTF-8 - self.assertEqual('\x00\xe6\x97\xa5\xff', - request.connection.written_data()) - - def test_receive_message(self): - request = _create_request('\x00Hello\xff\x00World!\xff') - self.assertEqual('Hello', msgutil.receive_message(request)) - self.assertEqual('World!', msgutil.receive_message(request)) - - def test_receive_message_unicode(self): - request = _create_request('\x00\xe6\x9c\xac\xff') - # U+672c is encoded as e6,9c,ac in UTF-8 - self.assertEqual(u'\u672c', msgutil.receive_message(request)) - - def test_receive_message_erroneous_unicode(self): - # \x80 and \x81 are invalid as UTF-8. - request = _create_request('\x00\x80\x81\xff') - # Invalid characters should be replaced with - # U+fffd REPLACEMENT CHARACTER - self.assertEqual(u'\ufffd\ufffd', msgutil.receive_message(request)) - - def test_receive_message_discard(self): - request = _create_request('\x80\x06IGNORE\x00Hello\xff' - '\x01DISREGARD\xff\x00World!\xff') - self.assertEqual('Hello', msgutil.receive_message(request)) - self.assertEqual('World!', msgutil.receive_message(request)) - - def test_payload_length(self): - for length, bytes in ((0, '\x00'), (0x7f, '\x7f'), (0x80, '\x81\x00'), - (0x1234, '\x80\xa4\x34')): - self.assertEqual(length, - msgutil._payload_length(_create_request(bytes))) - - def test_receive_bytes(self): - request = _create_request('abcdefg') - self.assertEqual('abc', msgutil._receive_bytes(request, 3)) - self.assertEqual('defg', msgutil._receive_bytes(request, 4)) - - def test_read_until(self): - request = _create_request('abcXdefgX') - self.assertEqual('abc', msgutil._read_until(request, 'X')) - self.assertEqual('defg', msgutil._read_until(request, 'X')) - - -class MessageReceiverTest(unittest.TestCase): - def test_queue(self): - request = _create_blocking_request() - receiver = msgutil.MessageReceiver(request) - - self.assertEqual(None, receiver.receive_nowait()) - - request.connection.put_bytes('\x00Hello!\xff') - self.assertEqual('Hello!', receiver.receive()) - - def test_onmessage(self): - onmessage_queue = Queue.Queue() - def onmessage_handler(message): - onmessage_queue.put(message) - - request = _create_blocking_request() - receiver = msgutil.MessageReceiver(request, onmessage_handler) - - request.connection.put_bytes('\x00Hello!\xff') - self.assertEqual('Hello!', onmessage_queue.get()) - - -class MessageSenderTest(unittest.TestCase): - def test_send(self): - request = _create_blocking_request() - sender = msgutil.MessageSender(request) - - sender.send('World') - self.assertEqual('\x00World\xff', request.connection.written_data()) - - def test_send_nowait(self): - # Use a queue to check the bytes written by MessageSender. - # request.connection.written_data() cannot be used here because - # MessageSender runs in a separate thread. - send_queue = Queue.Queue() - def write(bytes): - send_queue.put(bytes) - request = _create_blocking_request() - request.connection.write = write - - sender = msgutil.MessageSender(request) - - sender.send_nowait('Hello') - sender.send_nowait('World') - self.assertEqual('\x00Hello\xff', send_queue.get()) - self.assertEqual('\x00World\xff', send_queue.get()) - - -if __name__ == '__main__': - unittest.main() - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_util.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_util.py deleted file mode 100644 index 61f0db5..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/test_util.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Tests for util module.""" - - -import os -import sys -import unittest - -import config # This must be imported before mod_pywebsocket. -from mod_pywebsocket import util - -_TEST_DATA_DIR = os.path.join(os.path.split(__file__)[0], 'testdata') - -class UtilTest(unittest.TestCase): - def test_get_stack_trace(self): - self.assertEqual('None\n', util.get_stack_trace()) - try: - a = 1 / 0 # Intentionally raise exception. - except Exception: - trace = util.get_stack_trace() - self.failUnless(trace.startswith('Traceback')) - self.failUnless(trace.find('ZeroDivisionError') != -1) - - def test_prepend_message_to_exception(self): - exc = Exception('World') - self.assertEqual('World', str(exc)) - util.prepend_message_to_exception('Hello ', exc) - self.assertEqual('Hello World', str(exc)) - - def test_get_script_interp(self): - cygwin_path = 'c:\\cygwin\\bin' - cygwin_perl = os.path.join(cygwin_path, 'perl') - self.assertEqual(None, util.get_script_interp( - os.path.join(_TEST_DATA_DIR, 'README'))) - self.assertEqual(None, util.get_script_interp( - os.path.join(_TEST_DATA_DIR, 'README'), cygwin_path)) - self.assertEqual('/usr/bin/perl -wT', util.get_script_interp( - os.path.join(_TEST_DATA_DIR, 'hello.pl'))) - self.assertEqual(cygwin_perl + ' -wT', util.get_script_interp( - os.path.join(_TEST_DATA_DIR, 'hello.pl'), cygwin_path)) - - -if __name__ == '__main__': - unittest.main() - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/README b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/README deleted file mode 100644 index c001aa5..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/README +++ /dev/null @@ -1 +0,0 @@ -Test data directory diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/blank_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/blank_wsh.py deleted file mode 100644 index 7f87c6a..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/blank_wsh.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -# intentionally left blank diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/origin_check_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/origin_check_wsh.py deleted file mode 100644 index 2c139fa..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/origin_check_wsh.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -def web_socket_do_extra_handshake(request): - if request.ws_origin == 'http://example.com': - return - raise ValueError('Unacceptable origin: %r' % request.ws_origin) - - -def web_socket_transfer_data(request): - request.connection.write('origin_check_wsh.py is called for %s, %s' % - (request.ws_resource, request.ws_protocol)) - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py deleted file mode 100644 index b982d02..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/exception_in_transfer_wsh.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Exception in web_socket_transfer_data(). -""" - - -def web_socket_do_extra_handshake(request): - pass - - -def web_socket_transfer_data(request): - raise Exception('Intentional Exception for %s, %s' % - (request.ws_resource, request.ws_protocol)) - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py deleted file mode 100644 index 17e7be1..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/no_wsh_at_the_end.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Correct signatures, wrong file name. -""" - - -def web_socket_do_extra_handshake(request): - pass - - -def web_socket_transfer_data(request): - request.connection.write( - 'sub/no_wsh_at_the_end.py is called for %s, %s' % - (request.ws_resource, request.ws_protocol)) - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py deleted file mode 100644 index 26352eb..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Non-callable handlers. -""" - - -web_socket_do_extra_handshake = True -web_socket_transfer_data = 1 - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/plain_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/plain_wsh.py deleted file mode 100644 index db3ff69..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/plain_wsh.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -def web_socket_do_extra_handshake(request): - pass - - -def web_socket_transfer_data(request): - request.connection.write('sub/plain_wsh.py is called for %s, %s' % - (request.ws_resource, request.ws_protocol)) - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py deleted file mode 100644 index 6bf659b..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_handshake_sig_wsh.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Wrong web_socket_do_extra_handshake signature. -""" - - -def no_web_socket_do_extra_handshake(request): - pass - - -def web_socket_transfer_data(request): - request.connection.write( - 'sub/wrong_handshake_sig_wsh.py is called for %s, %s' % - (request.ws_resource, request.ws_protocol)) - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py deleted file mode 100644 index e0e2e55..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/wrong_transfer_sig_wsh.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2009, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -"""Wrong web_socket_transfer_data() signature. -""" - - -def web_socket_do_extra_handshake(request): - pass - - -def no_web_socket_transfer_data(request): - request.connection.write( - 'sub/wrong_transfer_sig_wsh.py is called for %s, %s' % - (request.ws_resource, request.ws_protocol)) - - -# vi:sts=4 sw=4 et diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/hello.pl b/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/hello.pl deleted file mode 100644 index 9dd01b4..0000000 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/hello.pl +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/perl -wT -print "Hello\n"; diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py index 2c45ab6..ac7a760 100644 --- a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py +++ b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py @@ -77,8 +77,8 @@ class QueueEngine: self._output_tee = OutputTee() log_date_format = "%Y-%m-%d %H:%M:%S" - sleep_duration_text = "5 mins" - seconds_to_sleep = 300 + sleep_duration_text = "2 mins" # This could be generated from seconds_to_sleep + seconds_to_sleep = 120 handled_error_code = 2 # Child processes exit with a special code to the parent queue process can detect the error was handled. @@ -142,8 +142,12 @@ class QueueEngine: self._output_tee.remove_log(self._work_log) self._work_log = None + def _now(self): + """Overriden by the unit tests to allow testing _sleep_message""" + return datetime.now() + def _sleep_message(self, message): - wake_time = datetime.now() + timedelta(seconds=self.seconds_to_sleep) + wake_time = self._now() + timedelta(seconds=self.seconds_to_sleep) return "%s Sleeping until %s (%s)." % (message, wake_time.strftime(self.log_date_format), self.sleep_duration_text) def _sleep(self, message): diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py index 626181d..ec91bdb 100644 --- a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py +++ b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py @@ -26,6 +26,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import datetime import os import shutil import tempfile @@ -160,6 +161,17 @@ class QueueEngineTest(unittest.TestCase): expected_callbacks.append('should_continue_work_queue') self.assertEquals(delegate._callbacks, expected_callbacks) + def test_now(self): + """Make sure there are no typos in the QueueEngine.now() method.""" + engine = QueueEngine("test", None, None) + self.assertTrue(isinstance(engine._now(), datetime.datetime)) + + def test_sleep_message(self): + engine = QueueEngine("test", None, None) + engine._now = lambda: datetime.datetime(2010, 1, 1) + expected_sleep_message = "MESSAGE Sleeping until 2010-01-01 00:02:00 (2 mins)." + self.assertEqual(engine._sleep_message("MESSAGE"), expected_sleep_message) + def setUp(self): self.temp_dir = tempfile.mkdtemp(suffix="work_queue_test_logs") diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py index dd048a1..c375ff9 100644 --- a/WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py +++ b/WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py @@ -33,7 +33,7 @@ from webkitpy.common.net.buildbot import Builder from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.thirdparty.mock import Mock from webkitpy.tool.bot.sheriff import Sheriff -from webkitpy.tool.mocktool import MockTool, mock_builder +from webkitpy.tool.mocktool import MockTool class MockSheriffBot(object): diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/download.py b/WebKitTools/Scripts/webkitpy/tool/commands/download.py index c66b95c..a283da9 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/download.py +++ b/WebKitTools/Scripts/webkitpy/tool/commands/download.py @@ -260,8 +260,9 @@ class AbstractRolloutPrepCommand(AbstractSequencedCommand): # of create-rollout for bug URLs. It should do better # parsing instead. log("Preparing rollout for bug %s." % commit_info.bug_id()) - return commit_info - log("Unable to parse bug number from diff.") + else: + log("Unable to parse bug number from diff.") + return commit_info def _prepare_state(self, options, args, tool): revision = args[0] diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py index 926037c..4dd9d7f 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py +++ b/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py @@ -26,9 +26,32 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import unittest + +from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.thirdparty.mock import Mock from webkitpy.tool.commands.commandtest import CommandsTest from webkitpy.tool.commands.download import * +from webkitpy.tool.mocktool import MockTool + + +class AbstractRolloutPrepCommandTest(unittest.TestCase): + def test_commit_info(self): + command = AbstractRolloutPrepCommand() + tool = MockTool() + command.bind_to_tool(tool) + output = OutputCapture() + + expected_stderr = "Preparing rollout for bug 42.\n" + commit_info = output.assert_outputs(self, command._commit_info, [1234], expected_stderr=expected_stderr) + self.assertTrue(commit_info) + + mock_commit_info = Mock() + mock_commit_info.bug_id = lambda: None + tool._checkout.commit_info_for_revision = lambda revision: mock_commit_info + expected_stderr = "Unable to parse bug number from diff.\n" + commit_info = output.assert_outputs(self, command._commit_info, [1234], expected_stderr=expected_stderr) + self.assertEqual(commit_info, mock_commit_info) class DownloadCommandsTest(CommandsTest): diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py index 4d23a4c..3d0ddd1 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py +++ b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py @@ -39,37 +39,37 @@ class EarlyWarningSytemTest(QueuesTest): ews._can_build = lambda: True ews.review_patch(Mock()) - def test_chromium_linux_ews(self): + def _default_expected_stderr(self, ews): + string_replacemnts = { + "name": ews.name, + "checkout_dir": os.getcwd(), # FIXME: Use of os.getcwd() is wrong, should be scm.checkout_root + } expected_stderr = { - "begin_work_queue": "CAUTION: chromium-ews will discard all local changes in \"%s\"\nRunning WebKit chromium-ews.\n" % os.getcwd(), + "begin_work_queue": "CAUTION: %(name)s will discard all local changes in \"%(checkout_dir)s\"\nRunning WebKit %(name)s.\n" % string_replacemnts, "handle_unexpected_error": "Mock error message\n", + "process_work_item": "MOCK: update_status: %(name)s Pass\n" % string_replacemnts, } - self.assert_queue_outputs(ChromiumLinuxEWS(), expected_stderr=expected_stderr) + return expected_stderr + + def _test_ews(self, ews): + self.assert_queue_outputs(ews, expected_stderr=self._default_expected_stderr(ews)) + + # FIXME: If all EWSes are going to output the same text, we + # could test them all in one method with a for loop over an array. + def test_chromium_linux_ews(self): + self._test_ews(ChromiumLinuxEWS()) def test_chromium_windows_ews(self): - expected_stderr = { - "begin_work_queue": "CAUTION: cr-win-ews will discard all local changes in \"%s\"\nRunning WebKit cr-win-ews.\n" % os.getcwd(), - "handle_unexpected_error": "Mock error message\n", - } - self.assert_queue_outputs(ChromiumWindowsEWS(), expected_stderr=expected_stderr) + self._test_ews(ChromiumWindowsEWS()) def test_qt_ews(self): - expected_stderr = { - "begin_work_queue": "CAUTION: qt-ews will discard all local changes in \"%s\"\nRunning WebKit qt-ews.\n" % os.getcwd(), - "handle_unexpected_error": "Mock error message\n", - } - self.assert_queue_outputs(QtEWS(), expected_stderr=expected_stderr) + self._test_ews(QtEWS()) def test_gtk_ews(self): - expected_stderr = { - "begin_work_queue": "CAUTION: gtk-ews will discard all local changes in \"%s\"\nRunning WebKit gtk-ews.\n" % os.getcwd(), - "handle_unexpected_error": "Mock error message\n", - } - self.assert_queue_outputs(GtkEWS(), expected_stderr=expected_stderr) + self._test_ews(GtkEWS()) def test_mac_ews(self): - expected_stderr = { - "begin_work_queue": "CAUTION: mac-ews will discard all local changes in \"%s\"\nRunning WebKit mac-ews.\n" % os.getcwd(), - "handle_unexpected_error": "Mock error message\n", - } - self.assert_queue_outputs(MacEWS(), expected_stderr=expected_stderr) + ews = MacEWS() + expected_stderr = self._default_expected_stderr(ews) + expected_stderr["process_work_item"] = "MOCK: update_status: mac-ews Error: mac-ews cannot process patches from non-committers :(\n" + self.assert_queue_outputs(ews, expected_stderr=expected_stderr) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queues.py b/WebKitTools/Scripts/webkitpy/tool/commands/queues.py index 775aa44..78ca729 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/queues.py +++ b/WebKitTools/Scripts/webkitpy/tool/commands/queues.py @@ -46,7 +46,6 @@ from webkitpy.tool.multicommandtool import Command class AbstractQueue(Command, QueueEngineDelegate): watchers = [ - "webkit-bot-watchers@googlegroups.com", ] _pass_status = "Pass" @@ -168,16 +167,17 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler): # Not using BugzillaQueries.fetch_patches_from_commit_queue() so we can reject patches with invalid committers/reviewers. bug_ids = self.tool.bugs.queries.fetch_bug_ids_from_commit_queue() all_patches = sum([self.tool.bugs.fetch_bug(bug_id).commit_queued_patches(include_invalid=True) for bug_id in bug_ids], []) - valid_patches = self.committer_validator.patches_after_rejecting_invalid_commiters_and_reviewers(all_patches) - if not self._builders_are_green(): - return filter(lambda patch: patch.is_rollout(), valid_patches) - return valid_patches + return self.committer_validator.patches_after_rejecting_invalid_commiters_and_reviewers(all_patches) def next_work_item(self): patches = self._validate_patches_in_commit_queue() + builders_are_green = self._builders_are_green() + if not builders_are_green: + patches = filter(lambda patch: patch.is_rollout(), patches) # FIXME: We could sort the patches in a specific order here, was suggested by https://bugs.webkit.org/show_bug.cgi?id=33395 if not patches: - self._update_status("Empty queue") + queue_text = "queue" if builders_are_green else "rollout queue" + self._update_status("Empty %s" % queue_text) return None # Only bother logging if we have patches in the queue. self.log_progress([patch.id() for patch in patches]) @@ -211,7 +211,8 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler): if not patch.is_rollout(): if not self._builders_are_green(): return False - self._update_status("Landing patch", patch) + patch_text = "rollout patch" if patch.is_rollout() else "patch" + self._update_status("Landing %s" % patch_text, patch) return True def _land(self, patch, first_run=False): @@ -226,7 +227,6 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler): "land-attachment", "--force-clean", "--build", - "--test", "--non-interactive", # The master process is responsible for checking the status # of the builders (see above call to _builders_are_green). @@ -235,6 +235,9 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler): "--quiet", patch.id() ] + # We don't bother to run tests for rollouts as that makes them too slow. + if not patch.is_rollout(): + args.append("--test") if not first_run: # The first time through, we don't reject the patch from the # commit queue because we want to make sure we can build and diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py index 16eb053..0bd42fb 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py +++ b/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py @@ -118,11 +118,13 @@ class CommitQueueTest(QueuesTest): def test_commit_queue(self): expected_stderr = { "begin_work_queue" : "CAUTION: commit-queue will discard all local changes in \"%s\"\nRunning WebKit commit-queue.\n" % MockSCM.fake_checkout_root, + "should_proceed_with_work_item": "MOCK: update_status: commit-queue Landing patch\n", # FIXME: The commit-queue warns about bad committers twice. This is due to the fact that we access Attachment.reviewer() twice and it logs each time. "next_work_item" : """Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com) Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com) 2 patches in commit-queue [197, 106] """, + "process_work_item" : "MOCK: update_status: commit-queue Pass\n", } self.assert_queue_outputs(CommitQueue(), expected_stderr=expected_stderr) @@ -131,11 +133,14 @@ Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.c tool.buildbot.light_tree_on_fire() expected_stderr = { "begin_work_queue" : "CAUTION: commit-queue will discard all local changes in \"%s\"\nRunning WebKit commit-queue.\n" % MockSCM.fake_checkout_root, + "should_proceed_with_work_item": "MOCK: update_status: commit-queue Builders [\"Builder2\"] are red. See http://build.webkit.org\n", # FIXME: The commit-queue warns about bad committers twice. This is due to the fact that we access Attachment.reviewer() twice and it logs each time. "next_work_item" : """Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com) Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com) +MOCK: update_status: commit-queue Builders ["Builder2"] are red. See http://build.webkit.org 1 patch in commit-queue [106] """, + "process_work_item" : "MOCK: update_status: commit-queue Builders [\"Builder2\"] are red. See http://build.webkit.org\n", } self.assert_queue_outputs(CommitQueue(), tool=tool, expected_stderr=expected_stderr) @@ -145,20 +150,33 @@ Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.c rollout_patch = MockPatch() expected_stderr = { "begin_work_queue": "CAUTION: commit-queue will discard all local changes in \"%s\"\nRunning WebKit commit-queue.\n" % MockSCM.fake_checkout_root, + "should_proceed_with_work_item": "MOCK: update_status: commit-queue Landing rollout patch\n", # FIXME: The commit-queue warns about bad committers twice. This is due to the fact that we access Attachment.reviewer() twice and it logs each time. "next_work_item": """Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com) Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com) +MOCK: update_status: commit-queue Builders ["Builder2"] are red. See http://build.webkit.org 1 patch in commit-queue [106] """, - "process_work_item": "MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--build', '--test', '--non-interactive', '--ignore-builders', '--build-style=both', '--quiet', 76543]\n", + "process_work_item": "MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--build', '--non-interactive', '--ignore-builders', '--build-style=both', '--quiet', 76543]\nMOCK: update_status: commit-queue Pass\n", } self.assert_queue_outputs(CommitQueue(), tool=tool, work_item=rollout_patch, expected_stderr=expected_stderr) + def test_can_build_and_test(self): + queue = CommitQueue() + tool = MockTool() + tool.executive = Mock() + queue.bind_to_tool(tool) + self.assertTrue(queue._can_build_and_test()) + expected_run_args = ["echo", "--status-host=example.com", "build-and-test", "--force-clean", "--build", "--test", "--non-interactive", "--no-update", "--build-style=both", "--quiet"] + tool.executive.run_and_throw_if_fail.assert_called_with(expected_run_args) + class StyleQueueTest(QueuesTest): def test_style_queue(self): expected_stderr = { "begin_work_queue" : "CAUTION: style-queue will discard all local changes in \"%s\"\nRunning WebKit style-queue.\n" % MockSCM.fake_checkout_root, + "should_proceed_with_work_item": "MOCK: update_status: style-queue Checking style\n", + "process_work_item" : "MOCK: update_status: style-queue Pass\n", "handle_unexpected_error" : "Mock error message\n", } self.assert_queue_outputs(StyleQueue(), expected_stderr=expected_stderr) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py index eb80d8f..24c8517 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py +++ b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py @@ -57,17 +57,46 @@ class SheriffBot(AbstractQueue, StepSequenceErrorHandler): def work_item_log_path(self, new_failures): return os.path.join("%s-logs" % self.name, "%s.log" % new_failures.keys()[0]) - def next_work_item(self): - self._irc_bot.process_pending_messages() - self._update() + def _new_failures(self, revisions_causing_failures, old_failing_svn_revisions): + # We ignore failures that might have been caused by svn_revisions that + # we've already complained about. This is conservative in the sense + # that we might be ignoring some new failures, but our experience has + # been that skipping this check causes a lot of spam for builders that + # take a long time to cycle. + old_failing_builder_names = [] + for svn_revision in old_failing_svn_revisions: + old_failing_builder_names.extend( + [builder.name() for builder in revisions_causing_failures[svn_revision]]) + new_failures = {} - revisions_causing_failures = self.tool.buildbot.revisions_causing_failures() for svn_revision, builders in revisions_causing_failures.items(): - if self.tool.status_server.svn_revision(svn_revision): + if svn_revision in old_failing_svn_revisions: # FIXME: We should re-process the work item after some time delay. # https://bugs.webkit.org/show_bug.cgi?id=36581 continue - new_failures[svn_revision] = builders + new_builders = [builder for builder in builders + if builder.name() not in old_failing_builder_names] + if new_builders: + new_failures[svn_revision] = new_builders + + return new_failures + + def next_work_item(self): + self._irc_bot.process_pending_messages() + self._update() + + # We do one read from buildbot to ensure a consistent view. + revisions_causing_failures = self.tool.buildbot.revisions_causing_failures() + + # Similarly, we read once from our the status_server. + old_failing_svn_revisions = [] + for svn_revision in revisions_causing_failures.keys(): + if self.tool.status_server.svn_revision(svn_revision): + old_failing_svn_revisions.append(svn_revision) + + new_failures = self._new_failures(revisions_causing_failures, + old_failing_svn_revisions) + self._sheriff.provoke_flaky_builders(revisions_causing_failures) return new_failures diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py index f121eda..4b4b8b6 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py +++ b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py @@ -30,18 +30,44 @@ import os from webkitpy.tool.commands.queuestest import QueuesTest from webkitpy.tool.commands.sheriffbot import SheriffBot -from webkitpy.tool.mocktool import mock_builder +from webkitpy.tool.mocktool import MockBuilder class SheriffBotTest(QueuesTest): + builder1 = MockBuilder("Builder1") + builder2 = MockBuilder("Builder2") + def test_sheriff_bot(self): mock_work_item = { - 29837: [mock_builder], + 29837: [self.builder1], } expected_stderr = { "begin_work_queue": "CAUTION: sheriff-bot will discard all local changes in \"%s\"\nRunning WebKit sheriff-bot.\n" % os.getcwd(), "next_work_item": "", - "process_work_item": "MOCK: irc.post: abarth, darin, eseidel: http://trac.webkit.org/changeset/29837 might have broken Mock builder name (Tests)\nMOCK bug comment: bug_id=42, cc=['webkit-bot-watchers@googlegroups.com', 'abarth@webkit.org', 'eric@webkit.org']\n--- Begin comment ---\\http://trac.webkit.org/changeset/29837 might have broken Mock builder name (Tests)\n--- End comment ---\n\n", + "process_work_item": "MOCK: irc.post: abarth, darin, eseidel: http://trac.webkit.org/changeset/29837 might have broken Builder1\nMOCK bug comment: bug_id=42, cc=['abarth@webkit.org', 'eric@webkit.org']\n--- Begin comment ---\\http://trac.webkit.org/changeset/29837 might have broken Builder1\n--- End comment ---\n\n", "handle_unexpected_error": "Mock error message\n" } self.assert_queue_outputs(SheriffBot(), work_item=mock_work_item, expected_stderr=expected_stderr) + + revisions_causing_failures = { + 1234: [builder1], + 1235: [builder1, builder2], + } + + def test_new_failures(self): + old_failing_svn_revisions = [] + self.assertEquals(SheriffBot()._new_failures(self.revisions_causing_failures, + old_failing_svn_revisions), + self.revisions_causing_failures) + + def test_new_failures_with_old_revisions(self): + old_failing_svn_revisions = [1234] + self.assertEquals(SheriffBot()._new_failures(self.revisions_causing_failures, + old_failing_svn_revisions), + {1235: [builder2]}) + + def test_new_failures_with_old_revisions(self): + old_failing_svn_revisions = [1235] + self.assertEquals(SheriffBot()._new_failures(self.revisions_causing_failures, + old_failing_svn_revisions), + {}) diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/upload.py b/WebKitTools/Scripts/webkitpy/tool/commands/upload.py index 99d45a6..4797ef6 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/upload.py +++ b/WebKitTools/Scripts/webkitpy/tool/commands/upload.py @@ -46,14 +46,23 @@ from webkitpy.tool.comments import bug_comment_from_svn_revision from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand from webkitpy.common.system.deprecated_logging import error, log + class CommitMessageForCurrentDiff(AbstractDeclarativeCommand): name = "commit-message" help_text = "Print a commit message suitable for the uncommitted changes" + def __init__(self): + options = [ + steps.Options.squash, + steps.Options.git_commit, + ] + AbstractDeclarativeCommand.__init__(self, options=options) + def execute(self, options, args, tool): # This command is a useful test to make sure commit_message_for_this_commit # always returns the right value regardless of the current working directory. - print "%s" % tool.checkout().commit_message_for_this_commit().message() + print "%s" % tool.checkout().commit_message_for_this_commit(options.git_commit, options.squash).message() + class CleanPendingCommit(AbstractDeclarativeCommand): name = "clean-pending-commit" diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py index eec3751..8f6483a 100644 --- a/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py +++ b/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py @@ -34,10 +34,7 @@ from webkitpy.tool.mocktool import MockTool class UploadCommandsTest(CommandsTest): def test_commit_message_for_current_diff(self): tool = MockTool() - mock_commit_message_for_this_commit = Mock() - mock_commit_message_for_this_commit.message = lambda: "Mock message" - tool._checkout.commit_message_for_this_commit = lambda: mock_commit_message_for_this_commit - expected_stdout = "Mock message\n" + expected_stdout = "This is a fake commit message that is at least 50 characters.\n" self.assert_execute_outputs(CommitMessageForCurrentDiff(), [], expected_stdout=expected_stdout, tool=tool) def test_clean_pending_commit(self): diff --git a/WebKitTools/Scripts/webkitpy/tool/mocktool.py b/WebKitTools/Scripts/webkitpy/tool/mocktool.py index 128362a..47fff1b 100644 --- a/WebKitTools/Scripts/webkitpy/tool/mocktool.py +++ b/WebKitTools/Scripts/webkitpy/tool/mocktool.py @@ -184,15 +184,6 @@ _bug4 = { } -class MockBuilder(object): - - def name(self): - return "Mock builder name (Tests)" - - -mock_builder = MockBuilder() - - class MockBugzillaQueries(Mock): def __init__(self, bugzilla): @@ -319,6 +310,9 @@ class MockBuilder(object): def __init__(self, name): self._name = name + def name(self): + return self._name + def force_build(self, username, comments): log("MOCK: force_build: name=%s, username=%s, comments=%s" % ( self._name, username, comments)) @@ -369,7 +363,7 @@ class MockBuildBot(object): def revisions_causing_failures(self): return { - "29837": [mock_builder] + "29837": [self.builder_with_name("Builder1")], } @@ -484,6 +478,7 @@ class MockStatusServer(object): return None def update_status(self, queue_name, status, patch=None, results_file=None): + log("MOCK: update_status: %s %s" % (queue_name, status)) return 187 def update_svn_revision(self, svn_revision, broken_bot): @@ -491,7 +486,12 @@ class MockStatusServer(object): class MockExecute(Mock): + def __init__(self, should_log): + self._should_log = should_log + def run_and_throw_if_fail(self, args, quiet=False): + if self._should_log: + log("MOCK run_and_throw_if_fail: %s" % args) return "MOCK output of child process" def run_command(self, @@ -502,6 +502,8 @@ class MockExecute(Mock): return_exit_code=False, return_stderr=True, decode_output=False): + if self._should_log: + log("MOCK run_command: %s" % args) return "MOCK output of child process" @@ -511,9 +513,7 @@ class MockTool(): self.wakeup_event = threading.Event() self.bugs = MockBugzilla() self.buildbot = MockBuildBot() - self.executive = MockExecute() - if log_executive: - self.executive.run_and_throw_if_fail = lambda args: log("MOCK run_and_throw_if_fail: %s" % args) + self.executive = MockExecute(should_log=log_executive) self._irc = None self.user = MockUser() self._scm = MockSCM() diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/checkstyle.py b/WebKitTools/Scripts/webkitpy/tool/steps/checkstyle.py index 7b2be99..93e6215 100644 --- a/WebKitTools/Scripts/webkitpy/tool/steps/checkstyle.py +++ b/WebKitTools/Scripts/webkitpy/tool/steps/checkstyle.py @@ -48,16 +48,17 @@ class CheckStyle(AbstractStep): if not self._options.check_style: return os.chdir(self._tool.scm().checkout_root) - try: - args = [] - if self._options.git_commit: - args.append("--git-commit") - args.append(self._options.git_commit) - if self._tool.scm().should_squash(self._options.squash): - args.append("--squash") - else: - args.append("--no-squash") + args = [] + if self._options.git_commit: + args.append("--git-commit") + args.append(self._options.git_commit) + if self._tool.scm().should_squash(self._options.squash): + args.append("--squash") + else: + args.append("--no-squash") + + try: self._run_script("check-webkit-style", args) except ScriptError, e: if self._options.non_interactive: diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/run_all.py b/WebKitTools/Scripts/webkitpy/tool/steps/checkstyle_unittest.py index 3885618..a23ea1b 100644 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/run_all.py +++ b/WebKitTools/Scripts/webkitpy/tool/steps/checkstyle_unittest.py @@ -1,19 +1,16 @@ -#!/usr/bin/env python -# -# Copyright 2009, Google Inc. -# All rights reserved. +# Copyright (C) 2009 Google Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # -# * Redistributions of source code must retain the above copyright +# * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above +# * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following disclaimer # in the documentation and/or other materials provided with the # distribution. -# * Neither the name of Google Inc. nor the names of its +# * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # @@ -29,36 +26,21 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""Run all tests in the same directory. -""" - - -import os -import re import unittest +from webkitpy.common.system.executive import ScriptError +from webkitpy.thirdparty.mock import Mock +from webkitpy.tool.mocktool import MockTool +from webkitpy.tool.steps.checkstyle import CheckStyle -_TEST_MODULE_PATTERN = re.compile(r'^(test_.+)\.py$') - - -def _list_test_modules(directory): - module_names = [] - for filename in os.listdir(directory): - match = _TEST_MODULE_PATTERN.search(filename) - if match: - module_names.append(match.group(1)) - return module_names - - -def _suite(): - loader = unittest.TestLoader() - return loader.loadTestsFromNames( - _list_test_modules(os.path.join(os.path.split(__file__)[0], '.'))) - - -if __name__ == '__main__': - unittest.main(defaultTest='_suite') +class CheckStyleTest(unittest.TestCase): + def test_should_squash_error(self): + """should_squash can throw an error. That error should not be eaten by CheckStyle.""" + def should_squash(squash): + raise ScriptError(message="Dummy error") -# vi:sts=4 sw=4 et + tool = MockTool() + tool._scm.should_squash = should_squash + step = CheckStyle(tool, Mock()) + self.assertRaises(ScriptError, step.run, []) diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/postcodereview.py b/WebKitTools/Scripts/webkitpy/tool/steps/postcodereview.py index 198cfce..8397519 100644 --- a/WebKitTools/Scripts/webkitpy/tool/steps/postcodereview.py +++ b/WebKitTools/Scripts/webkitpy/tool/steps/postcodereview.py @@ -42,33 +42,32 @@ class PostCodeReview(AbstractStep): def run(self, state): if not self._options.fancy_review: return - # FIXME: This will always be None because we don't retrieve the issue - # number from the ChangeLog yet. - codereview_issue = state.get("codereview_issue") + + bug_id = state.get("bug_id") + if not bug_id: + raise ScriptError(message="Cannot upload a fancy review without a bug ID.") + message = self._options.description if not message: # If we have an issue number, then the message becomes the label # of the new patch. Otherwise, it becomes the title of the whole # issue. - if codereview_issue: - message = "Updated patch" - elif state.get("bug_title"): + if state.get("bug_title"): # This is the common case for the the first "upload" command. message = state.get("bug_title") - elif state.get("bug_id"): + elif bug_id: # This is the common case for the "post" command and - # subsequent runs of the "upload" command. In this case, - # I'd rather add the new patch to the existing issue, but - # that's not implemented yet. - message = "Code review for %s" % self._tool.bugs.bug_url_for_bug_id(state["bug_id"]) + # subsequent runs of the "upload" command. + message = "Code review for %s" % self._tool.bugs.bug_url_for_bug_id(bug_id) else: # Unreachable with our current commands, but we might hit # this case if we support bug-less code reviews. message = "Code review" + + # Use the bug ID as the rietveld issue number. This means rietveld code reviews + # when there are multiple different patches on a bug will be a bit wonky, but + # webkit-patch assumes one-patch-per-bug. created_issue = self._tool.codereview.post(diff=self.cached_lookup(state, "diff"), message=message, - codereview_issue=codereview_issue, + codereview_issue=bug_id, cc=self._options.cc) - if created_issue: - # FIXME: Record the issue number in the ChangeLog. - state["codereview_issue"] = created_issue diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/postdiff.py b/WebKitTools/Scripts/webkitpy/tool/steps/postdiff.py index a542dba..79739cd 100644 --- a/WebKitTools/Scripts/webkitpy/tool/steps/postdiff.py +++ b/WebKitTools/Scripts/webkitpy/tool/steps/postdiff.py @@ -44,11 +44,6 @@ class PostDiff(AbstractStep): diff = self.cached_lookup(state, "diff") description = self._options.description or "Patch" comment_text = None - codereview_issue = state.get("codereview_issue") - # Include codereview issue number in patch name. This is a bit of a hack, - # but it makes doing the rietveld integration a lot easier. - if codereview_issue: - description += "-%s" % state["codereview_issue"] self._tool.bugs.add_patch_to_bug(state["bug_id"], diff, description, comment_text=comment_text, mark_for_review=self._options.review, mark_for_commit_queue=self._options.request_commit) if self._options.open_bug: self._tool.user.open_url(self._tool.bugs.bug_url_for_bug_id(state["bug_id"])) diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/runtests.py b/WebKitTools/Scripts/webkitpy/tool/steps/runtests.py index b1c2d3b..0734d8f 100644 --- a/WebKitTools/Scripts/webkitpy/tool/steps/runtests.py +++ b/WebKitTools/Scripts/webkitpy/tool/steps/runtests.py @@ -57,6 +57,11 @@ class RunTests(AbstractStep): if self._options.non_interactive: args.append("--no-launch-safari") args.append("--exit-after-n-failures=1") + # FIXME: Hack to work around https://bugs.webkit.org/show_bug.cgi?id=38912 + # when running the commit-queue on a mac leopard machine. + if self.port().name() == "Mac" and self.port().is_leopard(): + args.extend(["--ignore-tests", "compositing/iframes"]) + if self._options.quiet: args.append("--quiet") self._tool.executive.run_and_throw_if_fail(args) diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py b/WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py index 5abfc6d..eee183b 100644 --- a/WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py +++ b/WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py @@ -29,9 +29,11 @@ import unittest from webkitpy.common.system.outputcapture import OutputCapture +from webkitpy.common.config.ports import WebKitPort from webkitpy.thirdparty.mock import Mock from webkitpy.tool.mocktool import MockTool from webkitpy.tool.steps.update import Update +from webkitpy.tool.steps.runtests import RunTests from webkitpy.tool.steps.promptforbugortitle import PromptForBugOrTitle @@ -55,3 +57,27 @@ class StepsTest(unittest.TestCase): tool = MockTool() tool.user.prompt = lambda message: 42 self._run_step(PromptForBugOrTitle, tool=tool) + + def test_runtests_leopard_commit_queue_hack(self): + expected_stderr = "Running Python unit tests\nRunning Perl unit tests\nRunning JavaScriptCore tests\nRunning run-webkit-tests\n" + OutputCapture().assert_outputs(self, self._run_step, [RunTests], expected_stderr=expected_stderr) + + def test_runtests_leopard_commit_queue_hack(self): + mock_options = Mock() + mock_options.non_interactive = True + step = RunTests(MockTool(log_executive=True), mock_options) + # FIXME: We shouldn't use a real port-object here, but there is too much to mock at the moment. + mock_port = WebKitPort() + mock_port.name = lambda: "Mac" + mock_port.is_leopard = lambda: True + step.port = lambda: mock_port + expected_stderr = """Running Python unit tests +MOCK run_and_throw_if_fail: ['WebKitTools/Scripts/test-webkitpy'] +Running Perl unit tests +MOCK run_and_throw_if_fail: ['WebKitTools/Scripts/test-webkitperl'] +Running JavaScriptCore tests +MOCK run_and_throw_if_fail: ['WebKitTools/Scripts/run-javascriptcore-tests'] +Running run-webkit-tests +MOCK run_and_throw_if_fail: ['WebKitTools/Scripts/run-webkit-tests', '--no-launch-safari', '--exit-after-n-failures=1', '--ignore-tests', 'compositing/iframes', '--quiet'] +""" + OutputCapture().assert_outputs(self, step.run, [{}], expected_stderr=expected_stderr) |