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 | |
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')
164 files changed, 5087 insertions, 4658 deletions
diff --git a/WebKitTools/ChangeLog b/WebKitTools/ChangeLog index 70b10f0..3596d14 100644 --- a/WebKitTools/ChangeLog +++ b/WebKitTools/ChangeLog @@ -1,3 +1,1529 @@ +2010-05-17 Sheriff Bot <webkit.review.bot@gmail.com> + + Unreviewed, rolling out r59631. + http://trac.webkit.org/changeset/59631 + https://bugs.webkit.org/show_bug.cgi?id=39255 + + chromium canaries can no longer run webkit_tests, suspect this + change. (Requested by atwilson on #webkit). + + * Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py: + * Scripts/webkitpy/layout_tests/run_webkit_tests.py: + * Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py: + +2010-05-17 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Attempt to make new-run-webkit-tests --help more sane + https://bugs.webkit.org/show_bug.cgi?id=37836 + + * Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py: + - Add a FIXME about options.singly and options.batch_size being different. + * Scripts/webkitpy/layout_tests/run_webkit_tests.py: + - Add support for hidden options. + - Add option groupings to attempt to simplify --help. + - Fix a bunch of option helps to start with a capitalized verb. + - Hide a bunch of options which make no sense to users. + - Sort options in --help. + * Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py: + - Add tests for option sorting. + +2010-05-17 Robert Hogan <robert@webkit.org> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Disable Icon Database by default in Qt DRT + + Unskip: + http/tests/misc/favicon-loads-with-images-disabled.html + http/tests/security/XFrameOptions/x-frame-options-deny-meta-tag-in-body.html + http/tests/security/XFrameOptions/x-frame-options-deny-meta-tag-parent-same-origin-deny.html + http/tests/security/XFrameOptions/x-frame-options-deny-meta-tag.html + + https://bugs.webkit.org/show_bug.cgi?id=37382 + + Add support for layoutTestController.setIconDatabaseEnabled and layoutTestController.disableImageLoading(). + The XFrameOptions tests were failing because of an extra resource load callback for favicon.ico requests. + These extra callbacks are removed by supporting both of the above layoutTestContoller commands. + + * DumpRenderTree/qt/DumpRenderTreeQt.cpp: + (WebCore::WebPage::resetSettings): + (WebCore::DumpRenderTree::DumpRenderTree): + * DumpRenderTree/qt/DumpRenderTreeQt.h: + (WebCore::DumpRenderTree::drtStoragePath): + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::reset): + (LayoutTestController::disableImageLoading): + (LayoutTestController::setIconDatabaseEnabled): + * DumpRenderTree/qt/LayoutTestControllerQt.h: + +2010-05-17 Fumitoshi Ukai <ukai@chromium.org> + + Reviewed by Eric Seidel. + + Chromium: Add --chromium option to new-run-webkit-websocketserver + https://bugs.webkit.org/show_bug.cgi?id=37664 + + os.environ setup and setup_mount for cygwin are moved in ChromiumWinPort.setup_environ_for_server. + + * Scripts/new-run-webkit-httpd: + Remove passing register_cygwin. + * Scripts/new-run-webkit-websocketserver: + Add --chromium flag. + Remove passing register_cygwin. + Create port object using options. + * Scripts/webkitpy/layout_tests/port/base.py: + Add setup_environ_for_server(). + * Scripts/webkitpy/layout_tests/port/chromium_win.py: + Ditto. + * Scripts/webkitpy/layout_tests/port/factory_unittest.py: Added. + * Scripts/webkitpy/layout_tests/port/http_server.py: + Remove register_cygwin_parameter. + Call setup_environ_for_server(). + * Scripts/webkitpy/layout_tests/port/websocket_server.py: + Ditto. + +2010-05-16 Daniel Bates <dbates@rim.com> + + Reviewed by Chris Jerdonek. + + https://bugs.webkit.org/show_bug.cgi?id=39184 + + Adds function VCSUtils::parseSvnProperty to parse an SVN property with + either a single-line or multi-line value change. + + * Scripts/VCSUtils.pm: + - Added function parseSvnProperty. We will use this function + towards resolving Bug #38885 <https://bugs.webkit.org/show_bug.cgi?id=38885>. + - Removed FIXME comment above function parseSvnPropertyValue, since + it is being used by parseSvnProperty. + - Modified function parseSvnPropertyValue to break out of "while (<$fileHandle>)" + loop when it encounters the start of the next property so that it can be + processed by its caller, parseSvnPropertyValue. We reference this bullet below + by (*). + * Scripts/webkitperl/VCSUtils_unittest/parseSvnProperty.pl: Added. + - Added unit tests. + * Scripts/webkitperl/VCSUtils_unittest/parseSvnPropertyValue.pl: + - Changed the name of the unit test "simple multi-line '-' change" to + "single-line '-' change followed by empty line" since the former was an + incorrect description of this test. + - Added unit test "single-line '-' change followed by the next property", and + "multi-line '-' change followed by the next property" to test (*) above. + +2010-05-16 Tony Chang <tony@chromium.org> + + Not reviewed, fixing layout test. + + Don't output Inspect Element since this is not enabled on the bots, + but most developers probably have it installed. + + * DumpRenderTree/mac/EventSendingController.mm: + (-[EventSendingController contextClick:]): + +2010-05-12 Tony Chang <tony@chromium.org> + + Reviewed by Darin Adler. + + Spellcheck disabling does not disable context menu + https://bugs.webkit.org/show_bug.cgi?id=25639 + + * DumpRenderTree/mac/EventSendingController.mm: + (+[EventSendingController isSelectorExcludedFromWebScript:]): + (+[EventSendingController webScriptNameForSelector:]): + (-[EventSendingController contextClick:]): add a bool parameter that + when true, dumps the context menu items to stdout. + +2010-05-16 Robert Hogan <robert@webkit.org> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Unskip fast/loader/main-document-url-for-non-http-loads.html + + Update Qt DRT to use frame loader, editor client and notification presenter + functions in DumpRenderTreeSupportQt. + + https://bugs.webkit.org/show_bug.cgi?id=38867 + + * DumpRenderTree/qt/DumpRenderTreeQt.cpp: + (WebCore::DumpRenderTree::open): + (WebCore::DumpRenderTree::dump): + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::LayoutTestController): + (LayoutTestController::reset): + (LayoutTestController::dumpEditingCallbacks): + (LayoutTestController::dumpFrameLoadCallbacks): + (LayoutTestController::dumpResourceLoadCallbacks): + (LayoutTestController::setWillSendRequestReturnsNullOnRedirect): + (LayoutTestController::setWillSendRequestReturnsNull): + (LayoutTestController::setWillSendRequestClearHeader): + +2010-05-16 Eric Seidel <eric@webkit.org> + + Unreviewed, rolling out r59571. + http://trac.webkit.org/changeset/59571 + https://bugs.webkit.org/show_bug.cgi?id=39054 + + Broke Cr Win, but we didn't notice immediately due to + https://bugs.webkit.org/show_bug.cgi?id=38926. It's possible + that this didn't actually break Cr Win, but rather that bug + 38926 necessitates a clean compile after this and sucessive + checkins only produced a partial recompile and thus failed to + build. + + * DumpRenderTree/chromium/WebViewHost.cpp: + (WebViewHost::canvas): + +2010-05-16 Robert Hogan <robert@webkit.org> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt]Unskip security/set-form-autocomplete-attribute.html + + Add support for layoutTestController.elementDoesAutoCompleteForElementWithId(). + + https://bugs.webkit.org/show_bug.cgi?id=38859 + + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::elementDoesAutoCompleteForElementWithId): + * DumpRenderTree/qt/LayoutTestControllerQt.h: + +2010-05-16 Kent Tamura <tkent@chromium.org> + + Reviewed by Dimitri Glazkov. + + [DRT/Chromium] Fix repaint, WebGL, textarea tests + https://bugs.webkit.org/show_bug.cgi?id=39054 + + * DumpRenderTree/chromium/WebViewHost.cpp: + (WebViewHost::canvas): Remove m_paintRect initialization in canvas(). + This line updated m_paintRect unexpectedly during paintRect(). + We don't need to initialize m_paintRect because show() does it. + +2010-05-16 Sergio Villar Senin <svillar@igalia.com> + + Reviewed by Gustavo Noronha Silva. + + [GTK] Improve reporting of frame loader callbacks in DRT + https://bugs.webkit.org/show_bug.cgi?id=36454 + + * DumpRenderTree/gtk/DumpRenderTree.cpp: + (webViewLoadStatusNotified): + (createWebView): added connection to notify::load-status and + signal callback + +2010-05-15 Daniel Bates <dbates@rim.com> + + Reviewed by Chris Jerdonek. + + https://bugs.webkit.org/show_bug.cgi?id=39170 + + Add function parseSvnPropertyValue to parse single-line and multi-line + property values of an SVN property change. + + * Scripts/VCSUtils.pm: + Added function parseSvnPropertyValue. We will use this as part of + Bug #38885 <https://bugs.webkit.org/show_bug.cgi?id=38885>. + * Scripts/webkitperl/VCSUtils_unittest/parseSvnPropertyValue.pl: Added. + +2010-05-15 Jochen Eisinger <jochen@chromium.org> + + Reviewed by Dmitry Titov. + + Add allowDatabase method to TestWebWorker. + https://bugs.webkit.org/show_bug.cgi?id=38742 + + * DumpRenderTree/chromium/TestWebWorker.h: + (TestWebWorker::allowDatabase): + +2010-05-15 Simon Hausmann <simon.hausmann@nokia.com> + + Rubber-stamped by Antti Koivisto. + + [Qt] Rename QtLauncher to QtTestBrowser + https://bugs.webkit.org/show_bug.cgi?id=37665 + + Forgot to remove the original directory after the rename. + + * QtLauncher: Removed. + +2010-05-15 Sheriff Bot <webkit.review.bot@gmail.com> + + Unreviewed, rolling out r59544. + http://trac.webkit.org/changeset/59544 + https://bugs.webkit.org/show_bug.cgi?id=39165 + + Cased LayoutTest to start crashing (Requested by abarth on + #webkit). + + * Scripts/old-run-webkit-tests: + +2010-05-15 Mario Sanchez Prada <msanchez@igalia.com> + + Reviewed by Xan Lopez. + + Ensure DRT loads GAIL (Gtk+ module), for a11y tests + https://bugs.webkit.org/show_bug.cgi?id=38648 + + Add the GTK_MODULES envvar (set to "gail") to the clean + environment when running DRT for the Gtk+ port + + * Scripts/old-run-webkit-tests: + +2010-05-15 Eric Seidel <eric@webkit.org> + + Reviewed by Maciej Stachowiak. + + Add script to check for minimum python version and install if missing on Tiger + https://bugs.webkit.org/show_bug.cgi?id=38886 + + Per Maciej's request on webkit-dev: + https://lists.webkit.org/pipermail/webkit-dev/2010-May/012785.html + provide a script which can automatically install Python on Tiger where + the system provided version is too old to be of use. + + Note this uses the official Mac Python installer from python.org. + This installs a copy of Python in /Library/Frameworks/Python.framework. + It also makes symlinks from /usr/local/bin to the Python.framework/bin. + + I have tested this script on Leopard and it worked fine. I have not + tested it on Tiger as I do not have access to a Tiger machine. In + either case this should provide a great starting point for someone + wishing to upgrade their copy of Python on Tiger. + + Future patches can make our scripts depend on a success return from this + script and then they can either skip tests/sections for which python is + insufficient, or they can fail themselves. + + * Scripts/ensure-valid-python: Added. + +2010-05-15 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + Display queue position inside EWS bubbles + https://bugs.webkit.org/show_bug.cgi?id=38979 + + This ended up being a lot of clean-up to our status server code. + + Added a new WorkItems model, a form with which to fill it, + and taught the Attachment class how to calculate the current queue + postion for an attachment using the data in WorkItems. + + I also finally made statusbubble.* and dashboard.* not use copy-paste code. + + The Attachment class has this summary() method which is very + controller/view-like and does not belong in a model class. + This patch got rid of all direct uses of summary(). + + * QueueStatusServer/handlers/dashboard.py: + - Build row objects to hand off to the view instead of handing off a + summary object and expecting the view to process it directly. + * QueueStatusServer/handlers/statusbubble.py: + - Build bubble object and hand them off to the view. + * QueueStatusServer/handlers/updatestatus.py: + - Code cleanup. Just move some code into _queue_status_from_request + to make the main put() handler easier to read. + * QueueStatusServer/handlers/updateworkitems.py: Added. + - Controller to handle storing WorkItems model objects. + * QueueStatusServer/main.py: + - Add route for /update-work-items + * QueueStatusServer/model/attachment.py: + - Add new methods to replace direct summary() access. + - Teach attachment how to calculate queue positions from WorkItems data. + * QueueStatusServer/model/queues.py: + - Move name_with_underscores here for easier re-use. + * QueueStatusServer/model/workitems.py: Added. + - New model for storing what items are currently queue for any bot. + * QueueStatusServer/templates/dashboard.html: + - Kill the copy/paste code! + * QueueStatusServer/templates/statusbubble.html: + - Kill the copy/paste code! + * QueueStatusServer/templates/updateworkitems.html: Added. + - Simple form for updating a queue's current work items. + +2010-05-15 Joanmarie Diggs <joanmarie.diggs@gmail.com> + + Reviewed by Xan Lopez. + + https://bugs.webkit.org/show_bug.cgi?id=30500 + [Gtk] Find a way for WebKit to "announce" itself so that ATs can readily distinguish it from true Gtk/Gail + + The "announcement" is now made in the form of an object attribute + associated with the AtkObject. + + * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp: + (AccessibilityUIElement::allAttributes): + (attributeSetToString): + +2010-05-15 Mario Sanchez Prada <msanchez@igalia.com> + + Reviewed by Xan Lopez. + + Accessibility: Implement isSelected in DRT for GTK + https://bugs.webkit.org/show_bug.cgi?id=31018 + + Implement AccessibilityUIElement::isSelected() for Gtk + + * DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp: + (AccessibilityUIElement::isSelected): + +2010-05-14 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + scm.py should use self.run instead of run_command + https://bugs.webkit.org/show_bug.cgi?id=38957 + + We've wanted to do this for a while, but it's a prerequiste for running + SVN from the cwd instead of the checkout_root. + + * Scripts/webkitpy/common/checkout/scm.py: + +2010-05-14 Anton Muhin <antonm@chromium.org> + + Reviewed by Darin Fisher. + + [Chromium] Consider implementing addOriginAccessWhitelistEntry method + https://bugs.webkit.org/show_bug.cgi?id=37578 + + Remove deprecated methods. + + * DumpRenderTree/chromium/LayoutTestController.cpp + * DumpRenderTree/chromium/LayoutTestController.h + +2010-05-14 Kenneth Russell <kbr@google.com> + + Reviewed by Darin Adler. + + Rename WebGLArray types to TypedArray types + https://bugs.webkit.org/show_bug.cgi?id=39091 + + Extended functionality of do-webcore-rename script and used it to + rename the WebGLArray types to the TypedArray naming convention. + The only source files which were touched by hand, and which are + being manually reviewed, are: + WebCore/page/DOMWindow.idl + WebCore/bindings/generic/RuntimeEnabledFeatures.h (script's changes undone) + WebKit/WebCore/bindings/js/JSDOMWindowCustom.cpp + WebKit/WebCore/bindings/v8/custom/V8DOMWindowCustom.cpp + These only needed to be touched to update the aliases between the + WebGLArray and TypedArray names introduced in bug 39036. (It was + not feasible to have do-webcore-rename handle this as it would + introduce circular renamings.) These aliases will be removed in + roughly a month once existing WebGL content has been updated. + + No new tests; covered under existing WebGL tests. Updated + constructed-objects-prototypes and prototype-inheritance-2 tests. + Ran all layout tests in Safari and all WebGL tests in Chromium. + + * Scripts/do-webcore-rename: + Handle the case where some renames are substrings of others. + Support renaming files containing custom JS bindings. If + isDOMTypeRename is non-zero, expand the regexp which rewrites + the file's contents in order to support custom JS bindings. + +2010-05-14 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + webkit-patch rollout throws exception if bug is already open + https://bugs.webkit.org/show_bug.cgi?id=38803 + + This was caused by someone incorrectly wrapping the code. :p + I'm going to have to start demanding unit tests for wrapping changes... + + I also fixed the code to be able to reopen bugs which were never confirmed. + Before it regressed, the code would just log in that case. Now it actually + will reopen the bug, but there is a FIXME about how the logic is a bit backwards. + + * Scripts/webkitpy/common/net/bugzilla.py: + * Scripts/webkitpy/common/net/bugzilla_unittest.py: + - Yay testing! + +2010-05-14 Simon Hausmann <simon.hausmann@nokia.com> + + Rubber-stamped by Antti Koivisto. + + [Qt] Rename QtLauncher to QtTestBrowser + + * QtTestBrowser: Copied from WebKitTools/QtLauncher. + * QtTestBrowser/QtLauncher.pro: Removed. + * QtTestBrowser/QtLauncher.qrc: Removed. + * QtTestBrowser/QtTestBrowser.pro: Copied from WebKitTools/QtLauncher/QtLauncher.pro. + * QtTestBrowser/QtTestBrowser.qrc: Copied from WebKitTools/QtLauncher/QtLauncher.qrc. + * Scripts/run-launcher: + * Scripts/webkitdirs.pm: + +2010-05-14 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Stop CCing webkit-bot-watchers + https://bugs.webkit.org/show_bug.cgi?id=39020 + + webkit-bot-watchers is somewhat of a failed experiment. No one + subscribed to the list (not even me). Removing it from the code + because wms says it bounces email sometimes. + + * Scripts/webkitpy/tool/commands/queues.py: + * Scripts/webkitpy/tool/commands/sheriffbot_unittest.py: + +2010-05-14 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + REGRESSION: webkit-patch commit-message throws exception + https://bugs.webkit.org/show_bug.cgi?id=38997 + + * Scripts/webkitpy/tool/commands/upload.py: + - Fix to respect and pass the --squash and --git-commit options. + * Scripts/webkitpy/tool/commands/upload_unittest.py: + - Fix the unit test to use the central MockCheckout instead of + its own custom Mock which didn't require enough parameters. + +2010-05-13 Diego Gonzalez <diegohcg@webkit.org> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Improve QtLauncher user agent dialog resize + https://bugs.webkit.org/show_bug.cgi?id=39062 + + * QtLauncher/main.cpp: + (LauncherWindow::showUserAgentDialog): + +2010-05-13 Eric Seidel <eric@webkit.org> + + Reviewed by Ojan Vafai. + + Disable compositing/iframes tests on the commit-queue as a workaround for bug 38912 + https://bugs.webkit.org/show_bug.cgi?id=39067 + + Fix yet another typo in my original hack. + I also added another unit test for this fix. + + * Scripts/webkitpy/common/config/ports.py: + * Scripts/webkitpy/common/config/ports_unittest.py: + +2010-05-13 Eric Seidel <eric@webkit.org> + + Reviewed by Ojan Vafai. + + Disable compositing/iframes tests on the commit-queue as a workaround for bug 38912 + https://bugs.webkit.org/show_bug.cgi?id=39067 + + My previous (unreviewed) hack didn't actually work due to checking "mac" instead of "Mac". + This change fixes my hack, and unit tests it. + + * Scripts/webkitpy/tool/mocktool.py: + - Make it possible to make run_command log too. + * Scripts/webkitpy/tool/steps/runtests.py: + - Fix my hack to actually work. + * Scripts/webkitpy/tool/steps/steps_unittest.py: + - Test my hack (and basic RunTests behavior as well). + +2010-05-13 Antonio Gomes <tonikitoo@webkit.org>, Yi Shen <yi.4.shen@nokia.com> + + Reviewed by Kenneth Christiansen. + + [Qt] Add LayoutTestController interface: computedStyleIncludingVisitedInfo + https://bugs.webkit.org/show_bug.cgi?id=37759 + + WebKitTools: + + * DumpRenderTree/qt/LayoutTestControllerQt.cpp: + (LayoutTestController::computedStyleIncludingVisitedInfo): + * DumpRenderTree/qt/LayoutTestControllerQt.h: + +2010-05-10 Adam Roben <aroben@apple.com> + + Convert status bar text to UTF-8 before logging it on Windows + + We were previously logging the text using printf("%S", bstr). This + converts the UTF-16 BSTR to a multibyte string using wctomb, which + uses the codepage for the current locale to perform the conversion. + The conversion was failing, causing printf to bail and truncate the + string. By converting to UTF-8 manually before logging, we avoid this + issue (and also end up with UTF-8 output, which is what the expected + results contain). We may have to do this in other places in DRT, + eventually. + + Fixes <http://webkit.org/b/38849> REGRESSION (r59016): + plugins/set-status.html fails on Windows + + Reviewed by Alexey Proskuryakov. + + * DumpRenderTree/win/DumpRenderTree.cpp: + (toUTF8): Moved this here from FrameLoadDelegate. Renamed from + BSTRtoString. + + * DumpRenderTree/win/DumpRenderTreeWin.h: Added declaration of toUTF8. + + * DumpRenderTree/win/FrameLoadDelegate.cpp: Removed BSTRtoString. + (descriptionSuitableForTestResult): Updated for rename. + + * DumpRenderTree/win/UIDelegate.cpp: + (UIDelegate::setStatusText): Convert the status bar text to UTF-8 + before logging it so that Windows won't try (and fail) to convert it + to the current locale's codepage. + +2010-05-13 Eric Seidel <eric@webkit.org> + + Unreviewed hack, attempting to get the commit-queue running again. + + Disable compositing/iframes tests on the commit-queue as a workaround for bug 38912 + https://bugs.webkit.org/show_bug.cgi?id=39067 + + I had this hack locally on the commit-queue, but it's fragile + and broke this morning. It will work much better if committed + to the repository. In either case it's temporary while we + work up a real fix for bug 38912. + + * Scripts/webkitpy/common/config/ports.py: + - Expose an is_leopard() method. + * Scripts/webkitpy/tool/steps/runtests.py: + - Don't run compositing/iframes tests on the commit-queue under leopard. + +2010-05-13 Fumitoshi Ukai <ukai@chromium.org> + + Unreviewed fix. Revert previous change. + + Even if with-statments are changed in websocket_server.py, it also claims syntax error for with statement in http_server.py. + Until python 2.5 is installed on tiger bot, skips websocket/tests on tiger. + + * Scripts/webkitpy/layout_tests/port/websocket_server.py: use with statement + +2010-05-13 Fumitoshi Ukai <ukai@chromium.org> + + Unreviewed fix. + + On tiger bot, it claims syntax error for with statement. + + * Scripts/webkitpy/layout_tests/port/websocket_server.py: rewrite with statement with try-finally. + +2010-05-13 Fumitoshi Ukai <ukai@chromium.org> + + Reviewed by David Levin. + + WebSocket: pywebsocket 0.5 + https://bugs.webkit.org/show_bug.cgi?id=38034 + + Remove pywebsocket from webkitpy/thirdparty. + Make pywebsocket autoinstalled. + + * Scripts/new-run-webkit-websocketserver: + Add --output-dir option. + * Scripts/old-run-webkit-tests: + Use new-run-webkit-websocketserver, rather than directly run pywebsocket's standalone.py + * Scripts/run-webkit-websocketserver: + Ditto. + * Scripts/webkitpy/layout_tests/port/websocket_server.py: + Use autoinstalled pywebsocket. + * Scripts/webkitpy/thirdparty/__init__.py: + Autoinstall pywebsocket + * Scripts/webkitpy/thirdparty/pywebsocket: Removed. + +2010-05-12 Chris Jerdonek <cjerdonek@webkit.org> + + Reviewed by Daniel Bates. + + Fixed a bug in svn-apply whereby the reviewer would not get set if + the portion of the patch for the ChangeLog contains "NOBODY (**PS!)" + in the leading junk. + + https://bugs.webkit.org/show_bug.cgi?id=38998 + + * Scripts/VCSUtils.pm: + - Added the $changeLogTimeZone variable from svn-apply. + - Added setChangeLogDateAndReviewer() from svn-apply. + - Added a localTimeInProjectTimeZone() subroutine. + - In setChangeLogDateAndReviewer(): + - Added an $epochTime parameter to make the subroutine more testable. + - Made the "NOBODY (**PS!)" regular expression more specific so that + it will not apply to text in the leading junk. + - Updated to call localTimeInProjectTimeZone(). + * Scripts/svn-apply: + - Removed the $changeLogTimeZone file variable. + - Added an $epochTime file variable to represent the current time. + - Removed the setChangeLogDateAndReviewer() subroutine. + - Updated patch() to use the new setChangeLogDateAndReviewer() syntax. + * Scripts/webkitperl/VCSUtils_unittest/setChangeLogDateAndReviewer.pl: Added. + - Added unit tests. + +2010-05-12 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + test-webkitpy fails on windows due to lack of readline module + https://bugs.webkit.org/show_bug.cgi?id=38884 + + Win32 Python does not have a readline module, so we should + not exit(1) if the import fails. + + Also the failure message is mac-specific and doesn't need to be. + Only print the mac-specific install instructions on mac. + + * Scripts/webkitpy/common/system/user.py: + +2010-05-12 Kent Tamura <tkent@chromium.org> + + Reviewed by Nate Chapin. + + [DRT/Chromium] Add a missing Sans-serif font setting + https://bugs.webkit.org/show_bug.cgi?id=38981 + + * DumpRenderTree/chromium/TestShell.cpp: + (TestShell::resetWebSettings): + Set "Helvetica" for Sans-serif. It is the same as the default setting of test_shell. + +2010-05-10 Ojan Vafai <ojan@chromium.org> + + Reviewed by Adam Barth. + + webkit-patch upload --fancy-review now uses the bugzilla bug ID as the rietveld ID + https://bugs.webkit.org/show_bug.cgi?id=38866 + + wkrietveld.appspot.com has already been updated to allow you to + pass --issue for the issue creation as well as subsequent uploads. + + Also, remove the extra code for trying to read out the issue ID + from the changelog description since we just use the bugzilla ID now. + + * Scripts/webkitpy/common/config/__init__.py: + * Scripts/webkitpy/common/net/rietveld.py: + * Scripts/webkitpy/tool/steps/postcodereview.py: + * Scripts/webkitpy/tool/steps/postdiff.py: + +2010-05-12 Ojan Vafai <ojan@chromium.org> + + No review needed. + + Marking myself as a reviewer. + + * Scripts/webkitpy/common/config/committers.py: + +2010-05-12 Dirk Pranke <dpranke@chromium.org> + + Reviewed by Eric Seidel. + + new-run-webkit-tests: add a --print default option so that you can + easily get the default output plus something (e.g., you can say + '--print default,config' instead of '--print misc,one-line-progress, + one-line-summary,unexpected,unexpected-results,updates,config'. + + Also, add more unit tests for --verbose, --print everything, etc. + + https://bugs.webkit.org/show_bug.cgi?id=38877 + + * Scripts/webkitpy/layout_tests/layout_package/printing.py: + * Scripts/webkitpy/layout_tests/layout_package/printing_unittest.py: + +2010-05-12 Csaba Osztrogonác <ossy@webkit.org> + + Reviewed by Adam Barth. + + Qt build failures cause SheriffBot false positives + https://bugs.webkit.org/show_bug.cgi?id=38969 + + Add Qt bots back to the core builders, because bug fixed by r59261. + + * Scripts/webkitpy/common/net/buildbot.py: + * Scripts/webkitpy/common/net/buildbot_unittest.py: + +2010-05-12 Csaba Osztrogonác <ossy@webkit.org> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Slave losts cause build break on bots + https://bugs.webkit.org/show_bug.cgi?id=38980 + + * Scripts/build-webkit: Remove 0 byte sized files from productDir before build. + +2010-05-12 Csaba Osztrogonác <ossy@webkit.org> + + Unreviewed trivial fix after r59254. + + * Scripts/old-run-webkit-tests: + +2010-05-12 Csaba Osztrogonác <ossy@webkit.org> + + Reviewed by Eric Seidel. + + run-webkit-tests --exit-after-N-failures should not count new tests as failures + https://bugs.webkit.org/show_bug.cgi?id=31829 + + * Scripts/old-run-webkit-tests: + +2010-05-12 James Robinson <jamesr@chromium.org> + + Reviewed by Simon Fraser. + + Disable smooth scrolling on OS X when running tests + https://bugs.webkit.org/show_bug.cgi?id=38964 + + Some tests (like fast/repaint/fixed-move-after-keyboard-scroll.html) + depend on smooth scrolling behavior. Since this is off by default + in Leopard and on by default in Snow Leopard, DRT should turn it + off always to ensure a consistent test environment. + + * DumpRenderTree/mac/DumpRenderTree.mm: + (resetDefaultsToConsistentValues): + +2010-05-10 Rodrigo Belem <rodrigo.belem@openbossa.org> + + Reviewed by Kenneth Christiansen , Simon Hausmann and Gustavo Noronha. + + [Qt, Gtk] Allows build-webkit script to receive an install prefix as parameter + https://bugs.webkit.org/show_bug.cgi?id=26224 + + Added more parameters to build-webkit script, the --prefix for gkt + and --install-libs, --install-headers for qt. Now it is possible + to change the install prefix for gtk and install path for qt. + + * Scripts/build-webkit: + * Scripts/webkitdirs.pm: + +2010-05-12 Philippe Normand <pnormand@igalia.com> + + Unreviewed, added my IRC nickname. + + * Scripts/webkitpy/common/config/committers.py: + +2010-05-11 Chris Jerdonek <cjerdonek@webkit.org> + + Reviewed by Eric Seidel. + + Fixed two FIXME's in svn-apply: eliminated the unnecessary %copiedFiles + variable and changed gitKnowsOfFile() to use exitStatus(). + + https://bugs.webkit.org/show_bug.cgi?id=38862 + + * Scripts/svn-apply: + +2010-05-11 Eric Seidel <eric@webkit.org> + + Unreviewed, just fixing python typo. + + EWS bots should poll more often than every 5 minutes + https://bugs.webkit.org/show_bug.cgi?id=38968 + + Typo in _now(), add a unittest to prove I fixed it. + + * Scripts/webkitpy/tool/bot/queueengine.py: + * Scripts/webkitpy/tool/bot/queueengine_unittest.py: + +2010-05-11 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + Qt build failures cause SheriffBot false positives + https://bugs.webkit.org/show_bug.cgi?id=38969 + + The Qt buildbot randomly fails to compile occasionally because its + network connection causes SVn to leave zero-byte files around. These + compile failures confuse SheriffBot into thinking someone's patch + caused a build break. + + In this patch, I've temporarily removed Qt from the list of core + builders. Ossy is working on a script to clean up the zero byte files. + Once that goes in, we can add Qt back to the core builders. + + * Scripts/webkitpy/common/net/buildbot.py: + * Scripts/webkitpy/common/net/buildbot_unittest.py: + +2010-05-11 Adam Barth <abarth@webkit.org> + + Reviewed by Eric Seidel. + + sheriffbot is spammy. + https://bugs.webkit.org/show_bug.cgi?id=38936 + + Reduce sheriffbot spam by not warning about new blameworthy revisions + that can be explained by previously blamed revisions. This might cause + us to not warn about some real failures, but we're getting too much + spam from slow builders that have large blamelists (and we've already + poked the responsible folks using data from a fast builder). + + * Scripts/webkitpy/tool/bot/sheriff_unittest.py: + - Removed unneeded import + * Scripts/webkitpy/tool/commands/sheriffbot.py: + - The logic change + * Scripts/webkitpy/tool/commands/sheriffbot_unittest.py: + - Tests of the change + * Scripts/webkitpy/tool/mocktool.py: + - Merged two declarations of MockBuilder + +2010-05-11 Tony Chang <tony@chromium.org> + + Reviewed by David Levin. + + [chromium] detect num processors to pass to make -j on Linux + https://bugs.webkit.org/show_bug.cgi?id=38833 + + * Scripts/webkitdirs.pm: + +2010-05-11 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + EWS bots should poll more often than every 5 minutes + https://bugs.webkit.org/show_bug.cgi?id=38968 + + We'll make them poll every 2 minutes to start with. + I'm going to re-write how polling works soon, so this is + a stop-gap to try and make the bots more responsive. + + If Bill notices any additional load on bugzilla we'll drop + the polling frequency back to 5 minutes. He's historically said + that the EWS bots appear to be a drop in the bucket and thus + should be able to poll much more frequently w/o causing trouble. + + * Scripts/webkitpy/tool/bot/queueengine.py: + * Scripts/webkitpy/tool/bot/queueengine_unittest.py: + - Add a test for sleep_message + +2010-05-11 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + commit-queue should commit rollouts without running tests + https://bugs.webkit.org/show_bug.cgi?id=38940 + + Most of this change is improving our test coverage for the commit-queue. + The only functional change is removing the --test flag when the commit-queue + is running in rollouts mode. + + I added test coverage for status updates, and updated the commit-queue status + messages to distinguish rollout vs. normal landing mode in its empty queue + and land patch messages. + + * Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py: + - Got rid of a bunch of copy/paste code using _default_expected_stderr + * Scripts/webkitpy/tool/commands/queues.py: + - Moved rollout patch filtering out of _validate_patches_in_commit_queue + so that we only have to check if the builders are green in one place. + - Make the "empty queue" message note which queue it is referring to. + - Don't pass --text to land-attachment when in rollout mode. + * Scripts/webkitpy/tool/commands/queues_unittest.py: + - Update results now that we're testing status updates. + - Test _can_build_and_test since I made a typo in that call while + writing this change and the unit tests failed to catch it! + * Scripts/webkitpy/tool/mocktool.py: + - Log status updates to make them testable in our unit tests. + +2010-05-11 Kent Tamura <tkent@chromium.org> + + Reviewed by Dimitri Glazkov. + + [DRT/Chromium] Apply recent changes of test_shell to DRT + https://bugs.webkit.org/show_bug.cgi?id=38895 + + Port the changes to test_shell during (r40492, r46810] of Chromium. + Highlights: + - Introduce NotificationPresenter + - Fix parameter mismatch of WebViewClient::startDragging() + + This change fixes dozens of unexpected behaviors. + + * DumpRenderTree/DumpRenderTree.gypi: + Add NotificationPresenter.{cpp,h} + * DumpRenderTree/chromium/EventSender.cpp: + (applyKeyModifier): + (EventSender::EventSender): + (EventSender::reset): + (EventSender::webview): + (EventSender::doDragDrop): + (EventSender::keyDown): + (EventSender::addTouchPoint): + (EventSender::clearTouchPoints): + (EventSender::releaseTouchPoint): + (EventSender::setTouchModifier): + (EventSender::updateTouchPoint): + (EventSender::cancelTouchPoint): + (EventSender::sendCurrentTouchEvent): + (EventSender::touchEnd): + (EventSender::touchMove): + (EventSender::touchStart): + (EventSender::touchCancel): + * DumpRenderTree/chromium/EventSender.h: + * DumpRenderTree/chromium/LayoutTestController.cpp: + (LayoutTestController::LayoutTestController): + (LayoutTestController::reset): + (LayoutTestController::showWebInspector): + (LayoutTestController::setAuthorAndUserStylesEnabled): + (LayoutTestController::setScrollbarPolicy): + (LayoutTestController::setWillSendRequestClearHeader): + (LayoutTestController::callShouldCloseOnWebView): + (LayoutTestController::grantDesktopNotificationPermission): + (LayoutTestController::removeOriginAccessWhitelistEntry): + (LayoutTestController::addUserScript): + (LayoutTestController::addUserStyleSheet): + * DumpRenderTree/chromium/LayoutTestController.h: + * DumpRenderTree/chromium/NotificationPresenter.cpp: Added. + * DumpRenderTree/chromium/NotificationPresenter.h: Added. + * DumpRenderTree/chromium/TestShell.cpp: + (TestShell::TestShell): + (TestShell::runFileTest): + (TestShell::resetTestController): + * DumpRenderTree/chromium/TestShell.h: + (TestShell::eventSender): + (TestShell::notificationPresenter): + (TestShell::showDevTools): + * DumpRenderTree/chromium/WebViewHost.cpp: + (WebViewHost::createView): + (WebViewHost::createPopupMenu): + (WebViewHost::startDragging): + (WebViewHost::notificationPresenter): + (WebViewHost::createApplicationCacheHost): + (WebViewHost::willSendRequest): + (WebViewHost::updateAddressBar): + (WebViewHost::updateURL): + * DumpRenderTree/chromium/WebViewHost.h: + (WebViewHost::addClearHeader): + (WebViewHost::clearHeaders): + +2010-05-10 Dirk Pranke <dpranke@chromium.org> + + Reviewed by Eric Seidel. + + new-run-webkit-tests: fix a path-handling bug that was breaking the + dryrun ports on windows, and add a comment about why we don't run + the chromium dryrun tests by default on every port. + + https://bugs.webkit.org/show_bug.cgi?id=38796 + + * Scripts/webkitpy/layout_tests/port/dryrun.py: + * Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py: + +2010-05-11 Mark Rowe <mrowe@apple.com> + + Reviewed by Darin Adler. + + <http://webkit.org/b/38941> build-webkit shouldn't always override ENABLE_FOO Xcode configuration settings + + * Scripts/build-webkit: Don't pass the ENABLE setting to xcodebuild if the value matches the default. + This will lead to xcodebuild picking up the settings from FeatureDefines.xcconfig, and will aid in + revealing problems that are due to inconsistent values for settings across projects. + +2010-05-11 Mark Rowe <mrowe@apple.com> + + Build fix. + + * DumpRenderTree/mac/LayoutTestControllerMac.mm: + +2010-05-10 Ojan Vafai <ojan@chromium.org> + + Reviewed by David Levin. + + CheckStyle was eating script errors when there were local-commits and working copy changes + https://bugs.webkit.org/show_bug.cgi?id=38880 + + * Scripts/webkitpy/tool/steps/checkstyle.py: + * Scripts/webkitpy/tool/steps/checkstyle_unittest.py: Added. + +2010-05-11 Jian Li <jianli@chromium.org> + + Reviewed by Dmitry Titov. + + Expose FileReader interface. + https://bugs.webkit.org/show_bug.cgi?id=38609 + + * Scripts/build-webkit: turn on building FileReader for Apple's WebKit. + +2010-05-10 Tony Chang <tony@chromium.org> + + Reviewed by Kent Tamura. + + [chromium] Build DRT when running build-webkit --chromium + https://bugs.webkit.org/show_bug.cgi?id=38730 + + * DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp: Removed. + * Scripts/build-dumprendertree: This does nothing now. + +2010-05-10 Jon Honeycutt <jhoneycutt@apple.com> + + REGRESSION(r59100): Added test is broken on many platforms. + https://bugs.webkit.org/show_bug.cgi?id=38881 + + Reviewed by Eric Seidel. + + * DumpRenderTree/win/TestNetscapePlugin/main.cpp: + (NPP_Destroy): + Use pluginLog, rather than printf, to match other platforms. + +2010-05-10 Dirk Pranke <dpranke@chromium.org> + + Unreviewed, build fix. + + Fix a build failure caused by assuming the default platform in + a unit test for new-run-webkit-tests instead of specifying + --platform test. + + * Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py: + +2010-05-10 Dirk Pranke <dpranke@chromium.org> + + Unreviewed, build-fix + + new-run-webkit-tests: fix test failure caused by me failing to update + the expected output. Also, run '--platform test' instead of + 'platform dryrun' since the former is guaranteed to work and the + latter isn't. + + * Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py: + +2010-05-10 Dirk Pranke <dpranke@chromium.org> + + Reviewed by David Levin. + + new-run-webkit-tests: looks like the unicode conversion broke + --print-last-failures and --retest-last-failures. Fixing. + + * Scripts/webkitpy/layout_tests/run_webkit_tests.py: + * Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py: + +2010-05-10 Dirk Pranke <dpranke@chromium.org> + + Reviewed by David Levin. + + Re-attempt to fix 38616 - newline handling in new-run-webkit-tests. + I didn't handle some cases correctly before and the solution was + confusing. The new patch assumes all calls to the printing module + don't have newlines, and will append newlines where necessary, just + like logging does. + + https://bugs.webkit.org/show_bug.cgi?id=38790 + + * Scripts/webkitpy/layout_tests/layout_package/metered_stream.py: + * Scripts/webkitpy/layout_tests/layout_package/metered_stream_unittest.py: + * Scripts/webkitpy/layout_tests/layout_package/printing.py: + * Scripts/webkitpy/layout_tests/layout_package/printing_unittest.py: + * Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py: + +2010-05-10 Kent Tamura <tkent@chromium.org> + + Reviewed by Adam Barth. + + Make tkent a reviewer + https://bugs.webkit.org/show_bug.cgi?id=38875 + + * Scripts/webkitpy/common/config/committers.py: + +2010-05-07 Jon Honeycutt <jhoneycutt@apple.com> + + Crash closing window containing Flash plug-in + https://bugs.webkit.org/show_bug.cgi?id=38797 + <rdar://problem/7935266> + + Reviewed by Eric Seidel. + + * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp: + (pluginAllocate): + Initialize new member to false. + + * DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h: + Added a new member. + + * DumpRenderTree/win/TestNetscapePlugin/main.cpp: + (NPP_New): + Check whether the element has the "testGetURLOnDestroy" attribute, and + record that. + (NPP_Destroy): + If "testGetURLOnDestroy" is set, perform a load while destroying the + plug-in. + +2010-05-10 Eric Seidel <eric@webkit.org> + + Reviewed by David Levin. + + Executive.kill_* do not work with windows python + https://bugs.webkit.org/show_bug.cgi?id=38872 + + http://trac.webkit.org/changeset/57444 is where the original + breakage occurred. + http://trac.webkit.org/changeset/58314 is where the regression + started affecting chromium. + + I have since learned that sys.platform has no "windows" value. + "win32" is always the value, under 32 or 64 bit windows + + The tests for this code are not run anywhere because + test-webkitpy does not yet work on "win32". Mostly due to + depending on unixisms like "cat" and "yes". + + * Scripts/webkitpy/common/system/executive.py: + * Scripts/webkitpy/common/system/executive_unittest.py: + +2010-05-10 Dirk Pranke <dpranke@chromium.org> + + Reviewed by Eric Seidel. + + Fix a bug in rebaseline-chromium-webkit-tests where we would crash + instead of logging an error and exiting if it couldn't find either a + debug or a release build of the image diff binary. + + https://bugs.webkit.org/show_bug.cgi?id=38692 + + * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py: + * Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests_unittest.py: Added. + +2010-05-10 Eric Seidel <eric@webkit.org> + + Reviewed by Adam Barth. + + new-run-webkit-tests fails when run under sys.platform == "windows" due to undefined signal.SIGKILL + https://bugs.webkit.org/show_bug.cgi?id=38861 + + * Scripts/webkitpy/common/system/executive.py: + * Scripts/webkitpy/common/system/executive_unittest.py: + +2010-05-10 Jer Noble <jer.noble@apple.com> + + Unreviewed. + + Adding myself as a committer. + + * Scripts/webkitpy/common/config/committers.py: + +2010-05-10 Hans Wennborg <hans@chromium.org> + + Reviewed by Jeremy Orlow. + + [Chromium] Add quota parameter to WebViewClient::createSessionStorageNamespace() + https://bugs.webkit.org/show_bug.cgi?id=38750 + + Put a per-origin quota on session storage since it is using memory in + the browser process, and should not be allowed to grow arbitrarily + large. See also http://trac.webkit.org/changeset/58828. + + * DumpRenderTree/chromium/WebViewHost.cpp: + (WebViewHost::createSessionStorageNamespace): + * DumpRenderTree/chromium/WebViewHost.h: + +2010-05-10 Csaba Osztrogonác <ossy@webkit.org> + + Rubber-stamped by Simon Hausmann. + + [Qt] Roll-out r59020 and r59021, because the Qt part of these changes + haven't been landed in Qt trunk yet. Should be rolled-in again after the merge. + + https://bugs.webkit.org/show_bug.cgi?id=32967 + + * DumpRenderTree/qt/DumpRenderTreeQt.cpp: + (WebCore::WebPage::WebPage): + (WebCore::DumpRenderTree::DumpRenderTree): + * DumpRenderTree/qt/DumpRenderTreeQt.h: + +2010-05-09 Daniel Bates <dbates@rim.com> + + Reviewed by Chris Jerdonek. + + https://bugs.webkit.org/show_bug.cgi?id=38812 + + Make the regular expressions for parsing the start of an SVN + and Git header global variables since they are used throughout + VCSUtils.pm. + + * Scripts/VCSUtils.pm: + +2010-05-09 Chris Jerdonek <cjerdonek@webkit.org> + + Reviewed by Daniel Bates. + + Finished moving the header-parsing logic from svn-apply and -unapply + to VCSUtils.pm's parsing methods. + + https://bugs.webkit.org/show_bug.cgi?id=38802 + + * Scripts/VCSUtils.pm: + - Added to parseGitDiffHeader() the ability to parse and store + whether a file is new or deleted. + - Also reordered in parseGitDiffHeader() some of the else statements + to a more readable ordering. + - Added to parseSvnDiffHeader() the ability to parse and store + whether a file is new. + * Scripts/svn-apply: + - Changed handleGitBinaryChange() to use the new "isNew" and "isDeletion" + diffHash key-values. + - Changed patch() to use the new "isNew" diffHash key-value. + * Scripts/svn-unapply: + - Changed patch() to use the new "isNew" and "isDeletion" diffHash key-values. + * Scripts/webkitperl/VCSUtils_unittest/parseDiff.pl: + - Added unit tests for new and deleted files. + * Scripts/webkitperl/VCSUtils_unittest/parseGitDiffHeader.pl: + - Updated the unit tests as necessary. + - Added a unit test for a deleted file. + * Scripts/webkitperl/VCSUtils_unittest/parseSvnDiffHeader.pl: + - Updated the unit tests as necessary. + +2010-05-08 Chris Jerdonek <cjerdonek@webkit.org> + + Reviewed by Daniel Bates. + + Added to svn-apply support for git renames and copies with similarity + index less than 100%. + + https://bugs.webkit.org/show_bug.cgi?id=32834 + + * Scripts/VCSUtils.pm: + - Added to parseGitDiffHeader() support for renames and similarity + index less than 100%. + - Added to parseDiff() support for processing renames, renames with + changes, and copies with changes. + - Added to parsePatch() the ability to process multiple return + values from parseDiff(). + * Scripts/svn-apply: + - Added to patch() the ability to process diff hashes with the + isDeletion key-value set. + * Scripts/webkitperl/VCSUtils_unittest/parseDiff.pl: + - Updated the unit tests as necessary. + - Added unit tests for rename with similarity index 100%, + rename with similarity index < 100%, and rename with a change + to the executable bit. + * Scripts/webkitperl/VCSUtils_unittest/parseGitDiffHeader.pl: + - Added unit tests for rename with similarity index 100%, + rename with similarity index < 100%, and rename with a change + to the executable bit. + * Scripts/webkitperl/VCSUtils_unittest/parsePatch.pl: + - Updated the unit tests as necessary. + * Scripts/webkitperl/VCSUtils_unittest/parseSvnDiffHeader.pl: + - Simplified the carriage-return unit test to more narrowly test + only carriage returns. + +2010-05-08 Chris Jerdonek <cjerdonek@webkit.org> + + Reviewed by Daniel Bates. + + This revision suppresses the misleading "error: pathspec..." messages + when using svn-apply to add a binary file from a Git diff. + + https://bugs.webkit.org/show_bug.cgi?id=38047 + + When adding a new binary file from a Git diff, svn-apply prints + a misleading error of the form -- "error: pathspec '<filename>' did + not match any file(s) known to git. Did you forget to 'git add'?" + This patch suppresses these messages since they are normal. + + * Scripts/VCSUtils.pm: + Added the callSilently() subroutine from runPatchCommand.pl, which + executes a Perl function while suppressing STDERR. + * Scripts/svn-apply: + Refactored the Git portion of scmKnowsOfFile() into a + gitKnowsOfFile(), and called this new subroutine using callSilently(). + * Scripts/webkitperl/VCSUtils_unittest/runPatchCommand.pl: + Removed callSilently() since it was moved to VCSUtils.pm in this patch. + +2010-05-08 Victor Wang <victorw@chromium.org> + + Reviewed by Adam Barth. + + [chromium]: Upload test results json files to app engine server + Add an option to run_webkit_tests.py to upload generated + JSON files to app engine server. These JSON files will be used + by chromium layout test falkiness dashboard. + + https://bugs.webkit.org/show_bug.cgi?id=36063 + + * Scripts/webkitpy/layout_tests/layout_package/test_results_uploader.py: Added. + * Scripts/webkitpy/layout_tests/run_webkit_tests.py: + +2010-05-08 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Build the ImageDiff tool for all platforms including Windows and Symbian + https://bugs.webkit.org/show_bug.cgi?id=38706 + + Use qmath.h instead of math.h to make it portable. + + * DumpRenderTree/qt/ImageDiff.cpp: + (main): + +2010-05-08 Robert Hogan <robert@roberthogan.net> + + Reviewed by Simon Hausmann. + + [Qt] Fix http/tests/xmlhttprequest/cross-origin-no-authorization.html + and http/tests/xmlhttprequest/cross-origin-authorization.html + + QHttpNetworkRequest adds Authorization and Cookie headers to XHRs + without knowing if this is valid behaviour or not. In order to allow + Qt to decide whether Cookie/Authorization headers should be added + to an XHR QtWebKit needs to use an attribute added to QNetworkRequest. + These new attributes are: QNetworkRequest::CookieLoadControlAttribute, + QNetworkRequest::CookieSaveControlAttribute,and + QNetworkRequest::AuthenticationReuseControlAttribute. + + In order to properly support the tests, Qt's DRT needs to use one + NetworkAccessManager for all pages. This allows it to use cached + credentials where appropriate. + + The tests now pass when run individually but there seems to be a problem with + leaking the results of requests across tests when run with the others in + http/tests. This will be addressed in a separate patch. + + https://bugs.webkit.org/show_bug.cgi?id=32967 + + + * DumpRenderTree/qt/DumpRenderTreeQt.cpp: + (WebCore::WebPage::WebPage): + (WebCore::DumpRenderTree::DumpRenderTree): + * DumpRenderTree/qt/DumpRenderTreeQt.h: + + +2010-05-04 Robert Hogan <robert@webkit.org> + + Reviewed by Eric Seidel. + + [Qt] unskip http/tests/plugins/npapi-response-headers.html + + Turns out this failed because run-webkit-tests was eating the first occurrence + of 'Content-Type: text/plain' in the test output as a header. Strange but true. + So do as Chromium does and preface all text dumps with the + 'Content-Type: text/plain' header. + + https://bugs.webkit.org/show_bug.cgi?id=38541 + + * DumpRenderTree/qt/DumpRenderTreeQt.cpp: + (WebCore::DumpRenderTree::dump): + +2010-05-08 Eric Seidel <eric@webkit.org> + + Reviewed by Daniel Bates. + + test-webkitpy fails under cygwin + https://bugs.webkit.org/show_bug.cgi?id=38718 + + * Scripts/webkitpy/common/system/executive.py: + - Add _KILL_PROCESS_KILLED_PROCESS_EXIT_CODE and + _KILL_ALL_KILLED_PROCESS_EXIT_CODE to store the expected + exit codes of processes killed by kill_process and kill_all. + These two constants are only used by the unit tests but are + stored in executive.py so they can be right next to the platform ifs. + - Remove unnecessary str() conversion, run_command does that for us. + - Make os.kill retry on cygwin on EAGAIN. It's unclear why CYGWIN + throws EAGAIN, but it only does so sometimes. 3 may not be enough + retries, but we'll try it to start with. + - Add _windows_image_name to automatically convert "yes" to "yes.exe" + for use with taskkill.exe /im. Various callers to kill_all could + be updated to remove the .exe, but that can be done in another patch. + - Use taskkill.exe for killall on cygwin. + * Scripts/webkitpy/common/system/executive_unittest.py: + - Use the new *_KILLED_PROCESS_EXIT_CODE constants which are correctly + set to 0 on windows/cygwin systems where taskkill.exe is used. + - Test _windows_image_name + * Scripts/webkitpy/layout_tests/port/chromium_win.py: + - Add FIXME about including mac-snowleopard in baseline_search_path. + * Scripts/webkitpy/layout_tests/port/webkit.py: + - Make default_configuration actually read from the Configuration file. + * Scripts/webkitpy/layout_tests/port/webkit_unittest.py: Added. + - Test default_configuration + * Scripts/webkitpy/layout_tests/port/win.py: + - Need a basic baseline_search_path if --platform dryrun is to work. + +2010-05-08 Eric Seidel <eric@webkit.org> + + Unreviewed, just reverting commit. + + REGRESSION(59000): r59000 contained all sorts of changes it should not have, needs revert. + https://bugs.webkit.org/show_bug.cgi?id=38798 + + * Scripts/webkitpy/common/system/executive.py: + * Scripts/webkitpy/common/system/executive_unittest.py: + * Scripts/webkitpy/layout_tests/port/chromium_linux.py: + * Scripts/webkitpy/layout_tests/port/chromium_mac.py: + * Scripts/webkitpy/layout_tests/port/chromium_win.py: + * Scripts/webkitpy/layout_tests/port/mac.py: + * Scripts/webkitpy/layout_tests/port/webkit.py: + * Scripts/webkitpy/layout_tests/port/webkit_unittest.py: Removed. + * Scripts/webkitpy/layout_tests/port/win.py: + * Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py: + * Scripts/webkitpy/tool/commands/download.py: + * Scripts/webkitpy/tool/commands/download_unittest.py: + +2010-05-06 Eric Seidel <eric@webkit.org> + + Reviewed by Daniel Bates. + + test-webkitpy fails under cygwin + https://bugs.webkit.org/show_bug.cgi?id=38718 + + * Scripts/webkitpy/common/system/executive.py: + - Add _KILL_PROCESS_KILLED_PROCESS_EXIT_CODE and + _KILL_ALL_KILLED_PROCESS_EXIT_CODE to store the expected + exit codes of processes killed by kill_process and kill_all. + These two constants are only used by the unit tests but are + stored in executive.py so they can be right next to the platform ifs. + - Remove unnecessary str() conversion, run_command does that for us. + - Make os.kill retry on cygwin on EAGAIN. It's unclear why CYGWIN + throws EAGAIN, but it only does so sometimes. 3 may not be enough + retries, but we'll try it to start with. + - Add _windows_image_name to automatically convert "yes" to "yes.exe" + for use with taskkill.exe /im. Various callers to kill_all could + be updated to remove the .exe, but that can be done in another patch. + - Use taskkill.exe for killall on cygwin. + * Scripts/webkitpy/common/system/executive_unittest.py: + - Use the new *_KILLED_PROCESS_EXIT_CODE constants which are correctly + set to 0 on windows/cygwin systems where taskkill.exe is used. + - Test _windows_image_name + * Scripts/webkitpy/layout_tests/port/chromium_win.py: + - Add FIXME about including mac-snowleopard in baseline_search_path. + * Scripts/webkitpy/layout_tests/port/webkit.py: + - Make default_configuration actually read from the Configuration file. + * Scripts/webkitpy/layout_tests/port/webkit_unittest.py: Added. + - Test default_configuration + * Scripts/webkitpy/layout_tests/port/win.py: + - Need a basic baseline_search_path if --platform dryrun is to work. + + 2010-05-07 Eric Seidel <eric@webkit.org> + + Reviewed by Nikolas Zimmermann. + + DryrunTest fails on every platform other than mac + https://bugs.webkit.org/show_bug.cgi?id=38796 + + The test uses the port detection logic to find a suitable + port to use results from. However that detection logic assumes + chromium on linux, which requires a chromium checkout which the + bots don't have. The test is broken and we'll need to fix it. + For now I'm just going to disable the test on all platforms besides mac. + + * Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py: + + 2010-05-07 Eric Seidel <eric@webkit.org> + + Reviewed by Daniel Bates. + + Clean up baseline_search_path to use map to reduce copy/paste code + https://bugs.webkit.org/show_bug.cgi?id=38792 + + Reading which portnames a port falls back to is easier if + we convert port names to paths with map instead of using copy/paste code. + + * Scripts/webkitpy/layout_tests/port/chromium_linux.py: + * Scripts/webkitpy/layout_tests/port/chromium_mac.py: + * Scripts/webkitpy/layout_tests/port/chromium_win.py: + * Scripts/webkitpy/layout_tests/port/mac.py: + + 2010-05-07 Eric Seidel <eric@webkit.org> + + Reviewed by Daniel Bates. + + rollout commands fail when commit is missing bug number + https://bugs.webkit.org/show_bug.cgi?id=38791 + + * Scripts/webkitpy/tool/commands/download.py: + - _commit_info failed to actually return the CommitInfo in the no-bug-id case. + * Scripts/webkitpy/tool/commands/download_unittest.py: + - Test that the fix worked. + +2010-05-07 Darin Fisher <darin@chromium.org> + + Fix build bustage: toElement<T> should be to<T>. + + * DumpRenderTree/chromium/LayoutTestController.cpp: + (LayoutTestController::elementDoesAutoCompleteForElementWithId): + 2010-05-07 Chris Jerdonek <cjerdonek@webkit.org> Reviewed by Daniel Bates. diff --git a/WebKitTools/DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp b/WebKitTools/DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp deleted file mode 100644 index 7167bf2..0000000 --- a/WebKitTools/DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp +++ /dev/null @@ -1,207 +0,0 @@ -# -# 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. -# - -{ - 'includes': [ - '../../../WebKit/chromium/features.gypi', - ], - 'variables': { - 'webkit_top': '../../..', - 'webkit_api_dir': '<(webkit_top)/WebKit/chromium', - 'conditions': [ - # Location of the chromium src directory and target type is different - # if webkit is built inside chromium or as standalone project. - ['inside_chromium_build==0', { - # DumpRenderTree is being built outside of the full chromium project. - # e.g. via build-dumprendertree --chromium - 'chromium_src_dir': '<(webkit_api_dir)', - 'webkit_support_gyp': '<(webkit_api_dir)/webkit/support/webkit_support.gyp', - },{ - # WebKit is checked out in src/chromium/third_party/WebKit - 'chromium_src_dir': '<(webkit_top)/../..', - 'webkit_support_gyp': '<(webkit_top)/../../webkit/webkit.gyp', - }], - ], - }, - 'target_defaults': { - 'target_conditions': [ - ['OS!="linux" and OS!="freebsd" and OS!="openbsd"', { - 'sources/': [ - ['exclude', '(Gtk|Linux)\\.cpp$'] - ] - }], - ['OS!="win"', { - 'sources/': [ - ['exclude', 'Win\\.cpp$'], - ] - }], - ['OS!="mac"', { - 'sources/': [ - # .mm is already excluded by common.gypi - ['exclude', 'Mac\\.cpp$'], - ] - }], - ], - }, - 'targets': [ - { - 'target_name': 'DumpRenderTree', - 'type': 'executable', - 'mac_bundle': 1, - 'dependencies': [ - '<(webkit_api_dir)/WebKit.gyp:webkit', - '<(webkit_top)/JavaScriptCore/JavaScriptCore.gyp/JavaScriptCore.gyp:wtf_config', - '<(chromium_src_dir)/third_party/icu/icu.gyp:icuuc', - '<(chromium_src_dir)/third_party/npapi/npapi.gyp:npapi', - '<(chromium_src_dir)/skia/skia.gyp:skia', - '<(webkit_support_gyp):webkit_support', - ], - 'include_dirs': [ - '.', - '<(webkit_api_dir)', - '<(webkit_top)/JavaScriptCore', - '<(chromium_src_dir)', - ], - 'defines': [ - # Technically not a unit test but require functions available only to - # unit tests. - 'UNIT_TEST', - ], - 'sources': [ - '../chromium/AccessibilityController.cpp', - '../chromium/AccessibilityController.h', - '../chromium/AccessibilityUIElement.cpp', - '../chromium/AccessibilityUIElement.h', - '../chromium/CppBoundClass.cpp', - '../chromium/CppBoundClass.h', - '../chromium/CppVariant.cpp', - '../chromium/CppVariant.h', - '../chromium/DumpRenderTree.cpp', - '../chromium/EventSender.cpp', - '../chromium/EventSender.h', - '../chromium/LayoutTestController.cpp', - '../chromium/LayoutTestController.h', - '../chromium/MockSpellCheck.cpp', - '../chromium/MockSpellCheck.h', - '../chromium/PlainTextController.cpp', - '../chromium/PlainTextController.h', - '../chromium/TestNavigationController.cpp', - '../chromium/TestNavigationController.h', - '../chromium/TestShell.cpp', - '../chromium/TestShell.h', - '../chromium/TestShellGtk.cpp', - '../chromium/TestShellMac.mm', - '../chromium/TestShellWin.cpp', - '../chromium/TextInputController.cpp', - '../chromium/TextInputController.h', - '../chromium/WebViewHost.cpp', - '../chromium/WebViewHost.h', - ], - 'conditions': [ - ['OS=="mac"', { - 'dependencies': ['LayoutTestHelper'], - 'mac_bundle_resources': [ - '../qt/fonts/AHEM____.TTF', - '../fonts/WebKitWeightWatcher100.ttf', - '../fonts/WebKitWeightWatcher200.ttf', - '../fonts/WebKitWeightWatcher300.ttf', - '../fonts/WebKitWeightWatcher400.ttf', - '../fonts/WebKitWeightWatcher500.ttf', - '../fonts/WebKitWeightWatcher600.ttf', - '../fonts/WebKitWeightWatcher700.ttf', - '../fonts/WebKitWeightWatcher800.ttf', - '../fonts/WebKitWeightWatcher900.ttf', - ], - 'actions': [ - { - 'action_name': 'repack_locale', - 'variables': { - 'repack_path': '<(chromium_src_dir)/tools/data_pack/repack.py', - 'pak_inputs': [ - '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_chromium_resources.pak', - '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_strings_en-US.pak', - '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_resources.pak', - ]}, - 'inputs': [ - '<(repack_path)', - '<@(pak_inputs)', - ], - 'outputs': [ - '<(INTERMEDIATE_DIR)/repack/DumpRenderTree.pak', - ], - 'action': ['python', '<(repack_path)', '<@(_outputs)', '<@(pak_inputs)'], - 'process_outputs_as_mac_bundle_resources': 1, - }, - ], # actions - }], # mac - ], # conditions - }, - - { - 'target_name': 'ImageDiff', - 'type': 'executable', - 'dependencies': [ - '<(webkit_api_dir)/WebKit.gyp:webkit', - '<(webkit_top)/JavaScriptCore/JavaScriptCore.gyp/JavaScriptCore.gyp:wtf', - '<(chromium_src_dir)/gfx/gfx.gyp:gfx', - ], - 'include_dirs': [ - '<(webkit_top)/JavaScriptCore', - '<(chromium_src_dir)', - ], - 'sources': [ - '../chromium/ImageDiff.cpp', - ], - }, - ], # targets - - 'conditions': [ - ['OS=="mac"', { - 'targets': [ - { - 'target_name': 'LayoutTestHelper', - 'type': 'executable', - 'sources': ['../chromium/LayoutTestHelper.mm'], - 'link_settings': { - 'libraries': [ - '$(SDKROOT)/System/Library/Frameworks/AppKit.framework', - ], - }, - }, - ], - }], - ], # conditions -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/WebKitTools/DumpRenderTree/DumpRenderTree.gypi b/WebKitTools/DumpRenderTree/DumpRenderTree.gypi new file mode 100644 index 0000000..693f02e --- /dev/null +++ b/WebKitTools/DumpRenderTree/DumpRenderTree.gypi @@ -0,0 +1,36 @@ +{ + 'variables': { + 'drt_files': [ + 'chromium/AccessibilityController.cpp', + 'chromium/AccessibilityController.h', + 'chromium/AccessibilityUIElement.cpp', + 'chromium/AccessibilityUIElement.h', + 'chromium/CppBoundClass.cpp', + 'chromium/CppBoundClass.h', + 'chromium/CppVariant.cpp', + 'chromium/CppVariant.h', + 'chromium/DumpRenderTree.cpp', + 'chromium/EventSender.cpp', + 'chromium/EventSender.h', + 'chromium/LayoutTestController.cpp', + 'chromium/LayoutTestController.h', + 'chromium/MockSpellCheck.cpp', + 'chromium/MockSpellCheck.h', + 'chromium/NotificationPresenter.h', + 'chromium/NotificationPresenter.cpp', + 'chromium/PlainTextController.cpp', + 'chromium/PlainTextController.h', + 'chromium/TestNavigationController.cpp', + 'chromium/TestNavigationController.h', + 'chromium/TestShell.cpp', + 'chromium/TestShell.h', + 'chromium/TestShellGtk.cpp', + 'chromium/TestShellMac.mm', + 'chromium/TestShellWin.cpp', + 'chromium/TextInputController.cpp', + 'chromium/TextInputController.h', + 'chromium/WebViewHost.cpp', + 'chromium/WebViewHost.h', + ], + } +} diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp index 83f420f..60103a5 100644 --- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp +++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp @@ -971,6 +971,7 @@ static NPObject *pluginAllocate(NPP npp, NPClass *theClass) newInstance->lastUrl = NULL; newInstance->lastHeaders = NULL; + newInstance->testGetURLOnDestroy = FALSE; newInstance->testDocumentOpenInDestroyStream = FALSE; newInstance->testWindowOpen = FALSE; newInstance->testKeyboardFocusForPlugins = FALSE; diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h index 2ae4dbf..0020e56 100644 --- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h +++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h @@ -40,6 +40,7 @@ typedef struct { NPObject* rememberedObject; NPStream* stream; NPBool testDocumentOpenInDestroyStream; + NPBool testGetURLOnDestroy; NPBool testWindowOpen; NPBool testKeyboardFocusForPlugins; char* onStreamLoad; diff --git a/WebKitTools/DumpRenderTree/chromium/EventSender.cpp b/WebKitTools/DumpRenderTree/chromium/EventSender.cpp index c48aaf4..381112e 100644 --- a/WebKitTools/DumpRenderTree/chromium/EventSender.cpp +++ b/WebKitTools/DumpRenderTree/chromium/EventSender.cpp @@ -50,6 +50,7 @@ #include "public/WebDragOperation.h" #include "public/WebPoint.h" #include "public/WebString.h" +#include "public/WebTouchPoint.h" #include "public/WebView.h" #include "webkit/support/webkit_support.h" #include <wtf/Deque.h> @@ -65,7 +66,6 @@ using namespace base; using namespace std; using namespace WebKit; -TestShell* EventSender::testShell = 0; WebPoint EventSender::lastMousePos; WebMouseEvent::Button EventSender::pressedButton = WebMouseEvent::ButtonNone; WebMouseEvent::Button EventSender::lastButtonType = WebMouseEvent::ButtonNone; @@ -94,6 +94,8 @@ static WebDragOperation currentDragEffect; static WebDragOperationsMask currentDragEffectsAllowed; static bool replayingSavedEvents = false; static Deque<SavedEvent> mouseEventQueue; +static int touchModifiers; +static Vector<WebTouchPoint> touchPoints; // Time and place of the last mouse up event. static double lastClickTimeSec = 0; @@ -161,7 +163,7 @@ static bool applyKeyModifier(const string& modifierName, WebInputEvent* event) event->modifiers |= WebInputEvent::AltKey; #if !OS(MAC_OS_X) // On Windows all keys with Alt modifier will be marked as system key. - // We keep the same behavior on Linux, see: + // We keep the same behavior on Linux and everywhere non-Mac, see: // WebKit/chromium/src/gtk/WebInputEventFactory.cpp // If we want to change this behavior on Linux, this piece of code must be // kept in sync with the related code in above file. @@ -246,13 +248,8 @@ enum KeyLocationCode { EventSender::EventSender(TestShell* shell) : m_methodFactory(this) + , m_shell(shell) { - // Set static testShell variable since we can't do it in an initializer list. - // We also need to be careful not to assign testShell to new windows which are - // temporary. - if (!testShell) - testShell = shell; - // Initialize the map that associates methods of this class with the names // they will use when called by JavaScript. The actual binding of those // names to their methods will be done by calling bindToJavaScript() (defined @@ -274,6 +271,16 @@ EventSender::EventSender(TestShell* shell) bindMethod("zoomPageOut", &EventSender::zoomPageOut); bindMethod("scheduleAsynchronousClick", &EventSender::scheduleAsynchronousClick); bindMethod("beginDragWithFiles", &EventSender::beginDragWithFiles); + bindMethod("addTouchPoint", &EventSender::addTouchPoint); + bindMethod("cancelTouchPoint", &EventSender::cancelTouchPoint); + bindMethod("clearTouchPoints", &EventSender::clearTouchPoints); + bindMethod("releaseTouchPoint", &EventSender::releaseTouchPoint); + bindMethod("updateTouchPoint", &EventSender::updateTouchPoint); + bindMethod("setTouchModifier", &EventSender::setTouchModifier); + bindMethod("touchCancel", &EventSender::touchCancel); + bindMethod("touchEnd", &EventSender::touchEnd); + bindMethod("touchMove", &EventSender::touchMove); + bindMethod("touchStart", &EventSender::touchStart); // When set to true (the default value), we batch mouse move and mouse up // events so we can simulate drag & drop. @@ -315,16 +322,16 @@ void EventSender::reset() clickCount = 0; lastButtonType = WebMouseEvent::ButtonNone; timeOffsetMs = 0; + touchModifiers = 0; + touchPoints.clear(); } WebView* EventSender::webview() { - return testShell->webView(); + return m_shell->webView(); } -void EventSender::doDragDrop(const WebKit::WebPoint& eventPos, - const WebDragData& dragData, - WebDragOperationsMask mask) +void EventSender::doDragDrop(const WebDragData& dragData, WebDragOperationsMask mask) { WebMouseEvent event; initMouseEvent(WebInputEvent::MouseDown, pressedButton, lastMousePos, &event); @@ -610,11 +617,11 @@ void EventSender::keyDown(const CppArgumentList& arguments, CppVariant* result) // We just simulate the same behavior here. string editCommand; if (getEditCommand(eventDown, &editCommand)) - testShell->webViewHost()->setEditCommand(editCommand, ""); + m_shell->webViewHost()->setEditCommand(editCommand, ""); webview()->handleInputEvent(eventDown); - testShell->webViewHost()->clearEditCommand(); + m_shell->webViewHost()->clearEditCommand(); if (generateChar) { eventChar.type = WebInputEvent::Char; @@ -787,6 +794,124 @@ void EventSender::beginDragWithFiles(const CppArgumentList& arguments, CppVarian result->setNull(); } +void EventSender::addTouchPoint(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + WebTouchPoint touchPoint; + touchPoint.state = WebTouchPoint::StatePressed; + touchPoint.position = WebPoint(arguments[0].toInt32(), arguments[1].toInt32()); + touchPoint.id = touchPoints.size(); + touchPoints.append(touchPoint); +} + +void EventSender::clearTouchPoints(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + touchPoints.clear(); +} + +void EventSender::releaseTouchPoint(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + const unsigned index = arguments[0].toInt32(); + ASSERT(index < touchPoints.size()); + + WebTouchPoint* touchPoint = &touchPoints[index]; + touchPoint->state = WebTouchPoint::StateReleased; +} + +void EventSender::setTouchModifier(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + int mask = 0; + const string keyName = arguments[0].toString(); + if (keyName == "shift") + mask = WebInputEvent::ShiftKey; + else if (keyName == "alt") + mask = WebInputEvent::AltKey; + else if (keyName == "ctrl") + mask = WebInputEvent::ControlKey; + else if (keyName == "meta") + mask = WebInputEvent::MetaKey; + + if (arguments[1].toBoolean()) + touchModifiers |= mask; + else + touchModifiers &= ~mask; +} + +void EventSender::updateTouchPoint(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + const unsigned index = arguments[0].toInt32(); + ASSERT(index < touchPoints.size()); + + WebPoint position(arguments[1].toInt32(), arguments[2].toInt32()); + WebTouchPoint* touchPoint = &touchPoints[index]; + touchPoint->state = WebTouchPoint::StateMoved; + touchPoint->position = position; +} + +void EventSender::cancelTouchPoint(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + const unsigned index = arguments[0].toInt32(); + ASSERT(index < touchPoints.size()); + + WebTouchPoint* touchPoint = &touchPoints[index]; + touchPoint->state = WebTouchPoint::StateCancelled; +} + +void EventSender::sendCurrentTouchEvent(const WebInputEvent::Type type) +{ + ASSERT(static_cast<unsigned>(WebTouchEvent::touchPointsLengthCap) > touchPoints.size()); + WebTouchEvent touchEvent; + touchEvent.type = type; + touchEvent.modifiers = touchModifiers; + touchEvent.touchPointsLength = touchPoints.size(); + for (unsigned i = 0; i < touchPoints.size(); ++i) + touchEvent.touchPoints[i] = touchPoints[i]; + webview()->handleInputEvent(touchEvent); + + for (unsigned i = 0; i < touchPoints.size(); ++i) { + WebTouchPoint* touchPoint = &touchPoints[i]; + if (touchPoint->state == WebTouchPoint::StateReleased) { + touchPoints.remove(i); + --i; + } else + touchPoint->state = WebTouchPoint::StateStationary; + } +} + +void EventSender::touchEnd(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + sendCurrentTouchEvent(WebInputEvent::TouchEnd); +} + +void EventSender::touchMove(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + sendCurrentTouchEvent(WebInputEvent::TouchMove); +} + +void EventSender::touchStart(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + sendCurrentTouchEvent(WebInputEvent::TouchStart); +} + +void EventSender::touchCancel(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + sendCurrentTouchEvent(WebInputEvent::TouchCancel); +} + // // Unimplemented stubs // diff --git a/WebKitTools/DumpRenderTree/chromium/EventSender.h b/WebKitTools/DumpRenderTree/chromium/EventSender.h index 756b008..ac475a3 100644 --- a/WebKitTools/DumpRenderTree/chromium/EventSender.h +++ b/WebKitTools/DumpRenderTree/chromium/EventSender.h @@ -60,9 +60,7 @@ public: void reset(); // Simulate drag&drop system call. - static void doDragDrop(const WebKit::WebPoint&, - const WebKit::WebDragData&, - WebKit::WebDragOperationsMask); + void doDragDrop(const WebKit::WebDragData&, WebKit::WebDragOperationsMask); // JS callback methods. void mouseDown(const CppArgumentList&, CppVariant*); @@ -80,6 +78,17 @@ public: void beginDragWithFiles(const CppArgumentList&, CppVariant*); CppVariant dragMode; + void addTouchPoint(const CppArgumentList&, CppVariant*); + void cancelTouchPoint(const CppArgumentList&, CppVariant*); + void clearTouchPoints(const CppArgumentList&, CppVariant*); + void releaseTouchPoint(const CppArgumentList&, CppVariant*); + void setTouchModifier(const CppArgumentList&, CppVariant*); + void touchCancel(const CppArgumentList&, CppVariant*); + void touchEnd(const CppArgumentList&, CppVariant*); + void touchMove(const CppArgumentList&, CppVariant*); + void touchStart(const CppArgumentList&, CppVariant*); + void updateTouchPoint(const CppArgumentList&, CppVariant*); + // Unimplemented stubs void contextClick(const CppArgumentList&, CppVariant*); void enableDOMUIEventLogging(const CppArgumentList&, CppVariant*); @@ -100,17 +109,17 @@ public: private: // Returns the test shell's webview. - static WebKit::WebView* webview(); + WebKit::WebView* webview(); // Returns true if dragMode is true. bool isDragMode() { return dragMode.isBool() && dragMode.toBoolean(); } // Sometimes we queue up mouse move and mouse up events for drag drop // handling purposes. These methods dispatch the event. - static void doMouseMove(const WebKit::WebMouseEvent&); - static void doMouseUp(const WebKit::WebMouseEvent&); + void doMouseMove(const WebKit::WebMouseEvent&); + void doMouseUp(const WebKit::WebMouseEvent&); static void doLeapForward(int milliseconds); - static void replaySavedEvents(); + void replaySavedEvents(); // Helper to return the button type given a button code static WebKit::WebMouseEvent::Button getButtonTypeFromButtonNumber(int); @@ -125,10 +134,13 @@ private: void updateClickCountForButton(WebKit::WebMouseEvent::Button); + // Compose a touch event from the current touch points and send it. + void sendCurrentTouchEvent(const WebKit::WebInputEvent::Type); + ScopedRunnableMethodFactory<EventSender> m_methodFactory; // Non-owning pointer. The EventSender is owned by the TestShell. - static TestShell* testShell; + TestShell* m_shell; // Location of last mouseMoveTo event. static WebKit::WebPoint lastMousePos; diff --git a/WebKitTools/DumpRenderTree/chromium/LayoutTestController.cpp b/WebKitTools/DumpRenderTree/chromium/LayoutTestController.cpp index 21b168c..4413ff2 100644 --- a/WebKitTools/DumpRenderTree/chromium/LayoutTestController.cpp +++ b/WebKitTools/DumpRenderTree/chromium/LayoutTestController.cpp @@ -41,6 +41,7 @@ #include "public/WebFrame.h" #include "public/WebInputElement.h" #include "public/WebKit.h" +#include "public/WebNotificationPresenter.h" #include "public/WebScriptSource.h" #include "public/WebSecurityPolicy.h" #include "public/WebSettings.h" @@ -48,11 +49,13 @@ #include "public/WebURL.h" #include "public/WebView.h" #include "webkit/support/webkit_support.h" +#include <wtf/text/WTFString.h> #if OS(WINDOWS) #include <wtf/OwnArrayPtr.h> #endif +using namespace WebCore; using namespace WebKit; using namespace std; @@ -90,10 +93,12 @@ LayoutTestController::LayoutTestController(TestShell* shell) bindMethod("setCloseRemainingWindowsWhenComplete", &LayoutTestController::setCloseRemainingWindowsWhenComplete); bindMethod("objCIdentityIsEqual", &LayoutTestController::objCIdentityIsEqual); bindMethod("setAlwaysAcceptCookies", &LayoutTestController::setAlwaysAcceptCookies); + bindMethod("showWebInspector", &LayoutTestController::showWebInspector); bindMethod("setWindowIsKey", &LayoutTestController::setWindowIsKey); bindMethod("setTabKeyCyclesThroughElements", &LayoutTestController::setTabKeyCyclesThroughElements); bindMethod("setUserStyleSheetLocation", &LayoutTestController::setUserStyleSheetLocation); bindMethod("setUserStyleSheetEnabled", &LayoutTestController::setUserStyleSheetEnabled); + bindMethod("setAuthorAndUserStylesEnabled", &LayoutTestController::setAuthorAndUserStylesEnabled); bindMethod("pathToLocalResource", &LayoutTestController::pathToLocalResource); bindMethod("addFileToPasteboardOnDrag", &LayoutTestController::addFileToPasteboardOnDrag); bindMethod("execCommand", &LayoutTestController::execCommand); @@ -109,18 +114,23 @@ LayoutTestController::LayoutTestController(TestShell* shell) bindMethod("disableImageLoading", &LayoutTestController::disableImageLoading); bindMethod("setIconDatabaseEnabled", &LayoutTestController::setIconDatabaseEnabled); bindMethod("setCustomPolicyDelegate", &LayoutTestController::setCustomPolicyDelegate); + bindMethod("setScrollbarPolicy", &LayoutTestController::setScrollbarPolicy); bindMethod("waitForPolicyDelegate", &LayoutTestController::waitForPolicyDelegate); + bindMethod("setWillSendRequestClearHeader", &LayoutTestController::setWillSendRequestClearHeader); bindMethod("setWillSendRequestReturnsNullOnRedirect", &LayoutTestController::setWillSendRequestReturnsNullOnRedirect); bindMethod("setWillSendRequestReturnsNull", &LayoutTestController::setWillSendRequestReturnsNull); bindMethod("addOriginAccessWhitelistEntry", &LayoutTestController::addOriginAccessWhitelistEntry); + bindMethod("removeOriginAccessWhitelistEntry", &LayoutTestController::removeOriginAccessWhitelistEntry); bindMethod("clearAllDatabases", &LayoutTestController::clearAllDatabases); bindMethod("setDatabaseQuota", &LayoutTestController::setDatabaseQuota); bindMethod("setPOSIXLocale", &LayoutTestController::setPOSIXLocale); bindMethod("counterValueForElementById", &LayoutTestController::counterValueForElementById); bindMethod("addUserScript", &LayoutTestController::addUserScript); + bindMethod("addUserStyleSheet", &LayoutTestController::addUserStyleSheet); bindMethod("pageNumberForElementById", &LayoutTestController::pageNumberForElementById); bindMethod("numberOfPages", &LayoutTestController::numberOfPages); bindMethod("dumpSelectionRect", &LayoutTestController::dumpSelectionRect); + bindMethod("grantDesktopNotificationPermission", &LayoutTestController::grantDesktopNotificationPermission); // The following are stubs. bindMethod("dumpAsWebArchive", &LayoutTestController::dumpAsWebArchive); @@ -135,6 +145,7 @@ LayoutTestController::LayoutTestController(TestShell* shell) bindMethod("accessStoredWebScriptObject", &LayoutTestController::accessStoredWebScriptObject); bindMethod("objCClassNameOf", &LayoutTestController::objCClassNameOf); bindMethod("addDisallowedURL", &LayoutTestController::addDisallowedURL); + bindMethod("callShouldCloseOnWebView", &LayoutTestController::callShouldCloseOnWebView); bindMethod("setCallCloseOnWebViews", &LayoutTestController::setCallCloseOnWebViews); bindMethod("setPrivateBrowsingEnabled", &LayoutTestController::setPrivateBrowsingEnabled); bindMethod("setUseDashboardCompatibilityMode", &LayoutTestController::setUseDashboardCompatibilityMode); @@ -440,11 +451,11 @@ void LayoutTestController::reset() if (m_shell) { m_shell->webView()->setZoomLevel(false, 0); m_shell->webView()->setTabKeyCyclesThroughElements(true); -#if defined(OS_LINUX) +#if !OS(DARWIN) && !OS(WINDOWS) // Actually, TOOLKIT_GTK // (Constants copied because we can't depend on the header that defined // them from this file.) m_shell->webView()->setSelectionColors(0xff1e90ff, 0xff000000, 0xffc8c8c8, 0xff323232); -#endif // defined(OS_LINUX) +#endif m_shell->webView()->removeAllUserContent(); } m_dumpAsText = false; @@ -468,7 +479,7 @@ void LayoutTestController::reset() m_webHistoryItemCount.set(0); webkit_support::SetAcceptAllCookies(false); - WebSecurityPolicy::resetOriginAccessWhiteLists(); + WebSecurityPolicy::resetOriginAccessWhitelists(); // Reset the default quota for each origin to 5MB webkit_support::SetDatabaseQuota(5 * 1024 * 1024); @@ -532,6 +543,12 @@ void LayoutTestController::setAlwaysAcceptCookies(const CppArgumentList& argumen result->setNull(); } +void LayoutTestController::showWebInspector(const CppArgumentList&, CppVariant* result) +{ + m_shell->showDevTools(); + result->setNull(); +} + void LayoutTestController::setWindowIsKey(const CppArgumentList& arguments, CppVariant* result) { if (arguments.size() > 0 && arguments[0].isBool()) @@ -555,6 +572,13 @@ void LayoutTestController::setUserStyleSheetLocation(const CppArgumentList& argu result->setNull(); } +void LayoutTestController::setAuthorAndUserStylesEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webView()->settings()->setAuthorAndUserStylesEnabled(arguments[0].value.boolValue); + result->setNull(); +} + void LayoutTestController::execCommand(const CppArgumentList& arguments, CppVariant* result) { result->setNull(); @@ -599,6 +623,13 @@ void LayoutTestController::setUseDashboardCompatibilityMode(const CppArgumentLis result->setNull(); } +void LayoutTestController::setScrollbarPolicy(const CppArgumentList&, CppVariant* result) +{ + // FIXME: implement. + // Currently only has a non-null implementation on QT. + result->setNull(); +} + void LayoutTestController::setCustomPolicyDelegate(const CppArgumentList& arguments, CppVariant* result) { if (arguments.size() > 0 && arguments[0].isBool()) { @@ -618,6 +649,16 @@ void LayoutTestController::waitForPolicyDelegate(const CppArgumentList&, CppVari result->setNull(); } +void LayoutTestController::setWillSendRequestClearHeader(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) { + string header = arguments[0].toString(); + if (!header.empty()) + m_shell->webViewHost()->addClearHeader(String::fromUTF8(header.c_str())); + } + result->setNull(); +} + void LayoutTestController::setWillSendRequestReturnsNullOnRedirect(const CppArgumentList& arguments, CppVariant* result) { if (arguments.size() > 0 && arguments[0].isBool()) @@ -734,7 +775,7 @@ bool LayoutTestController::elementDoesAutoCompleteForElementWithId(const WebStri if (element.isNull() || !element.hasTagName("input")) return false; - WebInputElement inputElement = element.toElement<WebInputElement>(); + WebInputElement inputElement = element.to<WebInputElement>(); return inputElement.autoComplete(); } @@ -800,6 +841,21 @@ void LayoutTestController::setIconDatabaseEnabled(const CppArgumentList&, CppVar result->setNull(); } +void LayoutTestController::callShouldCloseOnWebView(const CppArgumentList&, CppVariant* result) +{ + result->set(m_shell->webView()->dispatchBeforeUnloadEvent()); +} + +void LayoutTestController::grantDesktopNotificationPermission(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() != 1 || !arguments[0].isString()) { + result->set(false); + return; + } + m_shell->notificationPresenter()->grantPermission(WebString::fromUTF8(arguments[0].toString())); + result->set(true); +} + // // Unimplemented stubs // @@ -1052,10 +1108,30 @@ void LayoutTestController::addOriginAccessWhitelistEntry(const CppArgumentList& if (!url.isValid()) return; - WebSecurityPolicy::whiteListAccessFromOrigin(url, - WebString::fromUTF8(arguments[1].toString()), - WebString::fromUTF8(arguments[2].toString()), - arguments[3].toBoolean()); + WebSecurityPolicy::addOriginAccessWhitelistEntry( + url, + WebString::fromUTF8(arguments[1].toString()), + WebString::fromUTF8(arguments[2].toString()), + arguments[3].toBoolean()); +} + +void LayoutTestController::removeOriginAccessWhitelistEntry(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() != 4 || !arguments[0].isString() || !arguments[1].isString() + || !arguments[2].isString() || !arguments[3].isBool()) + return; + + WebKit::WebURL url(GURL(arguments[0].toString())); + if (!url.isValid()) + return; + + WebSecurityPolicy::removeOriginAccessWhitelistEntry( + url, + WebString::fromUTF8(arguments[1].toString()), + WebString::fromUTF8(arguments[2].toString()), + arguments[3].toBoolean()); } void LayoutTestController::clearAllDatabases(const CppArgumentList& arguments, CppVariant* result) @@ -1183,7 +1259,15 @@ void LayoutTestController::forceRedSelectionColors(const CppArgumentList& argume void LayoutTestController::addUserScript(const CppArgumentList& arguments, CppVariant* result) { result->setNull(); - if (arguments.size() < 1 || !arguments[0].isString() || !arguments[1].isBool()) + if (arguments.size() < 2 || !arguments[0].isString() || !arguments[1].isBool()) return; m_shell->webView()->addUserScript(WebString::fromUTF8(arguments[0].toString()), arguments[1].toBoolean()); } + +void LayoutTestController::addUserStyleSheet(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isString()) + return; + m_shell->webView()->addUserStyleSheet(WebString::fromUTF8(arguments[0].toString())); +} diff --git a/WebKitTools/DumpRenderTree/chromium/LayoutTestController.h b/WebKitTools/DumpRenderTree/chromium/LayoutTestController.h index ca8b014..e7a48e2 100644 --- a/WebKitTools/DumpRenderTree/chromium/LayoutTestController.h +++ b/WebKitTools/DumpRenderTree/chromium/LayoutTestController.h @@ -127,6 +127,9 @@ public: // Changes the cookie policy from the default to allow all cookies. void setAlwaysAcceptCookies(const CppArgumentList&, CppVariant*); + // Shows DevTools window. + void showWebInspector(const CppArgumentList&, CppVariant*); + // Gives focus to the window. void setWindowIsKey(const CppArgumentList&, CppVariant*); @@ -139,11 +142,16 @@ public: void setUserStyleSheetEnabled(const CppArgumentList&, CppVariant*); void setUserStyleSheetLocation(const CppArgumentList&, CppVariant*); + // Passes this preference through to WebSettings. + void setAuthorAndUserStylesEnabled(const CppArgumentList&, CppVariant*); + // Puts Webkit in "dashboard compatibility mode", which is used in obscure // Mac-only circumstances. It's not really necessary, and will most likely // never be used by Chrome, but some layout tests depend on its presence. void setUseDashboardCompatibilityMode(const CppArgumentList&, CppVariant*); + void setScrollbarPolicy(const CppArgumentList&, CppVariant*); + // Causes navigation actions just printout the intended navigation instead // of taking you to the page. This is used for cases like mailto, where you // don't actually want to open the mail program. @@ -152,6 +160,9 @@ public: // Delays completion of the test until the policy delegate runs. void waitForPolicyDelegate(const CppArgumentList&, CppVariant*); + // Causes WillSendRequest to clear certain headers. + void setWillSendRequestClearHeader(const CppArgumentList&, CppVariant*); + // Causes WillSendRequest to block redirects. void setWillSendRequestReturnsNullOnRedirect(const CppArgumentList&, CppVariant*); @@ -195,6 +206,9 @@ public: void dumpSelectionRect(const CppArgumentList&, CppVariant*); + // Grants permission for desktop notifications to an origin + void grantDesktopNotificationPermission(const CppArgumentList&, CppVariant*); + // The following are only stubs. TODO(pamg): Implement any of these that // are needed to pass the layout tests. void dumpAsWebArchive(const CppArgumentList&, CppVariant*); @@ -210,6 +224,7 @@ public: void accessStoredWebScriptObject(const CppArgumentList&, CppVariant*); void objCClassNameOf(const CppArgumentList&, CppVariant*); void addDisallowedURL(const CppArgumentList&, CppVariant*); + void callShouldCloseOnWebView(const CppArgumentList&, CppVariant*); void setCallCloseOnWebViews(const CppArgumentList&, CppVariant*); void setPrivateBrowsingEnabled(const CppArgumentList&, CppVariant*); @@ -228,8 +243,9 @@ public: // that case (as the Mac does). void fallbackMethod(const CppArgumentList&, CppVariant*); - // Allows layout tests to call SecurityOrigin::addOriginAccessWhitelistEntry(). + // Allows layout tests to manage origins' whitelisting. void addOriginAccessWhitelistEntry(const CppArgumentList&, CppVariant*); + void removeOriginAccessWhitelistEntry(const CppArgumentList&, CppVariant*); // Clears all databases. void clearAllDatabases(const CppArgumentList&, CppVariant*); @@ -259,8 +275,9 @@ public: // Forces the selection colors for testing under Linux. void forceRedSelectionColors(const CppArgumentList&, CppVariant*); - // Adds a user script to be injected into new documents. + // Adds a user script or user style sheet to be injected into new documents. void addUserScript(const CppArgumentList&, CppVariant*); + void addUserStyleSheet(const CppArgumentList&, CppVariant*); public: // The following methods are not exposed to JavaScript. diff --git a/WebKitTools/DumpRenderTree/chromium/NotificationPresenter.cpp b/WebKitTools/DumpRenderTree/chromium/NotificationPresenter.cpp new file mode 100644 index 0000000..86903be --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/NotificationPresenter.cpp @@ -0,0 +1,119 @@ +/* + * 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. + */ + +#include "config.h" +#include "NotificationPresenter.h" + +#include "googleurl/src/gurl.h" +#include "public/WebNotification.h" +#include "public/WebNotificationPermissionCallback.h" +#include "public/WebSecurityOrigin.h" +#include "public/WebString.h" +#include "public/WebURL.h" +#include <wtf/text/CString.h> + +using namespace WebCore; +using namespace WebKit; + +void NotificationPresenter::grantPermission(const WebString& origin) +{ + // Make sure it's in the form of an origin. + GURL url(origin); + m_allowedOrigins.add(String(url.GetOrigin().spec().c_str())); +} + +// The output from all these methods matches what DumpRenderTree produces. +bool NotificationPresenter::show(const WebNotification& notification) +{ + if (!notification.replaceId().isEmpty()) { + String replaceId(notification.replaceId().data(), notification.replaceId().length()); + if (m_replacements.find(replaceId) != m_replacements.end()) + printf("REPLACING NOTIFICATION %s\n", + m_replacements.find(replaceId)->second.utf8().data()); + + WebString identifier = notification.isHTML() ? + notification.url().spec().utf16() : notification.title(); + m_replacements.set(replaceId, String(identifier.data(), identifier.length())); + } + + if (notification.isHTML()) { + printf("DESKTOP NOTIFICATION: contents at %s\n", + notification.url().spec().data()); + } else { + printf("DESKTOP NOTIFICATION:%s icon %s, title %s, text %s\n", + notification.dir() == "rtl" ? "(RTL)" : "", + notification.iconURL().isEmpty() ? "" : + notification.iconURL().spec().data(), + notification.title().isEmpty() ? "" : + notification.title().utf8().data(), + notification.body().isEmpty() ? "" : + notification.body().utf8().data()); + } + + WebNotification eventTarget(notification); + eventTarget.dispatchDisplayEvent(); + return true; +} + +void NotificationPresenter::cancel(const WebNotification& notification) +{ + WebString identifier; + if (notification.isHTML()) + identifier = notification.url().spec().utf16(); + else + identifier = notification.title(); + + printf("DESKTOP NOTIFICATION CLOSED: %s\n", identifier.utf8().data()); + WebNotification eventTarget(notification); + eventTarget.dispatchCloseEvent(false); +} + +void NotificationPresenter::objectDestroyed(const WebKit::WebNotification& notification) +{ + // Nothing to do. Not storing the objects. +} + +WebNotificationPresenter::Permission NotificationPresenter::checkPermission(const WebURL& url) +{ + // Check with the layout test controller + String origin = String(static_cast<GURL>(url).GetOrigin().spec().c_str()); + bool allowed = m_allowedOrigins.find(origin) != m_allowedOrigins.end(); + return allowed ? WebNotificationPresenter::PermissionAllowed + : WebNotificationPresenter::PermissionDenied; +} + +void NotificationPresenter::requestPermission( + const WebSecurityOrigin& origin, + WebNotificationPermissionCallback* callback) +{ + printf("DESKTOP NOTIFICATION PERMISSION REQUESTED: %s\n", + origin.toString().utf8().data()); + callback->permissionRequestComplete(); +} diff --git a/WebKitTools/DumpRenderTree/chromium/NotificationPresenter.h b/WebKitTools/DumpRenderTree/chromium/NotificationPresenter.h new file mode 100644 index 0000000..f30862f --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/NotificationPresenter.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#ifndef NotificationPresenter_h +#define NotificationPresenter_h + +#include "public/WebNotificationPresenter.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> + +class TestShell; + +// A class that implements WebNotificationPresenter for DRT. +class NotificationPresenter : public WebKit::WebNotificationPresenter { +public: + explicit NotificationPresenter(TestShell* shell) : m_shell(shell) {} + + // Called by the LayoutTestController to simulate a user granting permission. + void grantPermission(const WebKit::WebString& origin); + + // WebKit::WebNotificationPresenter interface + virtual bool show(const WebKit::WebNotification&); + virtual void cancel(const WebKit::WebNotification&); + virtual void objectDestroyed(const WebKit::WebNotification&); + virtual Permission checkPermission(const WebKit::WebURL&); + virtual void requestPermission(const WebKit::WebSecurityOrigin&, WebKit::WebNotificationPermissionCallback*); + + void reset() { m_allowedOrigins.clear(); } + +private: + // Non-owned pointer. The NotificationPresenter is owned by the test shell. + TestShell* m_shell; + + // Set of allowed origins. + HashSet<WebCore::String> m_allowedOrigins; + + // Map of active replacement IDs to the titles of those notifications + HashMap<WebCore::String, WebCore::String> m_replacements; +}; + +#endif // NotificationPresenter_h diff --git a/WebKitTools/DumpRenderTree/chromium/TestShell.cpp b/WebKitTools/DumpRenderTree/chromium/TestShell.cpp index e4229fe..24a895a 100644 --- a/WebKitTools/DumpRenderTree/chromium/TestShell.cpp +++ b/WebKitTools/DumpRenderTree/chromium/TestShell.cpp @@ -85,6 +85,7 @@ TestShell::TestShell() m_eventSender.set(new EventSender(this)); m_plainTextController.set(new PlainTextController()); m_textInputController.set(new TextInputController(this)); + m_notificationPresenter.set(new NotificationPresenter(this)); m_webViewHost = createWebView(); m_webView = m_webViewHost->webView(); @@ -131,6 +132,7 @@ void TestShell::resetWebSettings(WebView& webView) settings->setSerifFontFamily(serif); settings->setStandardFontFamily(serif); settings->setFixedFontFamily(WebString::fromUTF8("Courier")); + settings->setSansSerifFontFamily(WebString::fromUTF8("Helvetica")); settings->setDefaultTextEncodingName(WebString::fromUTF8("ISO-8859-1")); settings->setDefaultFontSize(16); @@ -175,6 +177,7 @@ void TestShell::resetWebSettings(WebView& webView) void TestShell::runFileTest(const TestParams& params) { + ASSERT(params.testUrl.isValid()); m_testIsPreparing = true; m_params = params; string testUrl = m_params.testUrl.spec(); @@ -212,6 +215,7 @@ void TestShell::resetTestController() m_layoutTestController->reset(); m_eventSender->reset(); m_webViewHost->reset(); + m_notificationPresenter->reset(); } void TestShell::loadURL(const WebURL& url) diff --git a/WebKitTools/DumpRenderTree/chromium/TestShell.h b/WebKitTools/DumpRenderTree/chromium/TestShell.h index c6a5b2e..283cbd4 100644 --- a/WebKitTools/DumpRenderTree/chromium/TestShell.h +++ b/WebKitTools/DumpRenderTree/chromium/TestShell.h @@ -31,6 +31,7 @@ #include "AccessibilityController.h" #include "EventSender.h" #include "LayoutTestController.h" +#include "NotificationPresenter.h" #include "PlainTextController.h" #include "TextInputController.h" #include "WebViewHost.h" @@ -43,6 +44,7 @@ namespace WebKit { class WebFrame; +class WebNotificationPresenter; class WebPreferences; class WebView; class WebURL; @@ -74,7 +76,9 @@ public: // Returns the host for the main WebView. WebViewHost* webViewHost() const { return m_webViewHost; } LayoutTestController* layoutTestController() const { return m_layoutTestController.get(); } + EventSender* eventSender() const { return m_eventSender.get(); } AccessibilityController* accessibilityController() const { return m_accessibilityController.get(); } + NotificationPresenter* notificationPresenter() const { return m_notificationPresenter.get(); } void bindJSObjectsToWindow(WebKit::WebFrame*); void runFileTest(const TestParams&); @@ -115,6 +119,7 @@ public: void closeRemainingWindows(); int windowCount(); static void resizeWindowForTest(WebViewHost*, const WebKit::WebURL&); + void showDevTools() {} // FIXME: imeplement this. static const int virtualWindowBorder = 3; @@ -135,6 +140,7 @@ private: OwnPtr<LayoutTestController*> m_layoutTestController; OwnPtr<PlainTextController*> m_plainTextController; OwnPtr<TextInputController*> m_textInputController; + OwnPtr<NotificationPresenter*> m_notificationPresenter; TestParams m_params; // List of all windows in this process. diff --git a/WebKitTools/DumpRenderTree/chromium/TestWebWorker.h b/WebKitTools/DumpRenderTree/chromium/TestWebWorker.h index f28cc20..9470804 100644 --- a/WebKitTools/DumpRenderTree/chromium/TestWebWorker.h +++ b/WebKitTools/DumpRenderTree/chromium/TestWebWorker.h @@ -80,6 +80,7 @@ public: virtual WebKit::WebWorker* createWorker(WebKit::WebWorkerClient*) { return 0; } virtual WebKit::WebNotificationPresenter* notificationPresenter() { return 0; } virtual WebKit::WebApplicationCacheHost* createApplicationCacheHost(WebKit::WebApplicationCacheHostClient*) { return 0; } + virtual bool allowDatabase(WebKit::WebFrame*, const WebKit::WebString&, const WebKit::WebString&, unsigned long) { return true; } private: ~TestWebWorker() {} diff --git a/WebKitTools/DumpRenderTree/chromium/WebViewHost.cpp b/WebKitTools/DumpRenderTree/chromium/WebViewHost.cpp index 6ea1de1..4761b1a 100644 --- a/WebKitTools/DumpRenderTree/chromium/WebViewHost.cpp +++ b/WebKitTools/DumpRenderTree/chromium/WebViewHost.cpp @@ -52,10 +52,12 @@ #include "public/WebURLRequest.h" #include "public/WebURLResponse.h" #include "public/WebView.h" +#include "public/WebWindowFeatures.h" #include "skia/ext/platform_canvas.h" #include "webkit/support/webkit_support.h" #include <wtf/Assertions.h> +using namespace WebCore; using namespace WebKit; using namespace skia; using namespace std; @@ -250,14 +252,19 @@ static string textAffinityDescription(WebTextAffinity affinity) // WebViewClient ------------------------------------------------------------- -WebView* WebViewHost::createView(WebFrame*) +WebView* WebViewHost::createView(WebFrame* creator) +{ + return createView(creator, WebWindowFeatures()); +} + +WebView* WebViewHost::createView(WebFrame*, const WebWindowFeatures&) { if (!layoutTestController()->canOpenWindows()) return 0; return m_shell->createWebView()->webView(); } -WebWidget* WebViewHost::createPopupMenu(bool) +WebWidget* WebViewHost::createPopupMenu(WebPopupType) { return 0; } @@ -267,9 +274,9 @@ WebWidget* WebViewHost::createPopupMenu(const WebPopupMenuInfo&) return 0; } -WebStorageNamespace* WebViewHost::createSessionStorageNamespace() +WebStorageNamespace* WebViewHost::createSessionStorageNamespace(unsigned quota) { - return WebKit::WebStorageNamespace::createSessionStorageNamespace(); + return WebKit::WebStorageNamespace::createSessionStorageNamespace(quota); } void WebViewHost::didAddMessageToConsole(const WebConsoleMessage& message, const WebString& sourceName, unsigned sourceLine) @@ -481,7 +488,7 @@ void WebViewHost::setStatusText(const WebString& text) printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", text.utf8().data()); } -void WebViewHost::startDragging(const WebPoint& mouseCoords, const WebDragData& data, WebDragOperationsMask mask) +void WebViewHost::startDragging(const WebDragData& data, WebDragOperationsMask mask, const WebImage&, const WebPoint&) { WebDragData mutableDragData = data; if (layoutTestController()->shouldAddFileToPasteboard()) { @@ -491,7 +498,7 @@ void WebViewHost::startDragging(const WebPoint& mouseCoords, const WebDragData& // When running a test, we need to fake a drag drop operation otherwise // Windows waits for real mouse events to know when the drag is over. - EventSender::doDragDrop(mouseCoords, mutableDragData, mask); + m_shell->eventSender()->doDragDrop(mutableDragData, mask); } void WebViewHost::navigateBackForwardSoon(int offset) @@ -515,6 +522,11 @@ void WebViewHost::focusAccessibilityObject(const WebAccessibilityObject& object) m_shell->accessibilityController()->setFocusedElement(object); } +WebNotificationPresenter* WebViewHost::notificationPresenter() +{ + return m_shell->notificationPresenter(); +} + // WebWidgetClient ----------------------------------------------------------- void WebViewHost::didInvalidateRect(const WebRect& rect) @@ -629,6 +641,11 @@ WebMediaPlayer* WebViewHost::createMediaPlayer(WebFrame* frame, WebMediaPlayerCl return webkit_support::CreateMediaPlayer(frame, client); } +WebApplicationCacheHost* WebViewHost::createApplicationCacheHost(WebFrame* frame, WebApplicationCacheHostClient* client) +{ + return webkit_support::CreateApplicationCacheHost(frame, client); +} + bool WebViewHost::allowPlugins(WebFrame* frame, bool enabledPerSettings) { return enabledPerSettings; @@ -906,6 +923,10 @@ void WebViewHost::willSendRequest(WebFrame*, unsigned identifier, WebURLRequest& return; } + HashSet<String>::const_iterator end = m_clearHeaders.end(); + for (HashSet<String>::const_iterator header = m_clearHeaders.begin(); header != end; ++header) + request.clearHTTPHeaderField(WebString(header->characters(), header->length())); + // Set the new substituted URL. request.setURL(webkit_support::RewriteLayoutTestsURL(request.url().spec())); } @@ -1115,7 +1136,7 @@ void WebViewHost::updateAddressBar(WebView* webView) if (!dataSource) return; - setAddressBarURL(dataSource->request().firstPartyForCookies()); + setAddressBarURL(dataSource->request().url()); } void WebViewHost::locationChangeDone(WebFrame* frame) @@ -1168,6 +1189,7 @@ void WebViewHost::updateURL(WebFrame* frame) entry->setContentState(historyItem); navigationController()->didNavigateToEntry(entry.release()); + updateAddressBar(frame->view()); m_lastPageIdUpdated = max(m_lastPageIdUpdated, m_pageId); } diff --git a/WebKitTools/DumpRenderTree/chromium/WebViewHost.h b/WebKitTools/DumpRenderTree/chromium/WebViewHost.h index c5bed7a..5227b28 100644 --- a/WebKitTools/DumpRenderTree/chromium/WebViewHost.h +++ b/WebKitTools/DumpRenderTree/chromium/WebViewHost.h @@ -37,15 +37,18 @@ #include "public/WebFrameClient.h" #include "public/WebViewClient.h" #include <wtf/HashMap.h> +#include <wtf/HashSet.h> #include <wtf/Vector.h> +#include <wtf/text/WTFString.h> class LayoutTestController; class TestShell; namespace WebKit { class WebFrame; class WebURL; -struct WebURLError; struct WebRect; +struct WebURLError; +struct WebWindowFeatures; } namespace skia { class PlatformCanvas; @@ -79,14 +82,18 @@ class WebViewHost : public WebKit::WebViewClient, public WebKit::WebFrameClient, void loadURLForFrame(const WebKit::WebURL&, const WebKit::WebString& frameName); TestNavigationController* navigationController() { return m_navigationController.get(); } + void addClearHeader(const WebCore::String& header) { m_clearHeaders.add(header); } + const HashSet<WebCore::String>& clearHeaders() const { return m_clearHeaders; } + // NavigationHost virtual bool navigate(const TestNavigationEntry&, bool reload); // WebKit::WebViewClient virtual WebKit::WebView* createView(WebKit::WebFrame*); - virtual WebKit::WebWidget* createPopupMenu(bool activatable); + virtual WebKit::WebView* createView(WebKit::WebFrame*, const WebKit::WebWindowFeatures&); + virtual WebKit::WebWidget* createPopupMenu(WebKit::WebPopupType); virtual WebKit::WebWidget* createPopupMenu(const WebKit::WebPopupMenuInfo&); - virtual WebKit::WebStorageNamespace* createSessionStorageNamespace(); + virtual WebKit::WebStorageNamespace* createSessionStorageNamespace(unsigned quota); virtual void didAddMessageToConsole(const WebKit::WebConsoleMessage&, const WebKit::WebString& sourceName, unsigned sourceLine); virtual void didStartLoading(); virtual void didStopLoading(); @@ -112,11 +119,12 @@ class WebViewHost : public WebKit::WebViewClient, public WebKit::WebFrameClient, virtual bool runModalBeforeUnloadDialog(WebKit::WebFrame*, const WebKit::WebString&); virtual void showContextMenu(WebKit::WebFrame*, const WebKit::WebContextMenuData&); virtual void setStatusText(const WebKit::WebString&); - virtual void startDragging(const WebKit::WebPoint&, const WebKit::WebDragData&, WebKit::WebDragOperationsMask); + virtual void startDragging(const WebKit::WebDragData&, WebKit::WebDragOperationsMask, const WebKit::WebImage&, const WebKit::WebPoint&); virtual void navigateBackForwardSoon(int offset); virtual int historyBackListCount(); virtual int historyForwardListCount(); virtual void focusAccessibilityObject(const WebKit::WebAccessibilityObject&); + virtual WebKit::WebNotificationPresenter* notificationPresenter(); // WebKit::WebWidgetClient virtual void didInvalidateRect(const WebKit::WebRect&); @@ -137,7 +145,8 @@ class WebViewHost : public WebKit::WebViewClient, public WebKit::WebFrameClient, virtual WebKit::WebPlugin* createPlugin(WebKit::WebFrame*, const WebKit::WebPluginParams&); virtual WebKit::WebWorker* createWorker(WebKit::WebFrame*, WebKit::WebWorkerClient*); virtual WebKit::WebMediaPlayer* createMediaPlayer(WebKit::WebFrame*, WebKit::WebMediaPlayerClient*); - virtual bool allowPlugins(WebKit::WebFrame*, bool enabledPerSettings); + virtual WebKit::WebApplicationCacheHost* createApplicationCacheHost(WebKit::WebFrame*, WebKit::WebApplicationCacheHostClient*); + virtual bool allowPlugins(WebKit::WebFrame*, bool enabledPerSettings); virtual bool allowImages(WebKit::WebFrame*, bool enabledPerSettings); virtual void loadURLExternally(WebKit::WebFrame*, const WebKit::WebURLRequest&, WebKit::WebNavigationPolicy); virtual WebKit::WebNavigationPolicy decidePolicyForNavigation( @@ -248,6 +257,9 @@ private: // true if we want to enable selection of trailing whitespaces bool m_selectTrailingWhitespaceEnabled; + // Set of headers to clear in willSendRequest. + HashSet<WebCore::String> m_clearHeaders; + // true if we should block any redirects bool m_blocksRedirects; diff --git a/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp b/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp index fabada3..3735d0a 100644 --- a/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp +++ b/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "AccessibilityUIElement.h" +#include "GRefPtr.h" #include <JavaScriptCore/JSStringRef.h> #include <wtf/Assertions.h> @@ -128,10 +129,22 @@ unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element) return 0; } +gchar* attributeSetToString(AtkAttributeSet* attributeSet) +{ + GString* str = g_string_new(0); + for (GSList* attributes = attributeSet; attributes; attributes = attributes->next) { + AtkAttribute* attribute = static_cast<AtkAttribute*>(attributes->data); + g_string_append(str, g_strconcat(attribute->name, ":", attribute->value, NULL)); + if (attributes->next) + g_string_append(str, ", "); + } + + return g_string_free(str, FALSE); +} + JSStringRef AccessibilityUIElement::allAttributes() { - // FIXME: implement - return JSStringCreateWithCharacters(0, 0); + return JSStringCreateWithUTF8CString(attributeSetToString(atk_object_get_attributes(ATK_OBJECT(m_element)))); } JSStringRef AccessibilityUIElement::attributesOfLinkedUIElements() @@ -364,8 +377,13 @@ bool AccessibilityUIElement::isRequired() const bool AccessibilityUIElement::isSelected() const { - // FIXME: implement - return false; + if (!ATK_IS_OBJECT(m_element)) + return false; + + GRefPtr<AtkStateSet> stateSet = adoptGRef(atk_object_ref_state_set(ATK_OBJECT(m_element))); + gboolean isSelected = atk_state_set_contains_state(stateSet.get(), ATK_STATE_SELECTED); + + return isSelected; } int AccessibilityUIElement::hierarchicalLevel() const diff --git a/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp index 0f3ca38..273bcdf 100644 --- a/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp +++ b/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp @@ -843,6 +843,19 @@ static WebKitWebView* webInspectorInspectWebView(WebKitWebInspector*, gpointer d return WEBKIT_WEB_VIEW(webView); } +static void webViewLoadStatusNotified(WebKitWebView* view, gpointer user_data) +{ + WebKitLoadStatus loadStatus = webkit_web_view_get_load_status(view); + + if (gLayoutTestController->dumpFrameLoadCallbacks()) { + if (loadStatus == WEBKIT_LOAD_PROVISIONAL) { + char* frameName = getFrameNameSuitableForTestResult(view, mainFrame); + printf("%s - didStartProvisionalLoadForFrame\n", frameName); + g_free(frameName); + } + } +} + static WebKitWebView* createWebView() { WebKitWebView* view = WEBKIT_WEB_VIEW(webkit_web_view_new()); @@ -871,6 +884,10 @@ static WebKitWebView* createWebView() "signal::onload-event", webViewOnloadEvent, 0, NULL); + g_signal_connect(view, + "notify::load-status", G_CALLBACK(webViewLoadStatusNotified), + NULL); + WebKitWebInspector* inspector = webkit_web_view_get_inspector(view); g_object_connect(G_OBJECT(inspector), "signal::inspect-web-view", webInspectorInspectWebView, 0, diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm index 25b9f4f..6572a8e 100644 --- a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm @@ -397,6 +397,9 @@ static void resetDefaultsToConsistentValues() ThemeScrollBarArrowStyle style; GetThemeScrollBarArrowStyle(&style); // Force HIToolbox to read from CFPreferences #endif + + [defaults setBool:NO forKey:@"AppleScrollAnimationEnabled"]; + if (initialValue) CFPreferencesSetValue(CFSTR("AppleScrollBarVariant"), initialValue.get(), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); diff --git a/WebKitTools/DumpRenderTree/mac/EventSendingController.mm b/WebKitTools/DumpRenderTree/mac/EventSendingController.mm index 8c5cebf..b5b6f82 100644 --- a/WebKitTools/DumpRenderTree/mac/EventSendingController.mm +++ b/WebKitTools/DumpRenderTree/mac/EventSendingController.mm @@ -122,7 +122,7 @@ BOOL replayingSavedEvents; { if (aSelector == @selector(beginDragWithFiles:) || aSelector == @selector(clearKillRing) - || aSelector == @selector(contextClick) + || aSelector == @selector(contextClick:) || aSelector == @selector(enableDOMUIEventLogging:) || aSelector == @selector(fireKeyboardEventsToElement:) || aSelector == @selector(keyDown:withModifiers:withLocation:) @@ -152,6 +152,8 @@ BOOL replayingSavedEvents; { if (aSelector == @selector(beginDragWithFiles:)) return @"beginDragWithFiles"; + if (aSelector == @selector(contextClick:)) + return @"contextClick"; if (aSelector == @selector(enableDOMUIEventLogging:)) return @"enableDOMUIEventLogging"; if (aSelector == @selector(fireKeyboardEventsToElement:)) @@ -492,7 +494,7 @@ static int buildModifierFlags(const WebScriptObject* modifiers) [self mouseScrollByX:x andY:y continuously:NO]; } -- (void)contextClick +- (void)contextClick:(BOOL)shouldPrintMenuItems { [[[mainFrame frameView] documentView] layout]; [self updateClickCountForButton:RightMouseButton]; @@ -508,8 +510,21 @@ static int buildModifierFlags(const WebScriptObject* modifiers) pressure:0.0]; NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; - if (subView) - [subView menuForEvent:event]; + if (subView) { + NSMenu* menu = [subView menuForEvent:event]; + if (shouldPrintMenuItems) { + printf("ContextMenuItems: "); + for (int i = 0; i < [menu numberOfItems]; ++i) { + NSMenuItem* menuItem = [menu itemAtIndex:i]; + if (!strcmp("Inspect Element", [[menuItem title] UTF8String])) + continue; + if (i > 0) + printf(", "); + fputs([menuItem isSeparatorItem] ? "<separator>" : [[menuItem title] UTF8String], stdout); + } + printf("\n"); + } + } } - (void)scheduleAsynchronousClick diff --git a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm index 29a5812..44aea81 100644 --- a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm +++ b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm @@ -66,6 +66,10 @@ #import <wtf/HashMap.h> #import <wtf/RetainPtr.h> +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#include <Foundation/NSPrivateDecls.h> +#endif + @interface CommandValidationTarget : NSObject <NSValidatedUserInterfaceItem> { SEL _action; @@ -758,7 +762,12 @@ void LayoutTestController::setWebViewEditable(bool editable) #ifndef BUILDING_ON_TIGER static NSString *SynchronousLoaderRunLoopMode = @"DumpRenderTreeSynchronousLoaderRunLoopMode"; -@interface SynchronousLoader : NSObject +#if defined(BUILDING_ON_LEOPARD) || defined(BUILDING_ON_SNOW_LEOPARD) || !defined(__COCOA_FORMAL_PROTOCOLS_2__) +@protocol NSURLConnectionDelegate <NSObject> +@end +#endif + +@interface SynchronousLoader : NSObject <NSURLConnectionDelegate> { NSString *m_username; NSString *m_password; diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp index 9d2a065..f9caca6 100644 --- a/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp +++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp @@ -80,10 +80,6 @@ #include <qdebug.h> -extern void qt_dump_set_accepts_editing(bool b); -extern void qt_dump_frame_loader(bool b); -extern void qt_dump_resource_load_callbacks(bool b); - namespace WebCore { NetworkAccessManager::NetworkAccessManager(QObject* parent) @@ -192,6 +188,7 @@ void WebPage::resetSettings() settings()->resetAttribute(QWebSettings::LocalContentCanAccessRemoteUrls); settings()->resetAttribute(QWebSettings::PluginsEnabled); settings()->resetAttribute(QWebSettings::JavaScriptCanAccessClipboard); + settings()->resetAttribute(QWebSettings::AutoLoadImages); m_drt->layoutTestController()->setCaretBrowsingEnabled(false); m_drt->layoutTestController()->setFrameFlatteningEnabled(false); @@ -343,14 +340,11 @@ DumpRenderTree::DumpRenderTree() , m_stdin(0) , m_enableTextOutput(false) , m_singleFileMode(false) + , m_persistentStoragePath(QString(getenv("DUMPRENDERTREE_TEMP"))) { DumpRenderTreeSupportQt::overwritePluginDirectories(); - char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP"); - if (dumpRenderTreeTemp) - QWebSettings::enablePersistentStorage(QString(dumpRenderTreeTemp)); - else - QWebSettings::enablePersistentStorage(); + QWebSettings::enablePersistentStorage(m_persistentStoragePath); // create our primary testing page/view. m_mainView = new QWebView(0); @@ -480,6 +474,7 @@ static bool shouldEnableDeveloperExtras(const QUrl& url) void DumpRenderTree::open(const QUrl& url) { + DumpRenderTreeSupportQt::dumpResourceLoadCallbacksPath(QFileInfo(url.toString()).path()); resetToConsistentStateBeforeTesting(); if (shouldEnableDeveloperExtras(m_page->mainFrame()->url())) { @@ -512,7 +507,7 @@ void DumpRenderTree::open(const QUrl& url) initializeFonts(); #endif - qt_dump_frame_loader(url.toString().contains("loading/")); + DumpRenderTreeSupportQt::dumpFrameLoader(url.toString().contains("loading/")); setTextOutputEnabled(true); m_page->mainFrame()->load(url); } @@ -733,8 +728,8 @@ static const char *methodNameStringForFailedTest(LayoutTestController *controlle void DumpRenderTree::dump() { // Prevent any further frame load or resource load callbacks from appearing after we dump the result. - qt_dump_frame_loader(false); - qt_dump_resource_load_callbacks(false); + DumpRenderTreeSupportQt::dumpFrameLoader(false); + DumpRenderTreeSupportQt::dumpResourceLoadCallbacks(false); QWebFrame *mainFrame = m_page->mainFrame(); @@ -751,6 +746,7 @@ void DumpRenderTree::dump() resultString = mainFrame->renderTreeDump(); if (!resultString.isEmpty()) { + fprintf(stdout, "Content-Type: text/plain\n"); fprintf(stdout, "%s", resultString.toUtf8().constData()); if (m_controller->shouldDumpBackForwardList()) diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h index 8309492..e6449b7 100644 --- a/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h +++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h @@ -86,6 +86,7 @@ public: LayoutTestController *layoutTestController() const { return m_controller; } EventSender *eventSender() const { return m_eventSender; } TextInputController *textInputController() const { return m_textInputController; } + QString persistentStoragePath() const { return m_persistentStoragePath; } QWebPage *createWindow(); int windowCount() const; @@ -141,6 +142,7 @@ private: QList<QObject*> windows; bool m_enableTextOutput; bool m_singleFileMode; + QString m_persistentStoragePath; }; class NetworkAccessManager : public QNetworkAccessManager { diff --git a/WebKitTools/DumpRenderTree/qt/ImageDiff.cpp b/WebKitTools/DumpRenderTree/qt/ImageDiff.cpp index 704ca13..8ee68db 100644 --- a/WebKitTools/DumpRenderTree/qt/ImageDiff.cpp +++ b/WebKitTools/DumpRenderTree/qt/ImageDiff.cpp @@ -17,13 +17,14 @@ Boston, MA 02110-1301, USA. */ +#include <QtCore/qmath.h> + #include <QApplication> #include <QBuffer> #include <QByteArray> #include <QImage> #include <QStringList> -#include <math.h> #include <stdio.h> int main(int argc, char* argv[]) @@ -102,7 +103,7 @@ int main(int argc, char* argv[]) qreal green = (qGreen(pixel) - qGreen(basePixel)) / static_cast<float>(qMax(255 - qGreen(basePixel), qGreen(basePixel))); qreal blue = (qBlue(pixel) - qBlue(basePixel)) / static_cast<float>(qMax(255 - qBlue(basePixel), qBlue(basePixel))); qreal alpha = (qAlpha(pixel) - qAlpha(basePixel)) / static_cast<float>(qMax(255 - qAlpha(basePixel), qAlpha(basePixel))); - qreal distance = sqrt(red * red + green * green + blue * blue + alpha * alpha) / 2.0f; + qreal distance = qSqrt(red * red + green * green + blue * blue + alpha * alpha) / 2.0f; int gray = distance * qreal(255); diffImage.setPixel(x, y, qRgb(gray, gray, gray)); if (distance >= 1 / qreal(255)) { @@ -118,7 +119,7 @@ int main(int argc, char* argv[]) if (difference <= tolerance) { difference = 0; } else { - difference = round(difference * 100) / 100; + difference = qRound(difference * 100) / 100; difference = qMax(difference, qreal(0.01)); } diff --git a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp index 3f92b8a..1fadd61 100644 --- a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp +++ b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp @@ -37,22 +37,13 @@ #include <QLocale> #include <qwebsettings.h> -extern void qt_dump_editing_callbacks(bool b); -extern void qt_dump_frame_loader(bool b); -extern void qt_dump_resource_load_callbacks(bool b); -extern void qt_set_will_send_request_returns_null_on_redirect(bool b); -extern void qt_set_will_send_request_returns_null(bool b); -extern void qt_set_will_send_request_clear_headers(const QStringList& headers); - -extern void qt_dump_notification(bool b); - LayoutTestController::LayoutTestController(WebCore::DumpRenderTree* drt) : QObject() , m_drt(drt) { qRegisterMetaType<QWebElement>("QWebElement"); reset(); - qt_dump_notification(true); + DumpRenderTreeSupportQt::dumpNotification(true); } void LayoutTestController::reset() @@ -73,12 +64,13 @@ void LayoutTestController::reset() m_handleErrorPages = false; m_webHistory = 0; m_globalFlag = false; - qt_dump_editing_callbacks(false); - qt_dump_frame_loader(false); - qt_dump_resource_load_callbacks(false); - qt_set_will_send_request_returns_null_on_redirect(false); - qt_set_will_send_request_returns_null(false); - qt_set_will_send_request_clear_headers(QStringList()); + DumpRenderTreeSupportQt::dumpEditingCallbacks(false); + DumpRenderTreeSupportQt::dumpFrameLoader(false); + DumpRenderTreeSupportQt::dumpResourceLoadCallbacks(false); + DumpRenderTreeSupportQt::setWillSendRequestReturnsNullOnRedirect(false); + DumpRenderTreeSupportQt::setWillSendRequestReturnsNull(false); + DumpRenderTreeSupportQt::setWillSendRequestClearHeaders(QStringList()); + setIconDatabaseEnabled(false); emit hidePage(); } @@ -217,32 +209,32 @@ QString LayoutTestController::pathToLocalResource(const QString& url) void LayoutTestController::dumpEditingCallbacks() { qDebug() << ">>>dumpEditingCallbacks"; - qt_dump_editing_callbacks(true); + DumpRenderTreeSupportQt::dumpEditingCallbacks(true); } void LayoutTestController::dumpFrameLoadCallbacks() { - qt_dump_frame_loader(true); + DumpRenderTreeSupportQt::dumpFrameLoader(true); } void LayoutTestController::dumpResourceLoadCallbacks() { - qt_dump_resource_load_callbacks(true); + DumpRenderTreeSupportQt::dumpResourceLoadCallbacks(true); } void LayoutTestController::setWillSendRequestReturnsNullOnRedirect(bool enabled) { - qt_set_will_send_request_returns_null_on_redirect(enabled); + DumpRenderTreeSupportQt::setWillSendRequestReturnsNullOnRedirect(enabled); } void LayoutTestController::setWillSendRequestReturnsNull(bool enabled) { - qt_set_will_send_request_returns_null(enabled); + DumpRenderTreeSupportQt::setWillSendRequestReturnsNull(enabled); } void LayoutTestController::setWillSendRequestClearHeader(const QStringList& headers) { - qt_set_will_send_request_clear_headers(headers); + DumpRenderTreeSupportQt::setWillSendRequestClearHeaders(headers); } void LayoutTestController::queueBackNavigation(int howFarBackward) @@ -465,8 +457,7 @@ unsigned LayoutTestController::numberOfActiveAnimations() const void LayoutTestController::disableImageLoading() { - // FIXME: Implement for testing fix for https://bugs.webkit.org/show_bug.cgi?id=27896 - // Also need to make sure image loading is re-enabled for each new test. + m_drt->webPage()->settings()->setAttribute(QWebSettings::AutoLoadImages, false); } void LayoutTestController::dispatchPendingLoadRequests() @@ -624,11 +615,28 @@ QString LayoutTestController::markerTextForListItem(const QWebElement& listItem) return DumpRenderTreeSupportQt::markerTextForListItem(listItem); } +QVariantMap LayoutTestController::computedStyleIncludingVisitedInfo(const QWebElement& element) const +{ + return DumpRenderTreeSupportQt::computedStyleIncludingVisitedInfo(element); +} + +bool LayoutTestController::elementDoesAutoCompleteForElementWithId(const QString& elementId) +{ + return DumpRenderTreeSupportQt::elementDoesAutoCompleteForElementWithId(m_drt->webPage()->mainFrame(), elementId); +} + void LayoutTestController::authenticateSession(const QString&, const QString&, const QString&) { // FIXME: If there is a concept per-session (per-process) credential storage, the credentials should be added to it for later use. } +void LayoutTestController::setIconDatabaseEnabled(bool enable) +{ + if (enable && !m_drt->persistentStoragePath().isEmpty()) + QWebSettings::setIconDatabasePath(m_drt->persistentStoragePath()); + else + QWebSettings::setIconDatabasePath(QString()); +} const unsigned LayoutTestController::maxViewWidth = 800; const unsigned LayoutTestController::maxViewHeight = 600; diff --git a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h index 44b80b7..1359a6f 100644 --- a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h +++ b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h @@ -157,6 +157,7 @@ public slots: bool pauseAnimationAtTimeOnElementWithId(const QString& animationName, double time, const QString& elementId); bool pauseTransitionAtTimeOnElementWithId(const QString& propertyName, double time, const QString& elementId); bool sampleSVGAnimationForElementAtTime(const QString& animationId, double time, const QString& elementId); + bool elementDoesAutoCompleteForElementWithId(const QString& elementId); unsigned numberOfActiveAnimations() const; @@ -168,6 +169,7 @@ public slots: void setDatabaseQuota(int size); void clearAllDatabases(); + void setIconDatabaseEnabled(bool enable); void waitForPolicyDelegate(); void overridePreference(const QString& name, const QVariant& value); @@ -186,6 +188,7 @@ public slots: void setScrollbarPolicy(const QString& orientation, const QString& policy); QString markerTextForListItem(const QWebElement& listItem); + QVariantMap computedStyleIncludingVisitedInfo(const QWebElement& element) const; // Simulate a request an embedding application could make, populating per-session credential storage. void authenticateSession(const QString& url, const QString& username, const QString& password); diff --git a/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp index e96adae..dfa4de0 100644 --- a/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp +++ b/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp @@ -140,6 +140,17 @@ wstring urlSuitableForTestResult(const wstring& url) return PathFindFileNameW(url.c_str()); } +string toUTF8(BSTR bstr) +{ + int result = WideCharToMultiByte(CP_UTF8, 0, bstr, SysStringLen(bstr) + 1, 0, 0, 0, 0); + Vector<char> utf8Vector(result); + result = WideCharToMultiByte(CP_UTF8, 0, bstr, SysStringLen(bstr) + 1, utf8Vector.data(), result, 0, 0); + if (!result) + return string(); + + return string(utf8Vector.data(), utf8Vector.size() - 1); +} + static LRESULT CALLBACK DumpRenderTreeWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { diff --git a/WebKitTools/DumpRenderTree/win/DumpRenderTreeWin.h b/WebKitTools/DumpRenderTree/win/DumpRenderTreeWin.h index 499c57b..eb5d312 100644 --- a/WebKitTools/DumpRenderTree/win/DumpRenderTreeWin.h +++ b/WebKitTools/DumpRenderTree/win/DumpRenderTreeWin.h @@ -49,6 +49,7 @@ extern HWND webViewWindow; #include <wtf/Vector.h> std::wstring urlSuitableForTestResult(const std::wstring& url); +std::string toUTF8(BSTR); IWebView* createWebViewAndOffscreenWindow(HWND* webViewWindow = 0); Vector<HWND>& openWindows(); typedef HashMap<HWND, COMPtr<IWebView> > WindowToWebViewMap; diff --git a/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp b/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp index cf4d221..9b9e3c1 100644 --- a/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp +++ b/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp @@ -48,17 +48,6 @@ using std::string; static FrameLoadDelegate* g_delegateWaitingOnTimer; -string BSTRtoString(BSTR bstr) -{ - int result = WideCharToMultiByte(CP_UTF8, 0, bstr, SysStringLen(bstr) + 1, 0, 0, 0, 0); - Vector<char> utf8Vector(result); - result = WideCharToMultiByte(CP_UTF8, 0, bstr, SysStringLen(bstr) + 1, utf8Vector.data(), result, 0, 0); - if (!result) - return string(); - - return string(utf8Vector.data(), utf8Vector.size() - 1); -} - string descriptionSuitableForTestResult(IWebFrame* webFrame) { COMPtr<IWebView> webView; @@ -70,11 +59,11 @@ string descriptionSuitableForTestResult(IWebFrame* webFrame) return string(); BSTR frameNameBSTR; - if (FAILED(webFrame->name(&frameNameBSTR)) || BSTRtoString(frameNameBSTR).empty() ) + if (FAILED(webFrame->name(&frameNameBSTR)) || toUTF8(frameNameBSTR).empty()) return (webFrame == mainFrame) ? "main frame" : string(); string frameName = (webFrame == mainFrame) ? "main frame" : "frame"; - frameName += " \"" + BSTRtoString(frameNameBSTR) + "\""; + frameName += " \"" + toUTF8(frameNameBSTR) + "\""; SysFreeString(frameNameBSTR); return frameName; diff --git a/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp b/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp index 89873b6..321d9cf 100644 --- a/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp +++ b/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp @@ -106,7 +106,9 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc pluginLog(instance, "src: %s", argv[i]); } else if (_stricmp(argn[i], "testdocumentopenindestroystream") == 0) obj->testDocumentOpenInDestroyStream = TRUE; - else if (_stricmp(argn[i], "testwindowopen") == 0) + else if (_stricmp(argn[i], "testGetURLOnDestroy") == 0) + obj->testGetURLOnDestroy = TRUE; + else if (_stricmp(argn[i], "testwindowopen") == 0) obj->testWindowOpen = TRUE; else if (_stricmp(argn[i], "onSetWindow") == 0 && !obj->onSetWindow) obj->onSetWindow = strdup(argv[i]); @@ -122,6 +124,9 @@ NPError NPP_Destroy(NPP instance, NPSavedData **save) { PluginObject *obj = (PluginObject*)instance->pdata; if (obj) { + if (obj->testGetURLOnDestroy) + browser->geturlnotify(obj->npp, "about:blank", "", ""); + if (obj->onStreamLoad) free(obj->onStreamLoad); @@ -137,7 +142,7 @@ NPError NPP_Destroy(NPP instance, NPSavedData **save) } if (obj->logDestroy) - printf("PLUGIN: NPP_Destroy\n"); + pluginLog(instance, "NPP_Destroy\n"); if (obj->onSetWindow) free(obj->onSetWindow); diff --git a/WebKitTools/DumpRenderTree/win/UIDelegate.cpp b/WebKitTools/DumpRenderTree/win/UIDelegate.cpp index 16724d7..6637068 100755 --- a/WebKitTools/DumpRenderTree/win/UIDelegate.cpp +++ b/WebKitTools/DumpRenderTree/win/UIDelegate.cpp @@ -625,7 +625,7 @@ HRESULT STDMETHODCALLTYPE UIDelegate::webViewDidInvalidate( HRESULT STDMETHODCALLTYPE UIDelegate::setStatusText(IWebView*, BSTR text) { if (gLayoutTestController->dumpStatusCallbacks()) - printf("UI DELEGATE STATUS CALLBACK: setStatusText:%S\n", text ? text : L""); + printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", text ? toUTF8(text).c_str() : ""); return S_OK; } diff --git a/WebKitTools/QtLauncher/QtLauncher.pro b/WebKitTools/QtTestBrowser/QtTestBrowser.pro index a91cee9..4c4d83f 100644 --- a/WebKitTools/QtLauncher/QtLauncher.pro +++ b/WebKitTools/QtTestBrowser/QtTestBrowser.pro @@ -55,4 +55,4 @@ contains(QT_CONFIG, opengl) { } RESOURCES += \ - QtLauncher.qrc + QtTestBrowser.qrc diff --git a/WebKitTools/QtLauncher/QtLauncher.qrc b/WebKitTools/QtTestBrowser/QtTestBrowser.qrc index ffe77b0..ffe77b0 100644 --- a/WebKitTools/QtLauncher/QtLauncher.qrc +++ b/WebKitTools/QtTestBrowser/QtTestBrowser.qrc diff --git a/WebKitTools/QtLauncher/fpstimer.cpp b/WebKitTools/QtTestBrowser/fpstimer.cpp index 3b72cef..3b72cef 100644 --- a/WebKitTools/QtLauncher/fpstimer.cpp +++ b/WebKitTools/QtTestBrowser/fpstimer.cpp diff --git a/WebKitTools/QtLauncher/fpstimer.h b/WebKitTools/QtTestBrowser/fpstimer.h index 66d38eb..66d38eb 100644 --- a/WebKitTools/QtLauncher/fpstimer.h +++ b/WebKitTools/QtTestBrowser/fpstimer.h diff --git a/WebKitTools/QtLauncher/locationedit.cpp b/WebKitTools/QtTestBrowser/locationedit.cpp index a97f148..a97f148 100644 --- a/WebKitTools/QtLauncher/locationedit.cpp +++ b/WebKitTools/QtTestBrowser/locationedit.cpp diff --git a/WebKitTools/QtLauncher/locationedit.h b/WebKitTools/QtTestBrowser/locationedit.h index e82920c..e82920c 100644 --- a/WebKitTools/QtLauncher/locationedit.h +++ b/WebKitTools/QtTestBrowser/locationedit.h diff --git a/WebKitTools/QtLauncher/main.cpp b/WebKitTools/QtTestBrowser/main.cpp index 748936b..49c08d9 100644 --- a/WebKitTools/QtLauncher/main.cpp +++ b/WebKitTools/QtTestBrowser/main.cpp @@ -734,13 +734,15 @@ void LauncherWindow::showUserAgentDialog() items << customUserAgent; QDialog* dialog = new QDialog(this); + dialog->resize(size().width() * 0.7, dialog->size().height()); + dialog->setMaximumHeight(dialog->size().height()); dialog->setWindowTitle("Change User Agent"); QVBoxLayout* layout = new QVBoxLayout(dialog); dialog->setLayout(layout); QComboBox* combo = new QComboBox(dialog); - combo->setMaximumWidth(size().width() * 0.7); + combo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); combo->setEditable(true); combo->insertItems(0, items); layout->addWidget(combo); diff --git a/WebKitTools/QtLauncher/mainwindow.cpp b/WebKitTools/QtTestBrowser/mainwindow.cpp index aa6aa26..aa6aa26 100644 --- a/WebKitTools/QtLauncher/mainwindow.cpp +++ b/WebKitTools/QtTestBrowser/mainwindow.cpp diff --git a/WebKitTools/QtLauncher/mainwindow.h b/WebKitTools/QtTestBrowser/mainwindow.h index d753a46..d753a46 100644 --- a/WebKitTools/QtLauncher/mainwindow.h +++ b/WebKitTools/QtTestBrowser/mainwindow.h diff --git a/WebKitTools/QtLauncher/urlloader.cpp b/WebKitTools/QtTestBrowser/urlloader.cpp index 630ead6..630ead6 100644 --- a/WebKitTools/QtLauncher/urlloader.cpp +++ b/WebKitTools/QtTestBrowser/urlloader.cpp diff --git a/WebKitTools/QtLauncher/urlloader.h b/WebKitTools/QtTestBrowser/urlloader.h index ed14adc..ed14adc 100644 --- a/WebKitTools/QtLauncher/urlloader.h +++ b/WebKitTools/QtTestBrowser/urlloader.h diff --git a/WebKitTools/QtLauncher/useragentlist.txt b/WebKitTools/QtTestBrowser/useragentlist.txt index ca53693..ca53693 100644 --- a/WebKitTools/QtLauncher/useragentlist.txt +++ b/WebKitTools/QtTestBrowser/useragentlist.txt diff --git a/WebKitTools/QtLauncher/utils.cpp b/WebKitTools/QtTestBrowser/utils.cpp index 2d45dd0..2d45dd0 100644 --- a/WebKitTools/QtLauncher/utils.cpp +++ b/WebKitTools/QtTestBrowser/utils.cpp diff --git a/WebKitTools/QtLauncher/utils.h b/WebKitTools/QtTestBrowser/utils.h index b67351e..b67351e 100644 --- a/WebKitTools/QtLauncher/utils.h +++ b/WebKitTools/QtTestBrowser/utils.h diff --git a/WebKitTools/QtLauncher/webinspector.h b/WebKitTools/QtTestBrowser/webinspector.h index d251c5c..d251c5c 100644 --- a/WebKitTools/QtLauncher/webinspector.h +++ b/WebKitTools/QtTestBrowser/webinspector.h diff --git a/WebKitTools/QtLauncher/webpage.cpp b/WebKitTools/QtTestBrowser/webpage.cpp index 624a66f..624a66f 100644 --- a/WebKitTools/QtLauncher/webpage.cpp +++ b/WebKitTools/QtTestBrowser/webpage.cpp diff --git a/WebKitTools/QtLauncher/webpage.h b/WebKitTools/QtTestBrowser/webpage.h index 061deb5..061deb5 100644 --- a/WebKitTools/QtLauncher/webpage.h +++ b/WebKitTools/QtTestBrowser/webpage.h diff --git a/WebKitTools/QtLauncher/webview.cpp b/WebKitTools/QtTestBrowser/webview.cpp index 1467cf8..1467cf8 100644 --- a/WebKitTools/QtLauncher/webview.cpp +++ b/WebKitTools/QtTestBrowser/webview.cpp diff --git a/WebKitTools/QtLauncher/webview.h b/WebKitTools/QtTestBrowser/webview.h index 30161bb..30161bb 100644 --- a/WebKitTools/QtLauncher/webview.h +++ b/WebKitTools/QtTestBrowser/webview.h diff --git a/WebKitTools/QueueStatusServer/handlers/dashboard.py b/WebKitTools/QueueStatusServer/handlers/dashboard.py index 80f30ec..bbb65b8 100644 --- a/WebKitTools/QueueStatusServer/handlers/dashboard.py +++ b/WebKitTools/QueueStatusServer/handlers/dashboard.py @@ -26,16 +26,55 @@ # (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 operator + from google.appengine.ext import webapp from google.appengine.ext.webapp import template from model.attachment import Attachment +from model.queues import queues + class Dashboard(webapp.RequestHandler): - def get(self): - attachments = Attachment.recent(limit=25) + # FIXME: This list probably belongs as part of a Queue object in queues.py + # Arrays are bubble_name, queue_name + # FIXME: Can this be unified with StatusBubble._queues_to_display? + _queues_to_display = [ + ["Style", "style-queue"], + ["Cr-Linux", "chromium-ews"], + ["Cr-Win", "cr-win-ews"], + ["Qt", "qt-ews"], + ["Gtk", "gtk-ews"], + ["Mac", "mac-ews"], + ["Win", "win-ews"], + ["Commit", "commit-queue"], + ] + # Split the zipped list into component parts + _header_names, _ordered_queue_names = zip(*_queues_to_display) + + # This asserts that all of the queues listed above are valid queue names. + assert(reduce(operator.and_, map(lambda name: name in queues, _ordered_queue_names))) + + def _build_bubble(self, attachment, queue_name): + queue_status = attachment.status_for_queue(queue_name) + bubble = { + "status_class": attachment.state_from_queue_status(queue_status) if queue_status else "none", + "status_date": queue_status.date if queue_status else None, + } + return bubble + + def _build_row(self, attachment): + row = { + "bug_id": attachment.bug_id(), + "attachment_id": attachment.id, + "bubbles": [self._build_bubble(attachment, queue_name) for queue_name in self._ordered_queue_names], + } + return row + + def get(self): template_values = { - "summaries" : [attachment.summary() for attachment in attachments], + "headers": self._header_names, + "rows": [self._build_row(attachment) for attachment in Attachment.recent(limit=25)], } self.response.out.write(template.render("templates/dashboard.html", template_values)) diff --git a/WebKitTools/QueueStatusServer/handlers/statusbubble.py b/WebKitTools/QueueStatusServer/handlers/statusbubble.py index d52509f..0e2b8de 100644 --- a/WebKitTools/QueueStatusServer/handlers/statusbubble.py +++ b/WebKitTools/QueueStatusServer/handlers/statusbubble.py @@ -26,17 +26,50 @@ # (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 operator + from google.appengine.ext import webapp from google.appengine.ext.webapp import template from model.attachment import Attachment +from model.workitems import WorkItems +from model.queues import queues, name_with_underscores class StatusBubble(webapp.RequestHandler): + # FIXME: This list probably belongs as part of a Queue object in queues.py + # Arrays are bubble_name, queue_name + _queues_to_display = [ + ["style", "style-queue"], + ["cr-linux", "chromium-ews"], + ["cr-win", "cr-win-ews"], + ["gtk", "gtk-ews"], + ["qt", "qt-ews"], + ["mac", "mac-ews"], + ["win", "win-ews"], + ] + + # This asserts that all of the queues listed above are valid queue names. + assert(reduce(operator.and_, map(lambda name_pair: name_pair[1] in queues, _queues_to_display))) + + def _build_bubble(self, queue_name_pair, attachment): + bubble_name = queue_name_pair[0] + queue_name = queue_name_pair[1] + + queue_status = attachment.status_for_queue(queue_name) + bubble = { + "name": bubble_name, + "attachment_id": attachment.id, + "queue_position": attachment.position_in_queue(queue_name), + "state": attachment.state_from_queue_status(queue_status) if queue_status else "none", + "status": queue_status, + } + return bubble + def get(self, attachment_id): attachment = Attachment(int(attachment_id)) - + bubbles = [self._build_bubble(name_pair, attachment) for name_pair in self._queues_to_display] template_values = { - "summary" : attachment.summary() + "bubbles": bubbles, } self.response.out.write(template.render("templates/statusbubble.html", template_values)) diff --git a/WebKitTools/QueueStatusServer/handlers/updatestatus.py b/WebKitTools/QueueStatusServer/handlers/updatestatus.py index 50d4b6e..5a93dbd 100644 --- a/WebKitTools/QueueStatusServer/handlers/updatestatus.py +++ b/WebKitTools/QueueStatusServer/handlers/updatestatus.py @@ -38,9 +38,10 @@ class UpdateStatus(UpdateBase): def get(self): self.response.out.write(template.render("templates/updatestatus.html", None)) - def post(self): + def _queue_status_from_request(self): queue_status = QueueStatus() + # FIXME: I think this can be removed, no one uses it. if users.get_current_user(): queue_status.author = users.get_current_user() @@ -53,6 +54,10 @@ class UpdateStatus(UpdateBase): queue_status.message = self.request.get("status") results_file = self.request.get("results_file") queue_status.results_file = db.Blob(str(results_file)) + return queue_status + + def post(self): + queue_status = self._queue_status_from_request() queue_status.put() - Attachment.dirty(patch_id) + Attachment.dirty(queue_status.active_patch_id) self.response.out.write(queue_status.key().id()) diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_wsh.py b/WebKitTools/QueueStatusServer/handlers/updateworkitems.py index 50cad31..b58e743 100644 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/example/echo_wsh.py +++ b/WebKitTools/QueueStatusServer/handlers/updateworkitems.py @@ -1,5 +1,4 @@ -# 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 @@ -27,23 +26,39 @@ # (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 google.appengine.ext import webapp, db +from google.appengine.ext.webapp import template -from mod_pywebsocket import msgutil +from handlers.updatebase import UpdateBase +from model.queues import queues +from model.workitems import WorkItems +from datetime import datetime -_GOODBYE_MESSAGE = 'Goodbye' +class UpdateWorkItems(UpdateBase): + def get(self): + self.response.out.write(template.render("templates/updateworkitems.html", None)) -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: + def _work_items_for_queue(self, queue_name): + if queue_name not in queues: + self.response.set_status(500) return + work_items = WorkItems.all().filter("queue_name =", queue_name).get() + if not work_items: + work_items = WorkItems() + work_items.queue_name = queue_name + return work_items + def _work_items_from_request(self): + queue_name = self.request.get("queue_name") + work_items = self._work_items_for_queue(queue_name) + items_string = self.request.get("work_items") + # Our parsing could be much more robust. + work_items.item_ids = map(int, items_string.split(" ")) + work_items.date = datetime.now() + return work_items -# vi:sts=4 sw=4 et + def post(self): + work_items = self._work_items_from_request() + work_items.put() diff --git a/WebKitTools/QueueStatusServer/main.py b/WebKitTools/QueueStatusServer/main.py index 8bfa7e9..fb6fc4b 100644 --- a/WebKitTools/QueueStatusServer/main.py +++ b/WebKitTools/QueueStatusServer/main.py @@ -43,6 +43,8 @@ from handlers.statusbubble import StatusBubble from handlers.svnrevision import SVNRevision from handlers.updatestatus import UpdateStatus from handlers.updatesvnrevision import UpdateSVNRevision +from handlers.updateworkitems import UpdateWorkItems + webapp.template.register_template_library('filters.webkit_extras') @@ -57,6 +59,7 @@ routes = [ (r'/svn-revision/(.*)', SVNRevision), (r'/queue-status/(.*)', RecentStatus), ('/update-status', UpdateStatus), + ('/update-work-items', UpdateWorkItems), ('/update-svn-revision', UpdateSVNRevision), ] diff --git a/WebKitTools/QueueStatusServer/model/attachment.py b/WebKitTools/QueueStatusServer/model/attachment.py index 751f78e..9ae59e8 100644 --- a/WebKitTools/QueueStatusServer/model/attachment.py +++ b/WebKitTools/QueueStatusServer/model/attachment.py @@ -30,8 +30,9 @@ import re from google.appengine.api import memcache -from model.queues import queues +from model.queues import queues, name_with_underscores from model.queuestatus import QueueStatus +from model.workitems import WorkItems class Attachment(object): @@ -60,6 +61,7 @@ class Attachment(object): def __init__(self, attachment_id): self.id = attachment_id self._summary = None + self._cached_queue_positions = None def summary(self): if self._summary: @@ -71,11 +73,7 @@ class Attachment(object): memcache.set(str(self.id), self._summary, namespace="attachment-summary") return self._summary - def _dash_to_underscore(self, dashed_name): - regexp = re.compile("-") - return regexp.sub("_", dashed_name) - - def _state_from_status(self, status): + def state_from_queue_status(self, status): table = { "Pass" : "pass", "Fail" : "fail", @@ -89,6 +87,40 @@ class Attachment(object): return "pending" return None + def position_in_queue(self, queue_name): + return self._queue_positions().get(queue_name) + + def status_for_queue(self, queue_name): + underscore_queue_name = name_with_underscores(queue_name) + # summary() is a horrible API and should be killed. + queue_summary = self.summary().get(underscore_queue_name) + if not queue_summary: + return None + return queue_summary.get("status") + + def bug_id(self): + return self.summary().get("bug_id") + + def _queue_positions(self): + if self._cached_queue_positions: + return self._cached_queue_positions + # FIXME: Should we be mem-caching this? + self._cached_queue_positions = self._calculate_queue_positions() + return self._cached_queue_positions + + def _calculate_queue_positions(self): + queue_positions = {} + for work_items in WorkItems.all().fetch(limit=len(queues)): + queue_name = str(work_items.queue_name) + try: + position = work_items.item_ids.index(self.id) + # Display 1-based indecies to the user. + queue_positions[queue_name] = position + 1 + except ValueError, e: + queue_positions[queue_name] = None + return queue_positions + + # FIXME: This is controller/view code and does not belong in a model. def _fetch_summary(self): summary = { "attachment_id" : self.id } @@ -102,8 +134,8 @@ class Attachment(object): summary[queue] = None status = QueueStatus.all().filter('queue_name =', queue).filter('active_patch_id =', self.id).order('-date').get() if status: - summary[self._dash_to_underscore(queue)] = { - "state" : self._state_from_status(status), - "status" : status, + summary[name_with_underscores(queue)] = { + "state": self.state_from_queue_status(status), + "status": status, } return summary diff --git a/WebKitTools/QueueStatusServer/model/queues.py b/WebKitTools/QueueStatusServer/model/queues.py index 57463de..46f2f15 100644 --- a/WebKitTools/QueueStatusServer/model/queues.py +++ b/WebKitTools/QueueStatusServer/model/queues.py @@ -1,4 +1,4 @@ -# Copyright (C) 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 @@ -26,6 +26,9 @@ # (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 re + + queues = [ "commit-queue", "style-queue", @@ -36,3 +39,8 @@ queues = [ "mac-ews", "win-ews", ] + + +def name_with_underscores(dashed_name): + regexp = re.compile("-") + return regexp.sub("_", dashed_name) diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py b/WebKitTools/QueueStatusServer/model/workitems.py index 26352eb..3ea59cb 100644 --- a/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/test/testdata/handlers/sub/non_callable_wsh.py +++ b/WebKitTools/QueueStatusServer/model/workitems.py @@ -1,5 +1,4 @@ -# 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 @@ -27,13 +26,10 @@ # (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 google.appengine.ext import db -"""Non-callable handlers. -""" - -web_socket_do_extra_handshake = True -web_socket_transfer_data = 1 - - -# vi:sts=4 sw=4 et +class WorkItems(db.Model): + queue_name = db.StringProperty() + item_ids = db.ListProperty(int) + date = db.DateTimeProperty(auto_now_add=True) diff --git a/WebKitTools/QueueStatusServer/templates/dashboard.html b/WebKitTools/QueueStatusServer/templates/dashboard.html index 14b7ede..c5c2359 100644 --- a/WebKitTools/QueueStatusServer/templates/dashboard.html +++ b/WebKitTools/QueueStatusServer/templates/dashboard.html @@ -17,57 +17,27 @@ function statusDetail(patch_id) { <tr> <th>Bug</th> <th>Attachment</th> - <th>Style</th> - <th>Cr-Linux</th> - <th>Cr-Win</th> - <th>Qt</th> - <th>Mac</th> - <th>Win</th> - <th>Gtk</th> - <th>Commit</th> + {% for header in headers %} + <th>{{ header }}</th> + {% endfor %} </tr> </thead> - <tbody>{% for summary in summaries %} + <tbody>{% for row in rows %} <tr> <td class="status"> - {{ summary.bug_id|force_escape|webkit_bug_id|safe }} + {{ row.bug_id|force_escape|webkit_bug_id|safe }} </td> <td class="status"> - {{ summary.attachment_id|force_escape|webkit_attachment_id|safe }} + {{ row.attachment_id|force_escape|webkit_attachment_id|safe }} </td> - <!-- FIXME: Find some way to remove this copy-and-paste code! --> - <td class="status {{ summary.style_queue.state }}"{% if summary.style_queue.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.style_queue.status.date|timesince }}"{% endif %}> - </td> - <td class="status {{ summary.chromium_ews.state }}"{% if summary.chromium_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.chromium_ews.status.date|timesince }} ago"{% endif %}> - </td> - <td class="status {{ summary.cr_win_ews.state }}"{% if summary.cr_win_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.cr_win_ews.status.date|timesince }} ago"{% endif %}> - </td> - <td class="status {{ summary.qt_ews.state }}"{% if summary.qt_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.qt_ews.status.date|timesince }} ago"{% endif %}> - </td> - <td class="status {{ summary.mac_ews.state }}"{% if summary.mac_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.mac_ews.status.date|timesince }} ago"{% endif %}> - </td> - <td class="status {{ summary.win_ews.state }}"{% if summary.win_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.win_ews.status.date|timesince }} ago"{% endif %}> - </td> - <td class="status {{ summary.gtk_ews.state }}"{% if summary.gtk_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.gtk_ews.status.date|timesince }} ago"{% endif %}> - </td> - <td class="status {{ summary.commit_queue.state }}"{% if summary.commit_queue.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.commit_queue.status.date|timesince }} ago"{% endif %}> + {% for bubble in row.bubbles %} + <td class="status {{ bubble.status_class }}" + {% if bubble.status %} + onclick="statusDetail({{ row.attachment_id }})" + title="{{ bubble.status_date|timesince }}" + {% endif %}> </td> + {% endfor %} </tr>{% endfor %} </tbody> </table> diff --git a/WebKitTools/QueueStatusServer/templates/statusbubble.html b/WebKitTools/QueueStatusServer/templates/statusbubble.html index c6e2134..3102741 100644 --- a/WebKitTools/QueueStatusServer/templates/statusbubble.html +++ b/WebKitTools/QueueStatusServer/templates/statusbubble.html @@ -18,26 +18,26 @@ body { border: 1px solid #AAA; background-color: white; font-size: 11px; + cursor: pointer; +} +.none { + cursor: auto; } .pass { background-color: #8FDF5F; border: 1px solid #4F8530; - cursor: pointer; } .fail { background-color: #E98080; border: 1px solid #A77272; - cursor: pointer; } .pending { background-color: #FFFC6C; border: 1px solid #C5C56D; - cursor: pointer; } .error { background-color: #E0B0FF; border: 1px solid #ACA0B3; - cursor: pointer; } </style> <script> @@ -47,41 +47,15 @@ function statusDetail(patch_id) { </script> </head> <body> -<!-- FIXME: Find some way to remove this copy-and-paste code! --> -<div class="status {{ summary.style_queue.state }}"{% if summary.style_queue.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.style_queue.status.date|timesince }} ago"{% endif %}> - style -</div> -<div class="status {{ summary.chromium_ews.state }}"{% if summary.chromium_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.chromium_ews.status.date|timesince }} ago"{% endif %}> - cr-linux -</div> -<div class="status {{ summary.cr_win_ews.state }}"{% if summary.cr_win_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.cr_win_ews.status.date|timesince }} ago"{% endif %}> - cr-win -</div> -<div class="status {{ summary.qt_ews.state }}"{% if summary.qt_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.qt_ews.status.date|timesince }} ago"{% endif %}> - qt -</div> -<div class="status {{ summary.gtk_ews.state }}"{% if summary.gtk_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.gtk_ews.status.date|timesince }} ago"{% endif %}> - gtk -</div> -<div class="status {{ summary.mac_ews.state }}"{% if summary.mac_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.mac_ews.status.date|timesince }} ago"{% endif %}> - mac -</div> -<div class="status {{ summary.win_ews.state }}"{% if summary.win_ews.status %} - onclick="statusDetail({{ summary.attachment_id }})" - title="{{ summary.win_ews.status.date|timesince }} ago"{% endif %}> - win +{% for bubble in bubbles %} +<div class="status {{ bubble.state }}"{% if bubble.status %} + onclick="statusDetail({{ bubble.attachment_id }})" + title="{{ bubble.status.date|timesince }} ago"{% endif %}> + {{ bubble.name }} + {% if bubble.queue_position %} + (#{{ bubble.queue_position }}) + {% endif %} </div> +{% endfor %} </body> </html> diff --git a/WebKitTools/QueueStatusServer/templates/updateworkitems.html b/WebKitTools/QueueStatusServer/templates/updateworkitems.html new file mode 100644 index 0000000..b086fc3 --- /dev/null +++ b/WebKitTools/QueueStatusServer/templates/updateworkitems.html @@ -0,0 +1,8 @@ +<form name="update_work_items" enctype="multipart/form-data" method="post"> +Update work items for a queue: <input name="queue_name"> + <div> + Work Items: + <input name="work_items"> + </div> + <div><input type="submit" value="Update Work Items"></div> +</form> diff --git a/WebKitTools/Scripts/VCSUtils.pm b/WebKitTools/Scripts/VCSUtils.pm index 777fe7f..41bbf40 100644 --- a/WebKitTools/Scripts/VCSUtils.pm +++ b/WebKitTools/Scripts/VCSUtils.pm @@ -43,6 +43,7 @@ BEGIN { $VERSION = 1.00; @ISA = qw(Exporter); @EXPORT = qw( + &callSilently &canonicalizePath &changeLogEmailAddress &changeLogName @@ -67,6 +68,7 @@ BEGIN { &pathRelativeToSVNRepositoryRootForPath &prepareParsedPatch &runPatchCommand + &setChangeLogDateAndReviewer &svnRevisionForDirectory &svnStatus ); @@ -83,6 +85,14 @@ my $isGitBranchBuild; my $isSVN; my $svnVersion; +# Project time zone for Cupertino, CA, US +my $changeLogTimeZone = "PST8PDT"; + +my $gitDiffStartRegEx = qr#^diff --git (\w/)?(.+) (\w/)?([^\r\n]+)#; +my $svnDiffStartRegEx = qr#^Index: ([^\r\n]+)#; +my $svnPropertyStartRegEx = qr#^(Modified|Name|Added|Deleted): ([^\r\n]+)#; # $2 is the name of the property. +my $svnPropertyValueStartRegEx = qr#^ (\+|-) ([^\r\n]+)#; # $2 is the start of the property's value (which may span multiple lines). + # This method is for portability. Return the system-appropriate exit # status of a child process. # @@ -97,6 +107,25 @@ sub exitStatus($) return WEXITSTATUS($returnvalue); } +# Call a function while suppressing STDERR, and return the return values +# as an array. +sub callSilently($@) { + my ($func, @args) = @_; + + # The following pattern was taken from here: + # http://www.sdsc.edu/~moreland/courses/IntroPerl/docs/manual/pod/perlfunc/open.html + # + # Also see this Perl documentation (search for "open OLDERR"): + # http://perldoc.perl.org/functions/open.html + open(OLDERR, ">&STDERR"); + close(STDERR); + my @returnValue = &$func(@args); + open(STDERR, ">&OLDERR"); + close(OLDERR); + + return @returnValue; +} + # Note, this method will not error if the file corresponding to the path does not exist. sub scmToggleExecutableBit { @@ -425,14 +454,23 @@ sub isExecutable($) # # Returns ($headerHashRef, $lastReadLine): # $headerHashRef: a hash reference representing a diff header, as follows-- -# copiedFromPath: the path from which the file was copied if the diff -# is a copy. +# copiedFromPath: the path from which the file was copied or moved if +# the diff is a copy or move. # executableBitDelta: the value 1 or -1 if the executable bit was added or # removed, respectively. New and deleted files have # this value only if the file is executable, in which # case the value is 1 and -1, respectively. # indexPath: the path of the target file. # isBinary: the value 1 if the diff is for a binary file. +# isDeletion: the value 1 if the diff is a file deletion. +# isCopyWithChanges: the value 1 if the file was copied or moved and +# the target file was changed in some way after being +# copied or moved (e.g. if its contents or executable +# bit were changed). +# isNew: the value 1 if the diff is for a new file. +# shouldDeleteSource: the value 1 if the file was copied or moved and +# the source file was deleted -- i.e. if the copy +# was actually a move. # svnConvertedText: the header text with some lines converted to SVN # format. Git-specific lines are preserved. # $lastReadLine: the line last read from $fileHandle. @@ -442,9 +480,8 @@ sub parseGitDiffHeader($$) $_ = $line; - my $headerStartRegEx = qr#^diff --git (\w/)?(.+) (\w/)?([^\r\n]+)#; my $indexPath; - if (/$headerStartRegEx/) { + if (/$gitDiffStartRegEx/) { # The first and second paths can differ in the case of copies # and renames. We use the second file path because it is the # destination path. @@ -458,9 +495,12 @@ sub parseGitDiffHeader($$) my $copiedFromPath; my $foundHeaderEnding; my $isBinary; + my $isDeletion; + my $isNew; my $newExecutableBit = 0; my $oldExecutableBit = 0; - my $similarityIndex; + my $shouldDeleteSource = 0; + my $similarityIndex = 0; my $svnConvertedText; while (1) { # Temporarily strip off any end-of-line characters to simplify @@ -468,45 +508,60 @@ sub parseGitDiffHeader($$) s/([\n\r]+)$//; my $eol = $1; - if (/^(deleted file|old) mode ([0-9]{6})/) { + if (/^(deleted file|old) mode (\d+)/) { $oldExecutableBit = (isExecutable($2) ? 1 : 0); - } elsif (/^new( file)? mode ([0-9]{6})/) { + $isDeletion = 1 if $1 eq "deleted file"; + } elsif (/^new( file)? mode (\d+)/) { $newExecutableBit = (isExecutable($2) ? 1 : 0); + $isNew = 1 if $1; + } elsif (/^similarity index (\d+)%/) { + $similarityIndex = $1; + } elsif (/^copy from (\S+)/) { + $copiedFromPath = $1; + } elsif (/^rename from (\S+)/) { + # FIXME: Record this as a move rather than as a copy-and-delete. + # This will simplify adding rename support to svn-unapply. + # Otherwise, the hash for a deletion would have to know + # everything about the file being deleted in order to + # support undoing itself. Recording as a move will also + # permit us to use "svn move" and "git move". + $copiedFromPath = $1; + $shouldDeleteSource = 1; } elsif (/^--- \S+/) { $_ = "--- $indexPath"; # Convert to SVN format. } elsif (/^\+\+\+ \S+/) { $_ = "+++ $indexPath"; # Convert to SVN format. $foundHeaderEnding = 1; - } elsif (/^similarity index (\d+)%/) { - $similarityIndex = $1; - } elsif (/^copy from (\S+)/) { - $copiedFromPath = $1; + } elsif (/^GIT binary patch$/ ) { + $isBinary = 1; + $foundHeaderEnding = 1; # The "git diff" command includes a line of the form "Binary files # <path1> and <path2> differ" if the --binary flag is not used. } elsif (/^Binary files / ) { die("Error: the Git diff contains a binary file without the binary data in ". "line: \"$_\". Be sure to use the --binary flag when invoking \"git diff\" ". "with diffs containing binary files."); - } elsif (/^GIT binary patch$/ ) { - $isBinary = 1; - $foundHeaderEnding = 1; } $svnConvertedText .= "$_$eol"; # Also restore end-of-line characters. $_ = <$fileHandle>; # Not defined if end-of-file reached. - last if (!defined($_) || /$headerStartRegEx/ || $foundHeaderEnding); + last if (!defined($_) || /$gitDiffStartRegEx/ || $foundHeaderEnding); } my $executableBitDelta = $newExecutableBit - $oldExecutableBit; my %header; - $header{copiedFromPath} = $copiedFromPath if ($copiedFromPath && $similarityIndex == 100); + $header{copiedFromPath} = $copiedFromPath if $copiedFromPath; $header{executableBitDelta} = $executableBitDelta if $executableBitDelta; $header{indexPath} = $indexPath; $header{isBinary} = $isBinary if $isBinary; + $header{isCopyWithChanges} = 1 if ($copiedFromPath && ($similarityIndex != 100 || $executableBitDelta)); + $header{isDeletion} = $isDeletion if $isDeletion; + $header{isNew} = $isNew if $isNew; + $header{shouldDeleteSource} = $shouldDeleteSource if $shouldDeleteSource; $header{svnConvertedText} = $svnConvertedText; return (\%header, $_); @@ -531,6 +586,7 @@ sub parseGitDiffHeader($$) # indexPath: the path of the target file, which is the path found in # the "Index:" line. # isBinary: the value 1 if the diff is for a binary file. +# isNew: the value 1 if the diff is for a new file. # sourceRevision: the revision number of the source, if it exists. This # is the same as the revision number the file was copied # from, in the case of a file copy. @@ -543,16 +599,17 @@ sub parseSvnDiffHeader($$) $_ = $line; - my $headerStartRegEx = qr/^Index: /; - - if (!/$headerStartRegEx/) { + my $indexPath; + if (/$svnDiffStartRegEx/) { + $indexPath = $1; + } else { die("First line of SVN diff does not begin with \"Index \": \"$_\""); } my $copiedFromPath; my $foundHeaderEnding; - my $indexPath; my $isBinary; + my $isNew; my $sourceRevision; my $svnConvertedText; while (1) { @@ -563,12 +620,11 @@ sub parseSvnDiffHeader($$) # Fix paths on ""---" and "+++" lines to match the leading # index line. - if (/^Index: ([^\r\n]+)/) { - $indexPath = $1; - } elsif (s/^--- \S+/--- $indexPath/) { + if (s/^--- \S+/--- $indexPath/) { # --- if (/^--- .+\(revision (\d+)\)/) { $sourceRevision = $1; + $isNew = 1 if !$sourceRevision; # if revision 0. if (/\(from (\S+):(\d+)\)$/) { # The "from" clause is created by svn-create-patch, in # which case there is always also a "revision" clause. @@ -588,7 +644,7 @@ sub parseSvnDiffHeader($$) $_ = <$fileHandle>; # Not defined if end-of-file reached. - last if (!defined($_) || /$headerStartRegEx/ || $foundHeaderEnding); + last if (!defined($_) || /$svnDiffStartRegEx/ || $foundHeaderEnding); } if (!$foundHeaderEnding) { @@ -600,6 +656,7 @@ sub parseSvnDiffHeader($$) $header{copiedFromPath} = $copiedFromPath if $copiedFromPath; $header{indexPath} = $indexPath; $header{isBinary} = $isBinary if $isBinary; + $header{isNew} = $isNew if $isNew; $header{sourceRevision} = $sourceRevision if $sourceRevision; $header{svnConvertedText} = $svnConvertedText; @@ -646,10 +703,10 @@ sub parseDiffHeader($$) my $isSvn; my $lastReadLine; - if ($line =~ /^Index:/) { + if ($line =~ $svnDiffStartRegEx) { $isSvn = 1; ($header, $lastReadLine) = parseSvnDiffHeader($fileHandle, $line); - } elsif ($line =~ /^diff --git/) { + } elsif ($line =~ $gitDiffStartRegEx) { $isGit = 1; ($header, $lastReadLine) = parseGitDiffHeader($fileHandle, $line); } else { @@ -681,7 +738,9 @@ sub parseDiffHeader($$) # indexPath: the path of the target file. For SVN-formatted diffs, # this is the same as the path in the "Index:" line. # isBinary: the value 1 if the diff is for a binary file. +# isDeletion: the value 1 if the diff is known from the header to be a deletion. # isGit: the value 1 if the diff is Git-formatted. +# isNew: the value 1 if the dif is known from the header to be a new file. # isSvn: the value 1 if the diff is SVN-formatted. # sourceRevision: the revision number of the source, if it exists. This # is the same as the revision number the file was copied @@ -700,25 +759,27 @@ sub parseDiffHeader($$) # header block. Leading junk is okay. # $line: the line last read from $fileHandle. # -# Returns ($diffHashRef, $lastReadLine): -# $diffHashRef: A reference to a %diffHash. -# See the %diffHash documentation above. +# Returns ($diffHashRefs, $lastReadLine): +# $diffHashRefs: A reference to an array of references to %diffHash hashes. +# See the %diffHash documentation above. # $lastReadLine: the line last read from $fileHandle sub parseDiff($$) { + # FIXME: Adjust this method so that it dies if the first line does not + # match the start of a diff. This will require a change to + # parsePatch() so that parsePatch() skips over leading junk. my ($fileHandle, $line) = @_; - my $headerStartRegEx = qr#^Index: #; # SVN-style header for the default - my $gitHeaderStartRegEx = qr#^diff --git \w/#; + my $headerStartRegEx = $svnDiffStartRegEx; # SVN-style header for the default my $headerHashRef; # Last header found, as returned by parseDiffHeader(). my $svnText; while (defined($line)) { - if (!$headerHashRef && ($line =~ $gitHeaderStartRegEx)) { + if (!$headerHashRef && ($line =~ $gitDiffStartRegEx)) { # Then assume all diffs in the patch are Git-formatted. This # block was made to be enterable at most once since we assume # all diffs in the patch are formatted the same (SVN or Git). - $headerStartRegEx = $gitHeaderStartRegEx; + $headerStartRegEx = $gitDiffStartRegEx; } if ($line !~ $headerStartRegEx) { @@ -738,19 +799,191 @@ sub parseDiff($$) $svnText .= $headerHashRef->{svnConvertedText}; } - my %diffHashRef; - $diffHashRef{copiedFromPath} = $headerHashRef->{copiedFromPath} if $headerHashRef->{copiedFromPath}; - # FIXME: Add executableBitDelta as a key. - $diffHashRef{indexPath} = $headerHashRef->{indexPath}; - $diffHashRef{isBinary} = $headerHashRef->{isBinary} if $headerHashRef->{isBinary}; - $diffHashRef{isGit} = $headerHashRef->{isGit} if $headerHashRef->{isGit}; - $diffHashRef{isSvn} = $headerHashRef->{isSvn} if $headerHashRef->{isSvn}; - $diffHashRef{sourceRevision} = $headerHashRef->{sourceRevision} if $headerHashRef->{sourceRevision}; - # FIXME: Remove the need for svnConvertedText. See the %diffHash - # code comments above for more information. - $diffHashRef{svnConvertedText} = $svnText; - - return (\%diffHashRef, $line); + my @diffHashRefs; + + if ($headerHashRef->{shouldDeleteSource}) { + my %deletionHash; + $deletionHash{indexPath} = $headerHashRef->{copiedFromPath}; + $deletionHash{isDeletion} = 1; + push @diffHashRefs, \%deletionHash; + } + if ($headerHashRef->{copiedFromPath}) { + my %copyHash; + $copyHash{copiedFromPath} = $headerHashRef->{copiedFromPath}; + $copyHash{indexPath} = $headerHashRef->{indexPath}; + $copyHash{sourceRevision} = $headerHashRef->{sourceRevision} if $headerHashRef->{sourceRevision}; + push @diffHashRefs, \%copyHash; + } + if (!$headerHashRef->{copiedFromPath} || $headerHashRef->{isCopyWithChanges}) { + # Then add the usual file modification. + my %diffHash; + # FIXME: Add executableBitDelta as a key. + $diffHash{indexPath} = $headerHashRef->{indexPath}; + $diffHash{isBinary} = $headerHashRef->{isBinary} if $headerHashRef->{isBinary}; + $diffHash{isDeletion} = $headerHashRef->{isDeletion} if $headerHashRef->{isDeletion}; + $diffHash{isGit} = $headerHashRef->{isGit} if $headerHashRef->{isGit}; + $diffHash{isNew} = $headerHashRef->{isNew} if $headerHashRef->{isNew}; + $diffHash{isSvn} = $headerHashRef->{isSvn} if $headerHashRef->{isSvn}; + if (!$headerHashRef->{copiedFromPath}) { + # If the file was copied, then we have already incorporated the + # sourceRevision information into the change. + $diffHash{sourceRevision} = $headerHashRef->{sourceRevision} if $headerHashRef->{sourceRevision}; + } + # FIXME: Remove the need for svnConvertedText. See the %diffHash + # code comments above for more information. + $diffHash{svnConvertedText} = $svnText; + push @diffHashRefs, \%diffHash; + } + + return (\@diffHashRefs, $line); +} + +# Parse the next SVN property from the given file handle, and advance the handle so the last +# line read is the first line after the property. +# +# This subroutine dies if the first line is not a valid start of an SVN property, +# or the property is missing a value, or the property change type (e.g. "Added") +# does not correspond to the property value type (e.g. "+"). +# +# Args: +# $fileHandle: advanced so the last line read from the handle is the first +# line of the property to parse. This should be a line +# that matches $svnPropertyStartRegEx. +# $line: the line last read from $fileHandle. +# +# Returns ($propertyHashRef, $lastReadLine): +# $propertyHashRef: a hash reference representing a SVN property. +# name: the name of the property. +# value: the last property value. For instance, suppose the property is "Modified". +# Then it has both a '-' and '+' property value in that order. Therefore, +# the value of this key is the value of the '+' property by ordering (since +# it is the last value). +# propertyChangeDelta: the value 1 or -1 if the property was added or +# removed, respectively. +# $lastReadLine: the line last read from $fileHandle. +# +# FIXME: This method is unused as of (05/16/2010). We will call this function +# as part of parsing a SVN footer. See <https://bugs.webkit.org/show_bug.cgi?id=38885>. +sub parseSvnProperty($$) +{ + my ($fileHandle, $line) = @_; + + $_ = $line; + + my $propertyName; + my $propertyChangeType; + if (/$svnPropertyStartRegEx/) { + $propertyChangeType = $1; + $propertyName = $2; + } else { + die("Failed to find SVN property: \"$_\"."); + } + + $_ = <$fileHandle>; # Not defined if end-of-file reached. + + # The "svn diff" command neither inserts newline characters between property values + # nor between successive properties. + # + # FIXME: We do not support property values that contain tailing newline characters + # as it is difficult to disambiguate these trailing newlines from the empty + # line that precedes the contents of a binary patch. + my $propertyValue; + my $propertyValueType; + while (defined($_) && /$svnPropertyValueStartRegEx/) { + # Note, a '-' property may be followed by a '+' property in the case of a "Modified" + # or "Name" property. We only care about the ending value (i.e. the '+' property) + # in such circumstances. So, we take the property value for the property to be its + # last parsed property value. + # + # FIXME: We may want to consider strictly enforcing a '-', '+' property ordering or + # add error checking to prevent '+', '+', ..., '+' and other invalid combinations. + $propertyValueType = $1; + ($propertyValue, $_) = parseSvnPropertyValue($fileHandle, $_); + } + + if (!$propertyValue) { + die("Failed to find the property value for the SVN property \"$propertyName\": \"$_\"."); + } + + my $propertyChangeDelta; + if ($propertyValueType eq '+') { + $propertyChangeDelta = 1; + } elsif ($propertyValueType eq '-') { + $propertyChangeDelta = -1; + } else { + die("Not reached."); + } + + # We perform a simple validation that an "Added" or "Deleted" property + # change type corresponds with a "+" and "-" value type, respectively. + my $expectedChangeDelta; + if ($propertyChangeType eq "Added") { + $expectedChangeDelta = 1; + } elsif ($propertyChangeType eq "Deleted") { + $expectedChangeDelta = -1; + } + + if ($expectedChangeDelta && $propertyChangeDelta != $expectedChangeDelta) { + die("The final property value type found \"$propertyValueType\" does not " . + "correspond to the property change type found \"$propertyChangeType\"."); + } + + my %propertyHash; + $propertyHash{name} = $propertyName; + $propertyHash{propertyChangeDelta} = $propertyChangeDelta; + $propertyHash{value} = $propertyValue; + return (\%propertyHash, $_); +} + +# Parse the value of an SVN property from the given file handle, and advance +# the handle so the last line read is the first line after the property value. +# +# This subroutine dies if the first line is an invalid SVN property value line +# (i.e. a line that does not begin with " +" or " -"). +# +# Args: +# $fileHandle: advanced so the last line read from the handle is the first +# line of the property value to parse. This should be a line +# beginning with " +" or " -". +# $line: the line last read from $fileHandle. +# +# Returns ($propertyValue, $lastReadLine): +# $propertyValue: the value of the property. +# $lastReadLine: the line last read from $fileHandle. +sub parseSvnPropertyValue($$) +{ + my ($fileHandle, $line) = @_; + + $_ = $line; + + my $propertyValue; + my $eol; + if (/$svnPropertyValueStartRegEx/) { + $propertyValue = $2; # Does not include the end-of-line character(s). + $eol = $POSTMATCH; + } else { + die("Failed to find property value beginning with '+' or '-': \"$_\"."); + } + + while (<$fileHandle>) { + if (/^$/ || /$svnPropertyValueStartRegEx/ || /$svnPropertyStartRegEx/) { + # Note, we may encounter an empty line before the contents of a binary patch. + # Also, we check for $svnPropertyValueStartRegEx because a '-' property may be + # followed by a '+' property in the case of a "Modified" or "Name" property. + # We check for $svnPropertyStartRegEx because it indicates the start of the + # next property to parse. + last; + } + + # Temporarily strip off any end-of-line characters. We add the end-of-line characters + # from the previously processed line to the start of this line so that the last line + # of the property value does not end in end-of-line characters. + s/([\n\r]+)$//; + $propertyValue .= "$eol$_"; + $eol = $1; + } + + return ($propertyValue, $_); } # Parse a patch file created by svn-create-patch. @@ -766,16 +999,16 @@ sub parsePatch($) { my ($fileHandle) = @_; + my $newDiffHashRefs; my @diffHashRefs; # return value my $line = <$fileHandle>; while (defined($line)) { # Otherwise, at EOF. - my $diffHashRef; - ($diffHashRef, $line) = parseDiff($fileHandle, $line); + ($newDiffHashRefs, $line) = parseDiff($fileHandle, $line); - push @diffHashRefs, $diffHashRef; + push @diffHashRefs, @$newDiffHashRefs; } return @diffHashRefs; @@ -853,6 +1086,51 @@ sub prepareParsedPatch($@) return \%preparedPatchHash; } +# Return localtime() for the project's time zone, given an integer time as +# returned by Perl's time() function. +sub localTimeInProjectTimeZone($) +{ + my $epochTime = shift; + + # Change the time zone temporarily for the localtime() call. + my $savedTimeZone = $ENV{'TZ'}; + $ENV{'TZ'} = $changeLogTimeZone; + my @localTime = localtime($epochTime); + if (defined $savedTimeZone) { + $ENV{'TZ'} = $savedTimeZone; + } else { + delete $ENV{'TZ'}; + } + + return @localTime; +} + +# Set the reviewer and date in a ChangeLog patch, and return the new patch. +# +# Args: +# $patch: a ChangeLog patch as a string. +# $reviewer: the name of the reviewer, or undef if the reviewer should not be set. +# $epochTime: an integer time as returned by Perl's time() function. +sub setChangeLogDateAndReviewer($$$) +{ + my ($patch, $reviewer, $epochTime) = @_; + + my @localTime = localTimeInProjectTimeZone($epochTime); + my $newDate = strftime("%Y-%m-%d", @localTime); + + my $firstChangeLogLineRegEx = qr#(\n\+)\d{4}-[^-]{2}-[^-]{2}( )#; + $patch =~ s/$firstChangeLogLineRegEx/$1$newDate$2/; + + if (defined($reviewer)) { + # We include a leading plus ("+") in the regular expression to make + # the regular expression less likely to match text in the leading junk + # for the patch, if the patch has leading junk. + $patch =~ s/(\n\+.*)NOBODY \(OOPS!\)/$1$reviewer/; + } + + return $patch; +} + # If possible, returns a ChangeLog patch equivalent to the given one, # but with the newest ChangeLog entry inserted at the top of the # file -- i.e. no leading context and all lines starting with "+". diff --git a/WebKitTools/Scripts/build-dumprendertree b/WebKitTools/Scripts/build-dumprendertree index 6502916..87c720f 100755 --- a/WebKitTools/Scripts/build-dumprendertree +++ b/WebKitTools/Scripts/build-dumprendertree @@ -70,20 +70,9 @@ if (isAppleMacWebKit()) { $result = buildXCodeProject("DumpRenderTree", $clean, XcodeOptions(), @ARGV); } elsif (isAppleWinWebKit()) { $result = buildVisualStudioProject("DumpRenderTree.sln", $clean); -} elsif (isQt() || isGtk() || isWx()) { - # Qt, Gtk and wxWindows build everything in one shot. No need to build anything here. +} elsif (isQt() || isGtk() || isWx() || isChromium()) { + # Qt, Gtk wxWindows, and Chromium build everything in one shot. No need to build anything here. $result = 0; -} elsif (isChromium()) { - if (isDarwin()) { - $result = buildXCodeProject("DumpRenderTree.gyp/DumpRenderTree", $clean, "-configuration", configuration(), @ARGV); - } elsif (isCygwin() || isWindows()) { - # Windows build - builds the root visual studio solution. - $result = buildChromiumVisualStudioProject("DumpRenderTree.gyp/DumpRenderTree.sln", $clean); - } elsif (isLinux()) { - $result = buildChromiumMakefile("../../WebKit/chromium/", "DumpRenderTree", $clean); - } else { - die "This platform is not supported by Chromium.\n"; - } } else { die "Building not defined for this platform!\n"; } diff --git a/WebKitTools/Scripts/build-webkit b/WebKitTools/Scripts/build-webkit index 3a3edb9..e70117f 100755 --- a/WebKitTools/Scripts/build-webkit +++ b/WebKitTools/Scripts/build-webkit @@ -31,6 +31,7 @@ use strict; use File::Basename; +use File::Find; use File::Spec; use FindBin; use Getopt::Long qw(:config pass_through); @@ -48,6 +49,9 @@ chdirWebKit(); my $showHelp = 0; my $clean = 0; my $minimal = 0; +my $installHeaders; +my $installLibs; +my $prefixPath; my $makeArgs; my $startTime = time(); @@ -178,7 +182,7 @@ my @features = ( define => "ENABLE_XSLT", default => 1, value => \$xsltSupport }, { option => "file-reader", desc => "Toggle FileReader support", - define => "ENABLE_FILE_READER", default => 0, value => \$fileReaderSupport }, + define => "ENABLE_FILE_READER", default => isAppleWebKit(), value => \$fileReaderSupport }, { option => "file-writer", desc => "Toggle FileWriter support", define => "ENABLE_FILE_WRITER", default => 0, value => \$fileWriterSupport }, @@ -225,6 +229,10 @@ Usage: $programName [options] [options to pass to build system] --qt Build the Qt port --inspector-frontend Copy changes to the inspector front-end files to the build directory + --install-headers=<path> Set installation path for the headers (Qt only) + --install-libs=<path> Set installation path for the libraries (Qt only) + + --prefix=<path> Set installation prefix to the given path (Gtk only) --makeargs=<arguments> Optional Makefile flags --minimal No optional features, unless explicitly enabled. @@ -234,6 +242,9 @@ EOF my %options = ( 'help' => \$showHelp, 'clean' => \$clean, + 'install-headers=s' => \$installHeaders, + 'install-libs=s' => \$installLibs, + 'prefix=s' => \$prefixPath, 'makeargs=s' => \$makeArgs, 'minimal' => \$minimal, ); @@ -257,6 +268,18 @@ setConfiguration(); my $productDir = productDir(); +# Remove 0 byte sized files from productDir after slave lost for Qt buildbots. +File::Find::find(\&unlinkZeroFiles, $productDir) if isQt(); + +sub unlinkZeroFiles () +{ + my $file = $File::Find::name; + if (! -s $file) { + unlink $file; + print "0 byte sized file removed from build directory: $file\n"; + } +} + # Check that all the project directories are there. my @projects = ("JavaScriptCore", "WebCore", "WebKit"); @@ -276,13 +299,15 @@ if (isGtk()) { push @options, autotoolsFlag(${$_->{value}}, $_->{option}); } + push @options, "--prefix=" . $prefixPath if defined($prefixPath); push @options, "--makeargs=" . $makeArgs if defined($makeArgs); } elsif (isAppleMacWebKit()) { push @options, XcodeOptions(); - sub option($$) + sub option($$$) { - my ($feature, $isEnabled) = @_; + my ($feature, $isEnabled, $defaultValue) = @_; + return "" if $defaultValue == $isEnabled; return $feature . "=" . ($isEnabled ? $feature : " "); } @@ -290,7 +315,8 @@ if (isGtk()) { if ($_->{option} eq "coverage") { push @options, XcodeCoverageSupportOptions() if $coverageSupport; } else { - push @options, option($_->{define}, ${$_->{value}}); + my $option = option($_->{define}, ${$_->{value}}, $_->{default}); + push @options, $option unless $option eq ""; } } @@ -338,12 +364,14 @@ if (isGtk()) { (system("perl WebKitTools/Scripts/update-webkit-support-libs") == 0) or die; } elsif (isQt()) { @options = @ARGV; + push @options, "--install-headers=" . $installHeaders if defined($installHeaders); + push @options, "--install-libs=" . $installLibs if defined($installLibs); push @options, "--makeargs=" . $makeArgs if defined($makeArgs); foreach (@features) { push @options, "DEFINES+=$_->{define}=${$_->{value}}" if ${$_->{value}} != $_->{default}; } - + if ($minimal) { push @options, "CONFIG+=minimal"; } diff --git a/WebKitTools/Scripts/do-webcore-rename b/WebKitTools/Scripts/do-webcore-rename index 56d8bed..32dd05e 100755 --- a/WebKitTools/Scripts/do-webcore-rename +++ b/WebKitTools/Scripts/do-webcore-rename @@ -67,9 +67,31 @@ sub wanted push @paths, $File::Find::name; } -my $isDOMTypeRename = 0; + +# Setting isDOMTypeRename to 1 rather than 0 expands the regexps used +# below to handle custom JavaScript bindings. +my $isDOMTypeRename = 1; my %renames = ( - "m_sel" => "m_selection", + "WebGLArray" => "ArrayBufferView", + "WebGLArrayBuffer" => "ArrayBuffer", + "WebGLByteArray" => "Int8Array", + "WebGLFloatArray" => "FloatArray", + "WebGLIntArray" => "Int32Array", + "WebGLIntegralTypedArrayBase" => "IntegralTypedArrayBase", + "WebGLShortArray" => "Int16Array", + "WebGLUnsignedByteArray" => "Uint8Array", + "WebGLUnsignedIntArray" => "Uint32Array", + "WebGLUnsignedShortArray" => "Uint16Array", + "WebGLTypedArrayBase" => "TypedArrayBase", + # JSDOMWindow constructors. + "webGLArrayBuffer" => "arrayBuffer", + "webGLByteArray" => "int8Array", + "webGLFloatArray" => "floatArray", + "webGLIntArray" => "int32Array", + "webGLShortArray" => "int16Array", + "webGLUnsignedByteArray" => "uint8Array", + "webGLUnsignedIntArray" => "uint32Array", + "webGLUnsignedShortArray" => "uint16Array", ); my %renamesContemplatedForTheFuture = ( @@ -142,12 +164,39 @@ my %renamesContemplatedForTheFuture = ( "NativeFunction" => "HostFunction", ); +# Sort the keys of the renames hash in order of decreasing length. This +# handles the case where some of the renames are substrings of others; +# i.e., "Foo" => "Bar" and "FooBuffer" => "BarBuffer". +my @sortedRenameKeys = sort { length($b) - length($a) } keys %renames; + # rename files +sub renameFile +{ + my $file = shift; + + if ($isDOMTypeRename) { + # Find the longest key in %renames which matches this more permissive regexp. + # (The old regexp would match ".../Foo.cpp" but not ".../JSFooCustom.cpp".) + # This handles renaming of custom JavaScript bindings even when some of the + # renames are substrings of others. The only reason we don't do this all the + # time is to avoid accidental file renamings for short, non-DOM renames. + for my $key (@sortedRenameKeys) { + my $newFile = ""; + $newFile = "$1$renames{$2}$3" if $file =~ /^(.*\/\w*)($key)(\w*\.\w+)$/; + if ($newFile ne "") { + return $newFile; + } + } + } else { + $file = "$1$renames{$2}$3" if $file =~ /^(.*\/)(\w+)(\.\w+)$/ && $renames{$2}; + } + return $file; +} + my %newFile; for my $file (sort @paths) { - my $f = $file; - $f = "$1$renames{$2}$3" if $f =~ /^(.*\/)(\w+)(\.\w+)$/ && $renames{$2}; + my $f = renameFile($file); if ($f ne $file) { $newFile{$file} = $f; } @@ -182,11 +231,12 @@ for my $file (sort @paths) { my $newContents = $contents; if ($isDOMTypeRename) { - for my $from (keys %renames) { - $newContents =~ s/\b$from/$renames{$from}/g; + for my $from (@sortedRenameKeys) { + # Handle JavaScript custom bindings. + $newContents =~ s/\b(JS|V8|to|)$from/$1$renames{$from}/g; } } else { - for my $from (keys %renames) { + for my $from (@sortedRenameKeys) { $newContents =~ s/\b$from(?!["\w])/$renames{$from}/g; # this " unconfuses Xcode syntax highlighting } } diff --git a/WebKitTools/Scripts/ensure-valid-python b/WebKitTools/Scripts/ensure-valid-python new file mode 100644 index 0000000..c21ad4e --- /dev/null +++ b/WebKitTools/Scripts/ensure-valid-python @@ -0,0 +1,135 @@ +#!/usr/bin/perl -w +# 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: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. Neither the name of Apple Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + +use strict; + +use File::Basename; +use File::Spec; +use File::Temp qw(tempdir); +use FindBin; + +use lib $FindBin::Bin; +use webkitdirs; +use VCSUtils; + +my $macPythonURL = "http://www.python.org/ftp/python/2.6.5/python-2.6.5-macosx10.3-2010-03-24.dmg"; +my $macPythonMD5 = "84489bba813fdbb6041b69d4310a86da"; +my $macPythonInstallerName = "Python.mpkg"; + +# We could use a consistent download location, like the source or build directory. +my $tempDirectory = File::Temp->newdir(); +my $downloadDirectory = $tempDirectory; +my $mountPoint = File::Spec->join($tempDirectory, "mount"); + +sub checkPythonVersion() +{ + # Will exit 0 if Python is 2.5 or greater, non-zero otherwise. + `python -c "import sys;sys.exit(sys.version_info[:2] < (2,5))"`; + return exitStatus($?) == 0; +} + +sub downloadFileToPath($$) +{ + my ($remoteURL, $localPath) = @_; + print "Downloading $remoteURL to $localPath\n"; + my $exitCode = system("curl", "-o", $localPath, $remoteURL); + return exitStatus($exitCode) == 0; +} + +sub checkMD5($$) +{ + my ($path, $expectedMD5) = @_; + my $md5Output = `md5 -q "$path"`; + chomp($md5Output); + my $isValid = $md5Output eq $expectedMD5; + print "'$md5Output' does not match expected: '$expectedMD5'\n" unless $isValid; + return $isValid; +} + +sub mountDMG($$) +{ + my ($dmgPath, $mountPoint) = @_; + print "Mounting $dmgPath at $mountPoint\n"; + return system("hdiutil", "attach", "-mountpoint", $mountPoint, "-nobrowse", $dmgPath) == 0; +} + +sub unmountDMG($) +{ + my ($mountPoint) = @_; + print "Unmounting disk image from $mountPoint\n"; + my $exitCode = system("hdiutil", "detach", $mountPoint); + return exitStatus($exitCode) == 0; +} + +sub runInstaller($) +{ + my ($installerPackage) = @_; + print "sudo will now ask for your password to run the Python installer.\n"; + print "The installer will install Python in /Library/Frameworks/Python.framework\n"; + print "and add symlinks from /usr/local/bin.\n"; + return system("sudo", "installer", "-verbose", "-pkg", $installerPackage, "-target", "/") == 0; +} + +sub downloadAndMountMacPythonDMG($$) +{ + my ($pythonURL, $pythonMD5) = @_; + my $localFilename = basename($pythonURL); + my $localPath = File::Spec->join($downloadDirectory, $localFilename); + + downloadFileToPath($pythonURL, $localPath) or die "Failed to download $pythonURL"; + checkMD5($localPath, $pythonMD5) or die "MD5 check failed on $localPath"; + return mountDMG($localPath, $mountPoint); +} + +sub installMacPython() +{ + downloadAndMountMacPythonDMG($macPythonURL, $macPythonMD5) or die "Failed to download and mount disk image."; + print "Mounted python install image at: $mountPoint\n"; + my $installerPackage = File::Spec->join($mountPoint, $macPythonInstallerName); + my $installSuccess = runInstaller($installerPackage); + unmountDMG($mountPoint) or die "Failed to unmount disk image from $mountPoint"; + return $installSuccess; +} + +sub main() +{ + # Congrats, your Python is fine. + return 0 if checkPythonVersion(); + + if (!isTiger()) { + print "Your Python version is insuficient to run WebKit's Python code. Please update.\n"; + print "See http://trac.webkit.org/wiki/PythonGuidelines for more info.\n"; + return 1; + } + + installMacPython() or die "Failed to install Python."; + + checkPythonVersion() or die "Final version check failed, must have failed to update Python"; + print "Successfully updated python.\n"; +} + +exit(main()); diff --git a/WebKitTools/Scripts/new-run-webkit-httpd b/WebKitTools/Scripts/new-run-webkit-httpd index 88ae84e..f6ec164 100755 --- a/WebKitTools/Scripts/new-run-webkit-httpd +++ b/WebKitTools/Scripts/new-run-webkit-httpd @@ -65,7 +65,6 @@ def run(options): tempfile.gettempdir(), port=options.port, root=options.root, - register_cygwin=options.register_cygwin, run_background=options.run_background) if options.server == 'start': httpd.start() diff --git a/WebKitTools/Scripts/new-run-webkit-websocketserver b/WebKitTools/Scripts/new-run-webkit-websocketserver index 8e4aeaa..4f6deaa 100644 --- a/WebKitTools/Scripts/new-run-webkit-websocketserver +++ b/WebKitTools/Scripts/new-run-webkit-websocketserver @@ -42,25 +42,32 @@ def main(): option_parser = optparse.OptionParser() option_parser.add_option('--server', type='choice', choices=['start', 'stop'], default='start', - help='Server action (start|stop)') + help='Server action (start|stop).') option_parser.add_option('-p', '--port', dest='port', - default=None, help='Port to listen on') + default=None, help='Port to listen on.') option_parser.add_option('-r', '--root', help='Absolute path to DocumentRoot ' - '(overrides layout test roots)') + '(overrides layout test roots).') option_parser.add_option('-t', '--tls', dest='use_tls', action='store_true', - default=False, help='use TLS (wss://)') + default=False, help='use TLS (wss://).') option_parser.add_option('-k', '--private_key', dest='private_key', default='', help='TLS private key file.') option_parser.add_option('-c', '--certificate', dest='certificate', default='', help='TLS certificate file.') + option_parser.add_option('--chromium', action='store_true', + dest='chromium', + default=False, + help='Use the Chromium port.') option_parser.add_option('--register_cygwin', action="store_true", dest="register_cygwin", - help='Register Cygwin paths (on Win try bots)') + help='Register Cygwin paths (on Win try bots).') option_parser.add_option('--pidfile', help='path to pid file.') + option_parser.add_option('--output-dir', dest='output_dir', + default=None, help='output directory.') option_parser.add_option('-v', '--verbose', action='store_true', - default=False, help='include debug-level logging') + default=False, + help='Include debug-level logging.') options, args = option_parser.parse_args() if not options.port: @@ -71,6 +78,9 @@ def main(): # FIXME: We shouldn't grab at this private variable. options.port = websocket_server._DEFAULT_WS_PORT + if not options.output_dir: + options.output_dir = tempfile.gettempdir() + kwds = {'port': options.port, 'use_tls': options.use_tls} if options.root: kwds['root'] = options.root @@ -78,12 +88,11 @@ def main(): kwds['private_key'] = options.private_key if options.certificate: kwds['certificate'] = options.certificate - kwds['register_cygwin'] = options.register_cygwin if options.pidfile: kwds['pidfile'] = options.pidfile port_obj = factory.get() - pywebsocket = websocket_server.PyWebSocket(port_obj, tempfile.gettempdir(), **kwds) + pywebsocket = websocket_server.PyWebSocket(port_obj, options.output_dir, **kwds) log_level = logging.WARN if options.verbose: diff --git a/WebKitTools/Scripts/old-run-webkit-tests b/WebKitTools/Scripts/old-run-webkit-tests index f6dbf5b..783606d 100755 --- a/WebKitTools/Scripts/old-run-webkit-tests +++ b/WebKitTools/Scripts/old-run-webkit-tests @@ -557,7 +557,7 @@ my $lastDirectory = ""; my $isHttpdOpen = 0; my $isWebSocketServerOpen = 0; -my $webSocketServerPID = 0; +my $webSocketServerPidFile = 0; my $failedToStartWebSocketServer = 0; # wss is disabled until all platforms support pyOpenSSL. # my $webSocketSecureServerPID = 0; @@ -1426,54 +1426,19 @@ sub openWebSocketServerIfNeeded() return 1 if $isWebSocketServerOpen; return 0 if $failedToStartWebSocketServer; - my $webSocketServerPath = "/usr/bin/python"; - my $webSocketPythonPath = "WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket"; my $webSocketHandlerDir = "$testDirectory"; - my $webSocketHandlerScanDir = "$testDirectory/websocket/tests"; - my $webSocketHandlerMapFile = "$webSocketHandlerScanDir/handler_map.txt"; - my $sslCertificate = "$testDirectory/http/conf/webkit-httpd.pem"; my $absTestResultsDirectory = resolveAndMakeTestResultsDirectory(); - my $logFile = "$absTestResultsDirectory/pywebsocket_log.txt"; + $webSocketServerPidFile = "$absTestResultsDirectory/websocket.pid"; my @args = ( - "WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py", - "--server-host", "127.0.0.1", + "WebKitTools/Scripts/new-run-webkit-websocketserver", + "--server", "start", "--port", "$webSocketPort", - "--document-root", "$webSocketHandlerDir", - "--scan-dir", "$webSocketHandlerScanDir", - "--websock-handlers-map-file", "$webSocketHandlerMapFile", - "--cgi-paths", "/websocket/tests", - "--log-file", "$logFile", - "--strict", + "--root", "$webSocketHandlerDir", + "--output-dir", "$absTestResultsDirectory", + "--pidfile", "$webSocketServerPidFile" ); - # wss is disabled until all platforms support pyOpenSSL. - # my @argsSecure = ( - # "WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py", - # "-p", "$webSocketSecurePort", - # "-d", "$webSocketHandlerDir", - # "-t", - # "-k", "$sslCertificate", - # "-c", "$sslCertificate", - # ); - - $ENV{"PYTHONPATH"} = $webSocketPythonPath; - $webSocketServerPID = open3(\*WEBSOCKETSERVER_IN, \*WEBSOCKETSERVER_OUT, \*WEBSOCKETSERVER_ERR, $webSocketServerPath, @args); - # wss is disabled until all platforms support pyOpenSSL. - # $webSocketSecureServerPID = open3(\*WEBSOCKETSECURESERVER_IN, \*WEBSOCKETSECURESERVER_OUT, \*WEBSOCKETSECURESERVER_ERR, $webSocketServerPath, @argsSecure); - # my @listen = ("http://127.0.0.1:$webSocketPort", "https://127.0.0.1:$webSocketSecurePort"); - my @listen = ("http://127.0.0.1:$webSocketPort"); - for (my $i = 0; $i < @listen; $i++) { - my $retryCount = 10; - while (system("/usr/bin/curl -k -q --silent --stderr - --output /dev/null $listen[$i]") && $retryCount) { - sleep 1; - --$retryCount; - } - unless ($retryCount) { - print STDERR "Timed out waiting for WebSocketServer to start.\n"; - $failedToStartWebSocketServer = 1; - return 0; - } - } + system "/usr/bin/python", @args; $isWebSocketServerOpen = 1; return 1; @@ -1483,17 +1448,15 @@ sub closeWebSocketServer() { return if !$isWebSocketServerOpen; - close WEBSOCKETSERVER_IN; - close WEBSOCKETSERVER_OUT; - close WEBSOCKETSERVER_ERR; - kill 15, $webSocketServerPID; + my @args = ( + "WebKitTools/Scripts/new-run-webkit-websocketserver", + "--server", "stop", + "--pidfile", "$webSocketServerPidFile" + ); + system "/usr/bin/python", @args; + unlink "$webSocketServerPidFile"; # wss is disabled until all platforms support pyOpenSSL. - # close WEBSOCKETSECURESERVER_IN; - # close WEBSOCKETSECURESERVER_OUT; - # close WEBSOCKETSECURESERVER_ERR; - # kill 15, $webSocketSecureServerPID; - $isWebSocketServerOpen = 0; } @@ -2277,7 +2240,8 @@ sub stopRunningTestsEarlyIfNeeded() return 0 if !$exitAfterNFailures || $resetResults; my $passCount = $counts{match} || 0; # $counts{match} will be undefined if we've not yet passed a test (e.g. the first test fails). - my $failureCount = $count - $passCount; # "Failure" here includes new tests, timeouts, crashes, etc. + my $newCount = $counts{new} || 0; + my $failureCount = $count - $passCount - $newCount; # "Failure" here includes timeouts, crashes, etc. return 0 if $failureCount < $exitAfterNFailures; print "\nExiting early after $failureCount failures. $count tests run."; diff --git a/WebKitTools/Scripts/run-launcher b/WebKitTools/Scripts/run-launcher index bc00aac..414d4af 100755 --- a/WebKitTools/Scripts/run-launcher +++ b/WebKitTools/Scripts/run-launcher @@ -47,7 +47,7 @@ checkFrameworks(); # Set paths according to the build system used if (isQt()) { my $libDir = catdir(productDir(), 'lib'); - $launcherPath = catdir($launcherPath, "bin", "QtLauncher"); + $launcherPath = catdir($launcherPath, "bin", "QtTestBrowser"); $ENV{QTWEBKIT_PLUGIN_PATH} = catdir($libDir, 'plugins'); diff --git a/WebKitTools/Scripts/run-webkit-websocketserver b/WebKitTools/Scripts/run-webkit-websocketserver index 06f9079..08d430b 100755 --- a/WebKitTools/Scripts/run-webkit-websocketserver +++ b/WebKitTools/Scripts/run-webkit-websocketserver @@ -47,7 +47,7 @@ my $webSocketPort = 8880; my $srcDir = sourceDir(); my $layoutTestsName = "$srcDir/LayoutTests"; my $testDirectory = File::Spec->rel2abs($layoutTestsName); -my $webSocketServerPID = 0; +my $webSocketServerPidFile = "$testDirectory/websocket.pid"; print "Starting Web Socket server...\n"; @@ -60,41 +60,29 @@ closeWebSocketServer(); print "Stopped.\n"; exit 0; - sub openWebSocketServer() { - my $webSocketServerPath = "/usr/bin/python"; - my $webSocketPythonPath = "$srcDir/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket"; my $webSocketHandlerDir = "$testDirectory"; - my $webSocketHandlerScanDir = "$testDirectory/websocket/tests"; - my $webSocketHandlerMapFile = "$webSocketHandlerScanDir/handler_map.txt"; my @args = ( - "$srcDir/WebKitTools/Scripts/webkitpy/thirdparty/pywebsocket/mod_pywebsocket/standalone.py", - "--server-host", "127.0.0.1", + "$srcDir/WebKitTools/Scripts/new-run-webkit-websocketserver", + "--server", "start", "--port", "$webSocketPort", - "--document-root", "$webSocketHandlerDir", - "--scan-dir", "$webSocketHandlerScanDir", - "--websock-handlers-map-file", "$webSocketHandlerMapFile", - "--cgi-paths", "/websocket/tests", + "--root", "$webSocketHandlerDir", + "--pidfile", "$webSocketServerPidFile" ); - - $ENV{"PYTHONPATH"} = $webSocketPythonPath; - $webSocketServerPID = open2(\*WEBSOCKETSERVER_IN, \*WEBSOCKETSERVER_OUT, $webSocketServerPath, @args); - - my $listen = "http://127.0.0.1:$webSocketPort"; - my $retryCount = 10; - while (system("/usr/bin/curl -k -q --silent --stderr - --output /dev/null $listen") && $retryCount) { - sleep 1; - --$retryCount; - } - die "Timed out waiting for WebSocketServer to start" unless $retryCount; + system "/usr/bin/python", @args; } sub closeWebSocketServer() { - close WEBSOCKETSERVER_IN; - close WEBSOCKETSERVER_OUT; - kill 15, $webSocketServerPID; + my @args = ( + "$srcDir/WebKitTools/Scripts/new-run-webkit-websocketserver", + "--server", "stop", + "--pidfile", "$webSocketServerPidFile" + ); + system "/usr/bin/python", @args; + unlink "$webSocketServerPidFile"; } + diff --git a/WebKitTools/Scripts/svn-apply b/WebKitTools/Scripts/svn-apply index b49ccec..33b2279 100755 --- a/WebKitTools/Scripts/svn-apply +++ b/WebKitTools/Scripts/svn-apply @@ -42,8 +42,7 @@ # Paths from Index: lines are used rather than the paths on the patch lines, which # makes patches generated by "cvs diff" work (increasingly unimportant since we # use Subversion now). -# ChangeLog patches use --fuzz=3 to prevent rejects, and the entry date is set in -# the patch to today's date using $changeLogTimeZone. +# ChangeLog patches use --fuzz=3 to prevent rejects. # Handles binary files (requires patches made by svn-create-patch). # Handles copied and moved files (requires patches made by svn-create-patch). # Handles git-diff patches (without binary changes) created at the top-level directory @@ -80,7 +79,6 @@ sub handleGitBinaryChange($$); sub isDirectoryEmptyForRemoval($); sub patch($); sub removeDirectoriesIfNeeded(); -sub setChangeLogDateAndReviewer($$); # These should be replaced by an scm class/module: sub scmKnowsOfFile($); @@ -88,10 +86,6 @@ sub scmCopy($$); sub scmAdd($); sub scmRemove($); - -# Project time zone for Cupertino, CA, US -my $changeLogTimeZone = "PST8PDT"; - my $merge = 0; my $showHelp = 0; my $reviewer; @@ -117,12 +111,12 @@ my %removeDirectoryIgnoreList = ( '_svn' => 1, ); +my $epochTime = time(); # This is used to set the date in ChangeLog files. my $globalExitStatus = 0; my $repositoryRootPath = determineVCSRoot(); my %checkedDirectories; -my %copiedFiles; # Need to use a typeglob to pass the file handle as a parameter, # otherwise get a bareword error. @@ -154,8 +148,6 @@ for my $copyDiffHashRef (@copyDiffHashRefs) { addDirectoriesIfNeeded(dirname($indexPath)); scmCopy($copiedFromPath, $indexPath); - - $copiedFiles{$indexPath} = $copiedFromPath; } for my $diffHashRef (@nonCopyDiffHashRefs) { @@ -247,14 +239,16 @@ sub handleBinaryChange($$) sub handleGitBinaryChange($$) { - my ($fullPath, $contents) = @_; + my ($fullPath, $diffHashRef) = @_; + + my $contents = $diffHashRef->{svnConvertedText}; my ($binaryChunkType, $binaryChunk, $reverseBinaryChunkType, $reverseBinaryChunk) = decodeGitBinaryPatch($contents, $fullPath); # FIXME: support "delta" type. die "only literal type is supported now" if ($binaryChunkType ne "literal" || $reverseBinaryChunkType ne "literal"); - my $isFileAddition = $contents =~ /\nnew file mode \d+\n/; - my $isFileDeletion = $contents =~ /\ndeleted file mode \d+\n/; + my $isFileAddition = $diffHashRef->{isNew}; + my $isFileDeletion = $diffHashRef->{isDeletion}; my $originalContents = ""; if (open FILE, $fullPath) { @@ -303,7 +297,10 @@ sub patch($) { my ($diffHashRef) = @_; - my $patch = $diffHashRef->{svnConvertedText}; + # Make sure $patch is initialized to some value. A deletion can have no + # svnConvertedText property in the case of a deletion resulting from a + # Git rename. + my $patch = $diffHashRef->{svnConvertedText} || ""; my $fullPath = $diffHashRef->{indexPath}; my $isBinary = $diffHashRef->{isBinary}; @@ -312,19 +309,18 @@ sub patch($) my $deletion = 0; my $addition = 0; - # FIXME: This information should be extracted from the diff file as - # part of the parsing stage, i.e. the call to parsePatch(). - $addition = 1 if ($patch =~ /\n--- .+\(revision 0\)\r?\n/ || $patch =~ /\n@@ -0,0 .* @@/) && !exists($copiedFiles{$fullPath}); - $deletion = 1 if $patch =~ /\n@@ .* \+0,0 @@/; + $addition = 1 if ($diffHashRef->{isNew} || $patch =~ /\n@@ -0,0 .* @@/); + $deletion = 1 if ($diffHashRef->{isDeletion} || $patch =~ /\n@@ .* \+0,0 @@/); if (!$addition && !$deletion && !$isBinary) { # Standard patch, patch tool can handle this. if (basename($fullPath) eq "ChangeLog") { my $changeLogDotOrigExisted = -f "${fullPath}.orig"; - applyPatch(setChangeLogDateAndReviewer(fixChangeLogPatch($patch), $reviewer), $fullPath, ["--fuzz=3"]); + my $newPatch = setChangeLogDateAndReviewer(fixChangeLogPatch($patch), $reviewer, $epochTime); + applyPatch($newPatch, $fullPath, ["--fuzz=3"]); unlink("${fullPath}.orig") if (! $changeLogDotOrigExisted); } else { - applyPatch($patch, $fullPath); + applyPatch($patch, $fullPath) if $patch; } } else { # Either a deletion, an addition or a binary change. @@ -333,18 +329,17 @@ sub patch($) if ($isBinary) { if ($isGit) { - handleGitBinaryChange($fullPath, $patch); + handleGitBinaryChange($fullPath, $diffHashRef); } else { - handleBinaryChange($fullPath, $patch); + handleBinaryChange($fullPath, $patch) if $patch; } } elsif ($deletion) { - # Deletion - applyPatch($patch, $fullPath, ["--force"]); + applyPatch($patch, $fullPath, ["--force"]) if $patch; scmRemove($fullPath); } else { # Addition rename($fullPath, "$fullPath.orig") if -e $fullPath; - applyPatch($patch, $fullPath); + applyPatch($patch, $fullPath) if $patch; unlink("$fullPath.orig") if -e "$fullPath.orig" && checksum($fullPath) eq checksum("$fullPath.orig"); scmAdd($fullPath); # What is this for? @@ -364,26 +359,6 @@ sub removeDirectoriesIfNeeded() } } -sub setChangeLogDateAndReviewer($$) -{ - my $patch = shift; - my $reviewer = shift; - my $savedTimeZone = $ENV{'TZ'}; - # Set TZ temporarily so that localtime() is in that time zone - $ENV{'TZ'} = $changeLogTimeZone; - my $newDate = strftime("%Y-%m-%d", localtime()); - if (defined $savedTimeZone) { - $ENV{'TZ'} = $savedTimeZone; - } else { - delete $ENV{'TZ'}; - } - $patch =~ s/(\n\+)\d{4}-[^-]{2}-[^-]{2}( )/$1$newDate$2/; - if (defined($reviewer)) { - $patch =~ s/NOBODY \(OOPS!\)/$reviewer/; - } - return $patch; -} - # This could be made into a more general "status" call, except svn and git # have different ideas about "moving" files which might get confusing. sub scmWillDeleteFile($) @@ -399,6 +374,22 @@ sub scmWillDeleteFile($) return 0; } +# Return whether the file at the given path is known to Git. +# +# This method outputs a message like the following to STDERR when +# returning false: +# +# "error: pathspec 'test.png' did not match any file(s) known to git. +# Did you forget to 'git add'?" +sub gitKnowsOfFile($) +{ + my $path = shift; + + `git ls-files --error-unmatch -- $path`; + my $exitStatus = exitStatus($?); + return $exitStatus == 0; +} + sub scmKnowsOfFile($) { my ($path) = @_; @@ -411,9 +402,8 @@ sub scmKnowsOfFile($) # This does not handle errors well. return 1; } elsif (isGit()) { - `git ls-files --error-unmatch -- $path`; - my $exitCode = $? >> 8; - return $exitCode == 0; + my @result = callSilently(\&gitKnowsOfFile, $path); + return $result[0]; } } diff --git a/WebKitTools/Scripts/svn-unapply b/WebKitTools/Scripts/svn-unapply index 2ef7249..e502560 100755 --- a/WebKitTools/Scripts/svn-unapply +++ b/WebKitTools/Scripts/svn-unapply @@ -149,10 +149,8 @@ sub patch($) my $deletion = 0; my $addition = 0; - # FIXME: This information should be extracted from the diff file as - # part of the parsing stage, i.e. the call to parsePatch(). - $addition = 1 if ($diffHashRef->{copiedFromPath} || $patch =~ /\n--- .+\(revision 0\)\n/ || $patch =~ /\n@@ -0,0 .* @@/); - $deletion = 1 if $patch =~ /\n@@ .* \+0,0 @@/; + $addition = 1 if ($diffHashRef->{isNew} || $diffHashRef->{copiedFromPath} || $patch =~ /\n@@ -0,0 .* @@/); + $deletion = 1 if ($diffHashRef->{isDeletion} || $patch =~ /\n@@ .* \+0,0 @@/); if (!$addition && !$deletion && !$isSvnBinary) { # Standard patch, patch tool can handle this. diff --git a/WebKitTools/Scripts/webkitdirs.pm b/WebKitTools/Scripts/webkitdirs.pm index 4fecf6b..c512009 100644 --- a/WebKitTools/Scripts/webkitdirs.pm +++ b/WebKitTools/Scripts/webkitdirs.pm @@ -891,7 +891,7 @@ sub launcherName() if (isGtk()) { return "GtkLauncher"; } elsif (isQt()) { - return "QtLauncher"; + return "QtTestBrowser"; } elsif (isWx()) { return "wxBrowser"; } elsif (isAppleWebKit()) { @@ -1239,7 +1239,7 @@ sub buildAutotoolsProject($@) my $make = 'make'; my $dir = productDir(); my $config = passedConfiguration() || configuration(); - my $prefix = $ENV{"WebKitInstallationPrefix"}; + my $prefix; my @buildArgs = (); my $makeArgs = $ENV{"WebKitMakeArguments"} || ""; @@ -1247,11 +1247,14 @@ sub buildAutotoolsProject($@) my $opt = $buildParams[$i]; if ($opt =~ /^--makeargs=(.*)/i ) { $makeArgs = $makeArgs . " " . $1; + } elsif ($opt =~ /^--prefix=(.*)/i ) { + $prefix = $1; } else { push @buildArgs, $opt; } } + $prefix = $ENV{"WebKitInstallationPrefix"} if !defined($prefix); push @buildArgs, "--prefix=" . $prefix if defined($prefix); # check if configuration is Debug @@ -1282,7 +1285,7 @@ sub buildAutotoolsProject($@) } print "Calling configure in " . $dir . "\n\n"; - print "Installation directory: $prefix\n" if(defined($prefix)); + print "Installation prefix directory: $prefix\n" if(defined($prefix)); # Make the path relative since it will appear in all -I compiler flags. # Long argument lists cause bizarre slowdowns in libtool. @@ -1311,6 +1314,8 @@ sub buildQMakeProject($@) my $qmakebin = "qmake"; # Allow override of the qmake binary from $PATH my $makeargs = ""; + my $installHeaders; + my $installLibs; for my $i (0 .. $#buildParams) { my $opt = $buildParams[$i]; if ($opt =~ /^--qmake=(.*)/i ) { @@ -1319,6 +1324,10 @@ sub buildQMakeProject($@) push @buildArgs, $1; } elsif ($opt =~ /^--makeargs=(.*)/i ) { $makeargs = $1; + } elsif ($opt =~ /^--install-headers=(.*)/i ) { + $installHeaders = $1; + } elsif ($opt =~ /^--install-libs=(.*)/i ) { + $installLibs = $1; } else { push @buildArgs, $opt; } @@ -1326,7 +1335,8 @@ sub buildQMakeProject($@) my $make = qtMakeCommand($qmakebin); my $config = configuration(); - my $prefix = $ENV{"WebKitInstallationPrefix"}; + push @buildArgs, "INSTALL_HEADERS=" . $installHeaders if defined($installHeaders); + push @buildArgs, "INSTALL_LIBS=" . $installLibs if defined($installLibs); my $dir = File::Spec->canonpath(baseProductDir()); $dir = File::Spec->catfile($dir, $config) unless isSymbian(); File::Path::mkpath($dir); @@ -1378,7 +1388,8 @@ sub buildQMakeProject($@) } print "Calling '$qmakebin @buildArgs' in " . $dir . "\n\n"; - print "Installation directory: $prefix\n" if(defined($prefix)); + print "Installation headers directory: $installHeaders\n" if(defined($installHeaders)); + print "Installation libraries directory: $installLibs\n" if(defined($installLibs)); $result = system "$qmakebin @buildArgs"; if ($result ne 0) { @@ -1427,7 +1438,8 @@ sub buildChromiumMakefile($$$) return system qw(rm -rf out); } my $config = configuration(); - my @command = ("make", "-j4", "BUILDTYPE=$config", $target); + my $numCpus = (grep /processor/, `cat /proc/cpuinfo`) || 1; + my @command = ("make", "-j$numCpus", "BUILDTYPE=$config", $target); print join(" ", @command) . "\n"; return system @command; } diff --git a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseDiff.pl b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseDiff.pl index 9f112b2..6af7da4 100644 --- a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseDiff.pl +++ b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseDiff.pl @@ -47,7 +47,7 @@ Index: Makefile all: END expectedReturn => [ -{ +[{ svnConvertedText => <<'END', # Same as input text Index: Makefile =================================================================== @@ -62,7 +62,7 @@ END indexPath => "Makefile", isSvn => 1, sourceRevision => "53052", -}, +}], undef], expectedNextLine => undef, }, @@ -84,7 +84,7 @@ Name: svn:mime-type Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA== END expectedReturn => [ -{ +[{ svnConvertedText => <<'END', # Same as input text Index: test_file.swf =================================================================== @@ -102,7 +102,7 @@ END indexPath => "test_file.swf", isBinary => 1, isSvn => 1, -}, +}], undef], expectedNextLine => undef, }, @@ -124,7 +124,7 @@ Index: Makefile all: END expectedReturn => [ -{ +[{ svnConvertedText => <<'END', # Same as input text LEADING JUNK @@ -142,7 +142,7 @@ END indexPath => "Makefile", isSvn => 1, sourceRevision => "53052", -}, +}], undef], expectedNextLine => undef, }, @@ -158,20 +158,11 @@ Index: Makefile_new +MODULES = JavaScriptCore JavaScriptGlue WebCore WebKit WebKitTools END expectedReturn => [ -{ - svnConvertedText => <<'END', # Same as input text -Index: Makefile_new -=================================================================== ---- Makefile_new (revision 53131) (from Makefile:53131) -+++ Makefile_new (working copy) -@@ -0,0 +1,1 @@ -+MODULES = JavaScriptCore JavaScriptGlue WebCore WebKit WebKitTools -END +[{ copiedFromPath => "Makefile", indexPath => "Makefile_new", - isSvn => 1, sourceRevision => "53131", -}, +}], undef], expectedNextLine => undef, }, @@ -190,7 +181,7 @@ Index: Makefile_new --- Makefile_new (revision 53131) (from Makefile:53131) END expectedReturn => [ -{ +[{ svnConvertedText => <<'END', Index: Makefile =================================================================== @@ -202,7 +193,7 @@ END indexPath => "Makefile", isSvn => 1, sourceRevision => "53131", -}, +}], "Index: Makefile_new\n"], expectedNextLine => "===================================================================\n", }, @@ -223,7 +214,7 @@ index f5d5e74..3b6aa92 100644 @@ -1,1 1,1 @@ public: END expectedReturn => [ -{ +[{ svnConvertedText => <<'END', # Same as input text Index: Makefile =================================================================== @@ -240,7 +231,7 @@ END indexPath => "Makefile", isSvn => 1, sourceRevision => "53131", -}, +}], undef], expectedNextLine => undef, }, @@ -258,7 +249,7 @@ index f5d5e74..3b6aa92 100644 @@ -1,1 1,1 @@ public: END expectedReturn => [ -{ +[{ svnConvertedText => <<'END', Index: Makefile index f5d5e74..3b6aa92 100644 @@ -268,10 +259,72 @@ index f5d5e74..3b6aa92 100644 END indexPath => "Makefile", isGit => 1, -}, +}], undef], expectedNextLine => undef, }, +{ # New test + diffName => "Git: new file", + inputText => <<'END', +diff --git a/foo.h b/foo.h +new file mode 100644 +index 0000000..3c9f114 +--- /dev/null ++++ b/foo.h +@@ -0,0 +1,34 @@ ++<html> +diff --git a/bar b/bar +index d45dd40..3494526 100644 +END + expectedReturn => [ +[{ + svnConvertedText => <<'END', +Index: foo.h +new file mode 100644 +index 0000000..3c9f114 +--- foo.h ++++ foo.h +@@ -0,0 +1,34 @@ ++<html> +END + indexPath => "foo.h", + isGit => 1, + isNew => 1, +}], +"diff --git a/bar b/bar\n"], + expectedNextLine => "index d45dd40..3494526 100644\n", +}, +{ # New test + diffName => "Git: file deletion", + inputText => <<'END', +diff --git a/foo b/foo +deleted file mode 100644 +index 1e50d1d..0000000 +--- a/foo ++++ /dev/null +@@ -1,1 +0,0 @@ +-line1 +diff --git a/bar b/bar +index d45dd40..3494526 100644 +END + expectedReturn => [ +[{ + svnConvertedText => <<'END', +Index: foo +deleted file mode 100644 +index 1e50d1d..0000000 +--- foo ++++ foo +@@ -1,1 +0,0 @@ +-line1 +END + indexPath => "foo", + isDeletion => 1, + isGit => 1, +}], +"diff --git a/bar b/bar\n"], + expectedNextLine => "index d45dd40..3494526 100644\n", +}, { # New test diffName => "Git: Git diff followed by SVN diff", # Should not recognize SVN start @@ -286,7 +339,7 @@ Index: Makefile_new --- Makefile_new (revision 53131) (from Makefile:53131) END expectedReturn => [ -{ +[{ svnConvertedText => <<'END', Index: Makefile index f5d5e74..3b6aa92 100644 @@ -299,10 +352,119 @@ Index: Makefile_new END indexPath => "Makefile", isGit => 1, -}, +}], undef], expectedNextLine => undef, }, +#### +# Git test cases: file moves (multiple return values) +## +{ + diffName => "Git: rename (with similarity index 100%)", + inputText => <<'END', +diff --git a/foo b/foo_new +similarity index 100% +rename from foo +rename to foo_new +diff --git a/bar b/bar +index d45dd40..3494526 100644 +END + expectedReturn => [ +[{ + indexPath => "foo", + isDeletion => 1, +}, +{ + copiedFromPath => "foo", + indexPath => "foo_new", +}], +"diff --git a/bar b/bar\n"], + expectedNextLine => "index d45dd40..3494526 100644\n", +}, +{ + diffName => "rename (with similarity index < 100%)", + inputText => <<'END', +diff --git a/foo b/foo_new +similarity index 99% +rename from foo +rename to foo_new +index 1e50d1d..1459d21 100644 +--- a/foo ++++ b/foo_new +@@ -15,3 +15,4 @@ release r deployment dep deploy: + line1 + line2 + line3 ++line4 +diff --git a/bar b/bar +index d45dd40..3494526 100644 +END + expectedReturn => [ +[{ + indexPath => "foo", + isDeletion => 1, +}, +{ + copiedFromPath => "foo", + indexPath => "foo_new", +}, +{ + indexPath => "foo_new", + isGit => 1, + svnConvertedText => <<'END', +Index: foo_new +similarity index 99% +rename from foo +rename to foo_new +index 1e50d1d..1459d21 100644 +--- foo_new ++++ foo_new +@@ -15,3 +15,4 @@ release r deployment dep deploy: + line1 + line2 + line3 ++line4 +END +}], +"diff --git a/bar b/bar\n"], + expectedNextLine => "index d45dd40..3494526 100644\n", +}, +{ + diffName => "rename (with executable bit change)", + inputText => <<'END', +diff --git a/foo b/foo_new +old mode 100644 +new mode 100755 +similarity index 100% +rename from foo +rename to foo_new +diff --git a/bar b/bar +index d45dd40..3494526 100644 +END + expectedReturn => [ +[{ + indexPath => "foo", + isDeletion => 1, +}, +{ + copiedFromPath => "foo", + indexPath => "foo_new", +}, +{ + indexPath => "foo_new", + isGit => 1, + svnConvertedText => <<'END', +Index: foo_new +old mode 100644 +new mode 100755 +similarity index 100% +rename from foo +rename to foo_new +END +}], +"diff --git a/bar b/bar\n"], + expectedNextLine => "index d45dd40..3494526 100644\n", +}, ); my $testCasesCount = @testCaseHashRefs; diff --git a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseGitDiffHeader.pl b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseGitDiffHeader.pl index 9e2a88d..bc0d4d4 100644 --- a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseGitDiffHeader.pl +++ b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseGitDiffHeader.pl @@ -57,7 +57,7 @@ END expectedNextLine => "-file contents\n", }, { # New test - diffName => "New file", + diffName => "new file", inputText => <<'END', diff --git a/foo.h b/foo.h new file mode 100644 @@ -77,11 +77,40 @@ index 0000000..3c9f114 +++ foo.h END indexPath => "foo.h", + isNew => 1, }, "@@ -0,0 +1,34 @@\n"], expectedNextLine => "+<html>\n", }, { # New test + diffName => "file deletion", + inputText => <<'END', +diff --git a/foo b/foo +deleted file mode 100644 +index 1e50d1d..0000000 +--- a/foo ++++ /dev/null +@@ -1,1 +0,0 @@ +-line1 +diff --git a/configure.ac b/configure.ac +index d45dd40..3494526 100644 +END + expectedReturn => [ +{ + svnConvertedText => <<'END', +Index: foo +deleted file mode 100644 +index 1e50d1d..0000000 +--- foo ++++ foo +END + indexPath => "foo", + isDeletion => 1, +}, +"@@ -1,1 +0,0 @@\n"], + expectedNextLine => "-line1\n", +}, +{ # New test diffName => "using --no-prefix", inputText => <<'END', diff --git foo.h foo.h @@ -149,7 +178,102 @@ similarity index 99% copy from foo copy to foo_new END + copiedFromPath => "foo", + indexPath => "foo_new", + isCopyWithChanges => 1, +}, +"diff --git a/bar b/bar\n"], + expectedNextLine => "index d45dd40..3494526 100644\n", +}, +{ # New test + diffName => "rename (with similarity index 100%)", + inputText => <<'END', +diff --git a/foo b/foo_new +similarity index 100% +rename from foo +rename to foo_new +diff --git a/bar b/bar +index d45dd40..3494526 100644 +END + expectedReturn => [ +{ + svnConvertedText => <<'END', +Index: foo_new +similarity index 100% +rename from foo +rename to foo_new +END + copiedFromPath => "foo", + indexPath => "foo_new", + shouldDeleteSource => 1, +}, +"diff --git a/bar b/bar\n"], + expectedNextLine => "index d45dd40..3494526 100644\n", +}, +{ # New test + diffName => "rename (with similarity index < 100%)", + inputText => <<'END', +diff --git a/foo b/foo_new +similarity index 99% +rename from foo +rename to foo_new +index 1e50d1d..1459d21 100644 +--- a/foo ++++ b/foo_new +@@ -15,3 +15,4 @@ release r deployment dep deploy: + line1 + line2 + line3 ++line4 +diff --git a/bar b/bar +index d45dd40..3494526 100644 +END + expectedReturn => [ +{ + svnConvertedText => <<'END', +Index: foo_new +similarity index 99% +rename from foo +rename to foo_new +index 1e50d1d..1459d21 100644 +--- foo_new ++++ foo_new +END + copiedFromPath => "foo", + indexPath => "foo_new", + isCopyWithChanges => 1, + shouldDeleteSource => 1, +}, +"@@ -15,3 +15,4 @@ release r deployment dep deploy:\n"], + expectedNextLine => " line1\n", +}, +{ # New test + diffName => "rename (with executable bit change)", + inputText => <<'END', +diff --git a/foo b/foo_new +old mode 100644 +new mode 100755 +similarity index 100% +rename from foo +rename to foo_new +diff --git a/bar b/bar +index d45dd40..3494526 100644 +END + expectedReturn => [ +{ + svnConvertedText => <<'END', +Index: foo_new +old mode 100644 +new mode 100755 +similarity index 100% +rename from foo +rename to foo_new +END + copiedFromPath => "foo", + executableBitDelta => 1, indexPath => "foo_new", + isCopyWithChanges => 1, + shouldDeleteSource => 1, }, "diff --git a/bar b/bar\n"], expectedNextLine => "index d45dd40..3494526 100644\n", @@ -182,6 +306,7 @@ GIT binary patch END indexPath => "foo.gif", isBinary => 1, + isNew => 1, }, "literal 7\n"], expectedNextLine => "OcmYex&reDa;sO8*F9L)B\n", @@ -211,6 +336,7 @@ GIT binary patch END indexPath => "foo.gif", isBinary => 1, + isDeletion => 1, }, "literal 0\n"], expectedNextLine => "HcmV?d00001\n", @@ -312,6 +438,7 @@ index 0000000..d03e242 END executableBitDelta => 1, indexPath => "foo", + isNew => 1, }, "@@ -0,0 +1 @@\n"], expectedNextLine => "+file contents\n", @@ -340,6 +467,7 @@ index d03e242..0000000 END executableBitDelta => -1, indexPath => "foo", + isDeletion => 1, }, "@@ -1 +0,0 @@\n"], expectedNextLine => "-file contents\n", diff --git a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parsePatch.pl b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parsePatch.pl index e6f82ca..8aae3d4 100644 --- a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parsePatch.pl +++ b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parsePatch.pl @@ -69,14 +69,6 @@ END sourceRevision => "53131", }, { - svnConvertedText => <<'END', -Index: Makefile_new -=================================================================== ---- Makefile_new (revision 53131) (from Makefile:53131) -+++ Makefile_new (working copy) -@@ -0,0 +1,1 @@ -+MODULES = JavaScriptCore JavaScriptGlue WebCore WebKit WebKitTools -END copiedFromPath => "Makefile", indexPath => "Makefile_new", sourceRevision => "53131", diff --git a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseSvnDiffHeader.pl b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseSvnDiffHeader.pl index b732889..ed8550d 100644 --- a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseSvnDiffHeader.pl +++ b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseSvnDiffHeader.pl @@ -83,6 +83,7 @@ Index: WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseDiffHeader.pl +++ WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseDiffHeader.pl (revision 0) END indexPath => "WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseDiffHeader.pl", + isNew => 1, }, "@@ -0,0 +1,262 @@\n"], expectedNextLine => "+#!/usr/bin/perl -w\n", @@ -119,7 +120,7 @@ END inputText => <<END, # No single quotes to allow interpolation of "\r" Index: index_path.py\r ===================================================================\r ---- index_path.py (revision 53048) (from copied_from_path.py:53048)\r +--- index_path.py (revision 53048)\r +++ index_path.py (working copy)\r @@ -0,0 +1,7 @@\r +# Python file...\r @@ -129,10 +130,9 @@ END svnConvertedText => <<END, # No single quotes to allow interpolation of "\r" Index: index_path.py\r ===================================================================\r ---- index_path.py (revision 53048) (from copied_from_path.py:53048)\r +--- index_path.py (revision 53048)\r +++ index_path.py (working copy)\r END - copiedFromPath => "copied_from_path.py", indexPath => "index_path.py", sourceRevision => 53048, }, diff --git a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseSvnProperty.pl b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseSvnProperty.pl new file mode 100644 index 0000000..cff7c2e --- /dev/null +++ b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseSvnProperty.pl @@ -0,0 +1,419 @@ +#!/usr/bin/perl -w +# +# Copyright (C) Research in Motion Limited 2010. All Rights Reserved. +# Copyright (C) 2010 Chris Jerdonek (chris.jerdonek@gmail.com) +# +# 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 Apple Computer, Inc. ("Apple") 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 of parseSvnProperty(). + +use strict; +use warnings; + +use Test::More; +use VCSUtils; + +my @testCaseHashRefs = ( +#### +# Simple test cases +## +{ + # New test + diffName => "simple: add svn:executable", + inputText => <<'END', +Added: svn:executable + + * +END + expectedReturn => [ +{ + name => "svn:executable", + propertyChangeDelta => 1, + value => "*", +}, +undef], + expectedNextLine => undef, +}, +{ + # New test + diffName => "simple: delete svn:executable", + inputText => <<'END', +Deleted: svn:executable + - * +END + expectedReturn => [ +{ + name => "svn:executable", + propertyChangeDelta => -1, + value => "*", +}, +undef], + expectedNextLine => undef, +}, +#### +# Using SVN 1.4 syntax +## +{ + # New test + diffName => "simple: delete svn:executable using SVN 1.4 syntax", + inputText => <<'END', +Name: svn:executable + - * +END + expectedReturn => [ +{ + name => "svn:executable", + propertyChangeDelta => -1, + value => "*", +}, +undef], + expectedNextLine => undef, +}, +{ + # New test + diffName => "simple: add svn:executable using SVN 1.4 syntax", + inputText => <<'END', +Name: svn:executable + + * +END + expectedReturn => [ +{ + name => "svn:executable", + propertyChangeDelta => 1, + value => "*", +}, +undef], + expectedNextLine => undef, +}, +#### +# Property value followed by empty line and start of next diff +## +{ + # New test + diffName => "add svn:executable, followed by empty line and start of next diff", + inputText => <<'END', +Added: svn:executable + + * + +Index: Makefile.shared +END + expectedReturn => [ +{ + name => "svn:executable", + propertyChangeDelta => 1, + value => "*", +}, +"\n"], + expectedNextLine => "Index: Makefile.shared\n", +}, +{ + # New test + diffName => "add svn:executable, followed by empty line and start of next property diff", + inputText => <<'END', +Added: svn:executable + + * + +Property changes on: Makefile.shared +END + expectedReturn => [ +{ + name => "svn:executable", + propertyChangeDelta => 1, + value => "*", +}, +"\n"], + expectedNextLine => "Property changes on: Makefile.shared\n", +}, +{ + # New test + diffName => "multi-line '+' change, followed by empty line and start of next diff", + inputText => <<'END', +Name: documentation + + A +long sentence that spans +multiple lines. + +Index: Makefile.shared +END + expectedReturn => [ +{ + name => "documentation", + propertyChangeDelta => 1, + value => "A\nlong sentence that spans\nmultiple lines.", +}, +"\n"], + expectedNextLine => "Index: Makefile.shared\n", +}, +{ + # New test + diffName => "multi-line '+' change, followed by empty line and start of next property diff", + inputText => <<'END', +Name: documentation + + A +long sentence that spans +multiple lines. + +Property changes on: Makefile.shared +END + expectedReturn => [ +{ + name => "documentation", + propertyChangeDelta => 1, + value => "A\nlong sentence that spans\nmultiple lines.", +}, +"\n"], + expectedNextLine => "Property changes on: Makefile.shared\n", +}, +#### +# Property value followed by empty line and start of binary patch +## +{ + # New test + diffName => "add svn:executable, followed by empty line and start of binary patch", + inputText => <<'END', +Added: svn:executable + + * + +Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA== +END + expectedReturn => [ +{ + name => "svn:executable", + propertyChangeDelta => 1, + value => "*", +}, +"\n"], + expectedNextLine => "Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==\n", +}, +{ + # New test + diffName => "multi-line '+' change, followed by empty line and start of binary patch", + inputText => <<'END', +Name: documentation + + A +long sentence that spans +multiple lines. + +Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA== +END + expectedReturn => [ +{ + name => "documentation", + propertyChangeDelta => 1, + value => "A\nlong sentence that spans\nmultiple lines.", +}, +"\n"], + expectedNextLine => "Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==\n", +}, +{ + # New test + diffName => "multi-line '-' change, followed by multi-line '+' change, empty line, and start of binary patch", + inputText => <<'END', +Modified: documentation + - A +long sentence that spans +multiple lines. + + Another +long sentence that spans +multiple lines. + +Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA== +END + expectedReturn => [ +{ + name => "documentation", + propertyChangeDelta => 1, + value => "Another\nlong sentence that spans\nmultiple lines.", +}, +"\n"], + expectedNextLine => "Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==\n", +}, +#### +# Successive properties +## +{ + # New test + diffName => "single-line '+' change followed by custom property with single-line '+' change", + inputText => <<'END', +Added: svn:executable + + * +Added: documentation + + A sentence. +END + expectedReturn => [ +{ + name => "svn:executable", + propertyChangeDelta => 1, + value => "*", +}, +"Added: documentation\n"], + expectedNextLine => " + A sentence.\n", +}, +{ + # New test + diffName => "multi-line '+' change, followed by svn:executable", + inputText => <<'END', +Name: documentation + + A +long sentence that spans +multiple lines. +Name: svn:executable + + * +END + expectedReturn => [ +{ + name => "documentation", + propertyChangeDelta => 1, + value => "A\nlong sentence that spans\nmultiple lines.", +}, +"Name: svn:executable\n"], + expectedNextLine => " + *\n", +}, +{ + # New test + diffName => "multi-line '-' change, followed by multi-line '+' change and add svn:executable", + inputText => <<'END', +Modified: documentation + - A +long sentence that spans +multiple lines. + + Another +long sentence that spans +multiple lines. +Added: svn:executable + + * +END + expectedReturn => [ +{ + name => "documentation", + propertyChangeDelta => 1, + value => "Another\nlong sentence that spans\nmultiple lines.", +}, +"Added: svn:executable\n"], + expectedNextLine => " + *\n", +}, +#### +# Property values with trailing new lines. +## +# FIXME: We do not support property values with trailing new lines, since it is difficult to +# disambiguate them from the empty line that preceeds the contents of a binary patch as +# in the test case (above): "multi-line '+' change, followed by empty line and start of binary patch". +{ + # New test + diffName => "single-line '+' with trailing new line", + inputText => <<'END', +Added: documentation + + A sentence. + +END + expectedReturn => [ +{ + name => "documentation", + propertyChangeDelta => 1, + value => "A sentence.", +}, +"\n"], + expectedNextLine => undef, +}, +{ + # New test + diffName => "single-line '+' with trailing new line, followed by empty line and start of binary patch", + inputText => <<'END', +Added: documentation + + A sentence. + + +Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA== +END + expectedReturn => [ +{ + name => "documentation", + propertyChangeDelta => 1, + value => "A sentence.", +}, +"\n"], + expectedNextLine => "\n", +}, +{ + # New test + diffName => "single-line '-' change with trailing new line, and single-line '+' change", + inputText => <<'END', +Modified: documentation + - A long sentence. + + + A sentence. +END + expectedReturn => [ +{ + name => "documentation", + propertyChangeDelta => -1, # Since we only interpret the '-' property. + value => "A long sentence.", +}, +"\n"], + expectedNextLine => " + A sentence.\n", +}, +{ + # New test + diffName => "multi-line '-' change with trailing new line, and multi-line '+' change", + inputText => <<'END', +Modified: documentation + - A +long sentence that spans +multiple lines. + + + Another +long sentence that spans +multiple lines. +END + expectedReturn => [ +{ + name => "documentation", + propertyChangeDelta => -1, # Since we only interpret the '-' property. + value => "A\nlong sentence that spans\nmultiple lines.", +}, +"\n"], + expectedNextLine => " + Another\n", +}, +); + +my $testCasesCount = @testCaseHashRefs; +plan(tests => 2 * $testCasesCount); # Total number of assertions. + +foreach my $testCase (@testCaseHashRefs) { + my $testNameStart = "parseSvnProperty(): $testCase->{diffName}: comparing"; + + my $fileHandle; + open($fileHandle, "<", \$testCase->{inputText}); + my $line = <$fileHandle>; + + my @got = VCSUtils::parseSvnProperty($fileHandle, $line); + my $expectedReturn = $testCase->{expectedReturn}; + + is_deeply(\@got, $expectedReturn, "$testNameStart return value."); + + my $gotNextLine = <$fileHandle>; + is($gotNextLine, $testCase->{expectedNextLine}, "$testNameStart next read line."); +} diff --git a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseSvnPropertyValue.pl b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseSvnPropertyValue.pl new file mode 100644 index 0000000..5c79862 --- /dev/null +++ b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/parseSvnPropertyValue.pl @@ -0,0 +1,149 @@ +#!/usr/bin/perl -w +# +# Copyright (C) Research in Motion Limited 2010. All Rights Reserved. +# Copyright (C) 2010 Chris Jerdonek (chris.jerdonek@gmail.com) +# +# 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 Apple Computer, Inc. ("Apple") 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 of parseSvnPropertyValue(). + +use strict; +use warnings; + +use Test::More; +use VCSUtils; + +my @testCaseHashRefs = ( +{ + # New test + diffName => "singe-line '+' change", + inputText => <<'END', + + * +END + expectedReturn => ["*", undef], + expectedNextLine => undef, +}, +{ + # New test + diffName => "single-line '-' change", + inputText => <<'END', + - * +END + expectedReturn => ["*", undef], + expectedNextLine => undef, +}, +{ + # New test + diffName => "single-line '-' change followed by empty line", + inputText => <<'END', + - * + +END + expectedReturn => ["*", "\n"], + expectedNextLine => undef, +}, +{ + # New test + diffName => "single-line '-' change followed by the next property", + inputText => <<'END', + - * +Deleted: svn:executable +END + expectedReturn => ["*", "Deleted: svn:executable\n"], + expectedNextLine => undef, +}, +{ + # New test + diffName => "multi-line '+' change and start of binary patch", + inputText => <<'END', + + A +long sentence that spans +multiple lines. + +Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA== +END + expectedReturn => ["A\nlong sentence that spans\nmultiple lines.", "\n"], + expectedNextLine => "Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==\n", +}, +{ + # New test + diffName => "multi-line '-' change followed by '+' single-line change", + inputText => <<'END', + - A +long sentence that spans +multiple lines. + + A single-line. +END + expectedReturn => ["A\nlong sentence that spans\nmultiple lines.", " + A single-line.\n"], + expectedNextLine => undef, +}, +{ + # New test + diffName => "multi-line '-' change followed by the next property", + inputText => <<'END', + - A +long sentence that spans +multiple lines. +Added: svn:executable +END + expectedReturn => ["A\nlong sentence that spans\nmultiple lines.", "Added: svn:executable\n"], + expectedNextLine => undef, +}, +{ + # New test + diffName => "multi-line '-' change followed by '+' multi-line change", + inputText => <<'END', + - A +long sentence that spans +multiple lines. + + Another +long sentence that spans +multiple lines. +END + expectedReturn => ["A\nlong sentence that spans\nmultiple lines.", " + Another\n"], + expectedNextLine => "long sentence that spans\n", +}, +); + +my $testCasesCount = @testCaseHashRefs; +plan(tests => 2 * $testCasesCount); # Total number of assertions. + +foreach my $testCase (@testCaseHashRefs) { + my $testNameStart = "parseSvnPropertyValue(): $testCase->{diffName}: comparing"; + + my $fileHandle; + open($fileHandle, "<", \$testCase->{inputText}); + my $line = <$fileHandle>; + + my @got = VCSUtils::parseSvnPropertyValue($fileHandle, $line); + my $expectedReturn = $testCase->{expectedReturn}; + + is_deeply(\@got, $expectedReturn, "$testNameStart return value."); + + my $gotNextLine = <$fileHandle>; + is($gotNextLine, $testCase->{expectedNextLine}, "$testNameStart next read line."); +} diff --git a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/runPatchCommand.pl b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/runPatchCommand.pl index 8111def..5acc517 100644 --- a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/runPatchCommand.pl +++ b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/runPatchCommand.pl @@ -33,19 +33,6 @@ use Test::Simple tests => 4; use VCSUtils; -# Call a function while suppressing STDERR. -sub callSilently($@) { - my ($func, @args) = @_; - - open(OLDERR, ">&STDERR"); - close(STDERR); - my @returnValue = &$func(@args); - open(STDERR, ">&OLDERR"); - close(OLDERR); # FIXME: Is this necessary? - - return @returnValue; -} - # New test $title = "runPatchCommand: Unsuccessful patch, forcing."; diff --git a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/setChangeLogDateAndReviewer.pl b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/setChangeLogDateAndReviewer.pl new file mode 100644 index 0000000..076d88c --- /dev/null +++ b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/setChangeLogDateAndReviewer.pl @@ -0,0 +1,128 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# +# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 of setChangeLogDateAndReviewer(). + +use strict; +use warnings; + +use Test::More; +use VCSUtils; + +my @testCaseHashRefs = ( +{ + testName => "reviewer defined and \"NOBODY (OOPS!)\" in leading junk", + reviewer => "John Doe", + epochTime => 1273414321, + patch => <<'END', +Subject: [PATCH] + +Reviewed by NOBODY (OOPS!). + +diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog +--- a/WebCore/ChangeLog ++++ b/WebCore/ChangeLog +@@ -1,3 +1,15 @@ ++2010-05-08 Chris Jerdonek <cjerdonek@webkit.org> ++ ++ Reviewed by NOBODY (OOPS!). ++ + 2010-05-08 Chris Jerdonek <cjerdonek@webkit.org> + + Reviewed by Jane Doe. +END + expectedReturn => <<'END', +Subject: [PATCH] + +Reviewed by NOBODY (OOPS!). + +diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog +--- a/WebCore/ChangeLog ++++ b/WebCore/ChangeLog +@@ -1,3 +1,15 @@ ++2010-05-09 Chris Jerdonek <cjerdonek@webkit.org> ++ ++ Reviewed by John Doe. ++ + 2010-05-08 Chris Jerdonek <cjerdonek@webkit.org> + + Reviewed by Jane Doe. +END +}, +{ + testName => "reviewer not defined and \"NOBODY (OOPS!)\" in leading junk", + reviewer => undef, + epochTime => 1273414321, + patch => <<'END', +Subject: [PATCH] + +Reviewed by NOBODY (OOPS!). + +diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog +--- a/WebCore/ChangeLog ++++ b/WebCore/ChangeLog +@@ -1,3 +1,15 @@ ++2010-05-08 Chris Jerdonek <cjerdonek@webkit.org> ++ ++ Reviewed by NOBODY (OOPS!). ++ + 2010-05-08 Chris Jerdonek <cjerdonek@webkit.org> + + Reviewed by Jane Doe. +END + expectedReturn => <<'END', +Subject: [PATCH] + +Reviewed by NOBODY (OOPS!). + +diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog +--- a/WebCore/ChangeLog ++++ b/WebCore/ChangeLog +@@ -1,3 +1,15 @@ ++2010-05-09 Chris Jerdonek <cjerdonek@webkit.org> ++ ++ Reviewed by NOBODY (OOPS!). ++ + 2010-05-08 Chris Jerdonek <cjerdonek@webkit.org> + + Reviewed by Jane Doe. +END +}, +); + +my $testCasesCount = @testCaseHashRefs; +plan(tests => 1 * $testCasesCount); # Total number of assertions. + +foreach my $testCase (@testCaseHashRefs) { + my $testNameStart = "setChangeLogDateAndReviewer(): $testCase->{testName}: comparing"; + + my $patch = $testCase->{patch}; + my $reviewer = $testCase->{reviewer}; + my $epochTime = $testCase->{epochTime}; + + my $got = VCSUtils::setChangeLogDateAndReviewer($patch, $reviewer, $epochTime); + my $expectedReturn = $testCase->{expectedReturn}; + + is($got, $expectedReturn, "$testNameStart return value."); +} 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/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/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) |