summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts/webkitpy
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2011-05-05 14:36:32 +0100
committerBen Murdoch <benm@google.com>2011-05-10 15:38:30 +0100
commitf05b935882198ccf7d81675736e3aeb089c5113a (patch)
tree4ea0ca838d9ef1b15cf17ddb3928efb427c7e5a1 /WebKitTools/Scripts/webkitpy
parent60fbdcc62bced8db2cb1fd233cc4d1e4ea17db1b (diff)
downloadexternal_webkit-f05b935882198ccf7d81675736e3aeb089c5113a.zip
external_webkit-f05b935882198ccf7d81675736e3aeb089c5113a.tar.gz
external_webkit-f05b935882198ccf7d81675736e3aeb089c5113a.tar.bz2
Merge WebKit at r74534: Initial merge by git.
Change-Id: I6ccd1154fa1b19c2ec2a66878eb675738735f1eb
Diffstat (limited to 'WebKitTools/Scripts/webkitpy')
-rw-r--r--WebKitTools/Scripts/webkitpy/__init__.py13
-rw-r--r--WebKitTools/Scripts/webkitpy/common/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/common/array_stream.py66
-rw-r--r--WebKitTools/Scripts/webkitpy/common/array_stream_unittest.py78
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/__init__.py3
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/api.py160
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/api_unittest.py196
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/changelog.py195
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/changelog_unittest.py203
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/commitinfo.py93
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/commitinfo_unittest.py61
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/diff_parser.py181
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/diff_parser_unittest.py146
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/scm.py915
-rw-r--r--WebKitTools/Scripts/webkitpy/common/checkout/scm_unittest.py1291
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/build.py138
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/build_unittest.py64
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/committers.py331
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/committers_unittest.py72
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/committervalidator.py120
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/committervalidator_unittest.py43
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/irc.py31
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/ports.py249
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/ports_unittest.py76
-rw-r--r--WebKitTools/Scripts/webkitpy/common/memoized.py55
-rw-r--r--WebKitTools/Scripts/webkitpy/common/memoized_unittest.py65
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/bugzilla/__init__.py8
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/bugzilla/attachment.py114
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/bugzilla/bug.py105
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/bugzilla/bug_unittest.py40
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py674
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/bugzilla/bugzilla_unittest.py342
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/buildbot.py452
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/buildbot_unittest.py410
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/credentials.py155
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/credentials_unittest.py176
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/failuremap.py84
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/failuremap_unittest.py76
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/irc/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/irc/ircbot.py91
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy.py62
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy_unittest.py43
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/layouttestresults.py90
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/layouttestresults_unittest.py77
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/networktransaction.py72
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/networktransaction_unittest.py93
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/regressionwindow.py51
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/statusserver.py160
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/statusserver_unittest.py43
-rw-r--r--WebKitTools/Scripts/webkitpy/common/newstringio.py40
-rw-r--r--WebKitTools/Scripts/webkitpy/common/newstringio_unittest.py46
-rw-r--r--WebKitTools/Scripts/webkitpy/common/prettypatch.py66
-rw-r--r--WebKitTools/Scripts/webkitpy/common/prettypatch_unittest.py70
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/__init__.py1
-rwxr-xr-xWebKitTools/Scripts/webkitpy/common/system/autoinstall.py517
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/deprecated_logging.py91
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/deprecated_logging_unittest.py60
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/executive.py399
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/executive_mock.py59
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py151
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/file_lock.py83
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/file_lock_unittest.py61
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/filesystem.py117
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/filesystem_mock.py80
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/filesystem_unittest.py157
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/fileutils.py33
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/logtesting.py258
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/logutils.py207
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/logutils_unittest.py142
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/ospath.py83
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/ospath_unittest.py62
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/outputcapture.py86
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/path.py138
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/path_unittest.py105
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/user.py143
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/user_unittest.py109
-rw-r--r--WebKitTools/Scripts/webkitpy/common/thread/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/common/thread/messagepump.py59
-rw-r--r--WebKitTools/Scripts/webkitpy/common/thread/messagepump_unittest.py83
-rw-r--r--WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue.py54
-rw-r--r--WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue_unittest.py53
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py231
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py210
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/__init__.py0
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py569
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py212
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py661
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator_unittest.py205
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/message_broker.py197
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/message_broker_unittest.py183
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream.py146
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream_unittest.py115
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py553
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing_unittest.py606
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py843
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations_unittest.py313
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py282
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures_unittest.py84
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_output.py56
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results.py61
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results_unittest.py52
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results_uploader.py107
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/__init__.py32
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/apache_http_server.py230
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/base.py861
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py315
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py555
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu.py154
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu_unittest.py75
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py190
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py176
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac_unittest.py40
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py186
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py174
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win_unittest.py74
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/config.py169
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/config_mock.py50
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/config_standalone.py70
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/config_unittest.py201
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py132
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/factory.py113
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py188
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome.py122
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome_unittest.py103
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/gtk.py83
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock.py132
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock_unittest.py111
-rwxr-xr-xWebKitTools/Scripts/webkitpy/layout_tests/port/http_server.py233
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/http_server_base.py81
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/httpd2.pem41
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf90
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/mac.py152
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/mac_unittest.py81
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/port_testcase.py97
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/qt.py119
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/server_process.py225
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/test.py312
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/test_files.py128
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/test_files_unittest.py75
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py504
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/webkit_unittest.py68
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py257
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/win.py75
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py966
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests_unittest.py157
-rwxr-xr-xWebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py1634
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py540
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/test_types/__init__.py0
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/test_types/image_diff.py146
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/test_types/test_type_base.py223
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/test_types/test_type_base_unittest.py47
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/test_types/text_diff.py91
-rwxr-xr-xWebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests.py160
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests_unittest.py102
-rw-r--r--WebKitTools/Scripts/webkitpy/python24/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/python24/versioning.py133
-rw-r--r--WebKitTools/Scripts/webkitpy/python24/versioning_unittest.py134
-rw-r--r--WebKitTools/Scripts/webkitpy/style/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checker.py739
-rwxr-xr-xWebKitTools/Scripts/webkitpy/style/checker_unittest.py779
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/common.py74
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/common_unittest.py124
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/cpp.py3126
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py3923
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/python.py56
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/python_unittest.py62
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/python_unittest_input.py2
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/test_expectations.py123
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/test_expectations_unittest.py168
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/text.py51
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/text_unittest.py94
-rw-r--r--WebKitTools/Scripts/webkitpy/style/error_handlers.py159
-rw-r--r--WebKitTools/Scripts/webkitpy/style/error_handlers_unittest.py187
-rw-r--r--WebKitTools/Scripts/webkitpy/style/filereader.py162
-rw-r--r--WebKitTools/Scripts/webkitpy/style/filereader_unittest.py166
-rw-r--r--WebKitTools/Scripts/webkitpy/style/filter.py278
-rw-r--r--WebKitTools/Scripts/webkitpy/style/filter_unittest.py256
-rw-r--r--WebKitTools/Scripts/webkitpy/style/main.py130
-rw-r--r--WebKitTools/Scripts/webkitpy/style/main_unittest.py124
-rw-r--r--WebKitTools/Scripts/webkitpy/style/optparser.py457
-rw-r--r--WebKitTools/Scripts/webkitpy/style/optparser_unittest.py258
-rw-r--r--WebKitTools/Scripts/webkitpy/style/patchreader.py66
-rw-r--r--WebKitTools/Scripts/webkitpy/style/patchreader_unittest.py92
-rw-r--r--WebKitTools/Scripts/webkitpy/style_references.py74
-rw-r--r--WebKitTools/Scripts/webkitpy/test/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/test/cat.py42
-rw-r--r--WebKitTools/Scripts/webkitpy/test/cat_unittest.py52
-rw-r--r--WebKitTools/Scripts/webkitpy/test/echo.py52
-rw-r--r--WebKitTools/Scripts/webkitpy/test/echo_unittest.py64
-rw-r--r--WebKitTools/Scripts/webkitpy/test/main.py140
-rw-r--r--WebKitTools/Scripts/webkitpy/test/skip.py52
-rw-r--r--WebKitTools/Scripts/webkitpy/test/skip_unittest.py77
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/BeautifulSoup.py2000
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/__init__.py98
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/mock.py309
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/LICENSE.txt19
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/README.txt11
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/__init__.py287
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/_speedups.c215
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/decoder.py273
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/encoder.py371
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/jsonfilter.py40
-rw-r--r--WebKitTools/Scripts/webkitpy/thirdparty/simplejson/scanner.py63
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/__init__.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask.py196
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py204
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/feeders.py90
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/feeders_unittest.py70
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/irc_command.py109
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/irc_command_unittest.py38
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py165
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py209
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/sheriff.py91
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py90
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot.py83
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py95
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/__init__.py12
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/abstractsequencedcommand.py43
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/commandtest.py48
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html180
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/loupe.js144
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css292
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js498
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/queue.js158
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js94
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/download.py394
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py190
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py182
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py132
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/openbugs.py63
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/openbugs_unittest.py50
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/prettydiff.py38
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queries.py393
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py90
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queues.py417
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py378
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py95
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/rebaseline.py114
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py38
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver.py268
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver_unittest.py88
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py106
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py57
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/stepsequence.py83
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/upload.py483
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py117
-rwxr-xr-xWebKitTools/Scripts/webkitpy/tool/comments.py43
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/grammar.py54
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/grammar_unittest.py41
-rwxr-xr-xWebKitTools/Scripts/webkitpy/tool/main.py133
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/mocktool.py676
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/mocktool_unittest.py59
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/multicommandtool.py314
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/multicommandtool_unittest.py177
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/__init__.py59
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/abstractstep.py79
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/applypatch.py43
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/applypatchwithlocalcommit.py43
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/build.py54
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/checkstyle.py66
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py52
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectorywithlocalcommits.py34
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/closebug.py51
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff.py58
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py40
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/closepatch.py36
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/commit.py81
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/confirmdiff.py77
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/createbug.py52
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/editchangelog.py38
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/ensurebuildersaregreen.py48
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/ensurelocalcommitifneeded.py43
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/metastep.py54
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/obsoletepatches.py51
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/options.py59
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/postdiff.py50
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/postdiffforcommit.py39
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/postdiffforrevert.py49
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/preparechangelog.py77
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/preparechangelog_unittest.py54
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py44
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/promptforbugortitle.py45
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/reopenbugafterrollout.py44
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/revertrevision.py35
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/runtests.py81
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py92
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers.py51
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers_unittest.py45
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/update.py45
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittest.py48
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py72
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer.py71
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer_unittest.py57
298 files changed, 0 insertions, 55175 deletions
diff --git a/WebKitTools/Scripts/webkitpy/__init__.py b/WebKitTools/Scripts/webkitpy/__init__.py
deleted file mode 100644
index b376bf2..0000000
--- a/WebKitTools/Scripts/webkitpy/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Required for Python to search this directory for module files
-
-# Keep this file free of any code or import statements that could
-# cause either an error to occur or a log message to be logged.
-# This ensures that calling code can import initialization code from
-# webkitpy before any errors or log messages due to code in this file.
-# Initialization code can include things like version-checking code and
-# logging configuration code.
-#
-# We do not execute any version-checking code or logging configuration
-# code in this file so that callers can opt-in as they want. This also
-# allows different callers to choose different initialization code,
-# as necessary.
diff --git a/WebKitTools/Scripts/webkitpy/common/__init__.py b/WebKitTools/Scripts/webkitpy/common/__init__.py
deleted file mode 100644
index ef65bee..0000000
--- a/WebKitTools/Scripts/webkitpy/common/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/common/array_stream.py b/WebKitTools/Scripts/webkitpy/common/array_stream.py
deleted file mode 100644
index e425d02..0000000
--- a/WebKitTools/Scripts/webkitpy/common/array_stream.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/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.
-
-"""Package that private an array-based implementation of a stream."""
-
-
-class ArrayStream(object):
- """Simple class that implmements a stream interface on top of an array.
-
- This is used primarily by unit test classes to mock output streams. It
- performs a similar function to StringIO, but (a) it is write-only, and
- (b) it can be used to retrieve each individual write(); StringIO
- concatenates all of the writes together.
- """
-
- def __init__(self):
- self._contents = []
-
- def write(self, msg):
- """Implement stream.write() by appending to the stream's contents."""
- self._contents.append(msg)
-
- def get(self):
- """Return the contents of a stream (as an array)."""
- return self._contents
-
- def reset(self):
- """Empty the stream."""
- self._contents = []
-
- def empty(self):
- """Return whether the stream is empty."""
- return (len(self._contents) == 0)
-
- def flush(self):
- """Flush the stream (a no-op implemented for compatibility)."""
- pass
-
- def __repr__(self):
- return '<ArrayStream: ' + str(self._contents) + '>'
diff --git a/WebKitTools/Scripts/webkitpy/common/array_stream_unittest.py b/WebKitTools/Scripts/webkitpy/common/array_stream_unittest.py
deleted file mode 100644
index 1a9b34a..0000000
--- a/WebKitTools/Scripts/webkitpy/common/array_stream_unittest.py
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/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 array_stream.py."""
-
-import pdb
-import unittest
-
-from webkitpy.common.array_stream import ArrayStream
-
-
-class ArrayStreamTest(unittest.TestCase):
- def assertEmpty(self, a_stream):
- self.assertTrue(a_stream.empty())
-
- def assertNotEmpty(self, a_stream):
- self.assertFalse(a_stream.empty())
-
- def assertContentsMatch(self, a_stream, contents):
- self.assertEquals(a_stream.get(), contents)
-
- def test_basics(self):
- a = ArrayStream()
- self.assertEmpty(a)
- self.assertContentsMatch(a, [])
-
- a.flush()
- self.assertEmpty(a)
- self.assertContentsMatch(a, [])
-
- a.write("foo")
- a.write("bar")
- self.assertNotEmpty(a)
- self.assertContentsMatch(a, ["foo", "bar"])
-
- a.flush()
- self.assertNotEmpty(a)
- self.assertContentsMatch(a, ["foo", "bar"])
-
- a.reset()
- self.assertEmpty(a)
- self.assertContentsMatch(a, [])
-
- self.assertEquals(str(a), "<ArrayStream: []>")
-
- a.write("foo")
- self.assertNotEmpty(a)
- self.assertContentsMatch(a, ["foo"])
- self.assertEquals(str(a), "<ArrayStream: ['foo']>")
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/__init__.py b/WebKitTools/Scripts/webkitpy/common/checkout/__init__.py
deleted file mode 100644
index 597dcbd..0000000
--- a/WebKitTools/Scripts/webkitpy/common/checkout/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# Required for Python to search this directory for module files
-
-from api import Checkout
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/api.py b/WebKitTools/Scripts/webkitpy/common/checkout/api.py
deleted file mode 100644
index dbe1e84..0000000
--- a/WebKitTools/Scripts/webkitpy/common/checkout/api.py
+++ /dev/null
@@ -1,160 +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.
-
-import os
-import StringIO
-
-from webkitpy.common.checkout.changelog import ChangeLog
-from webkitpy.common.checkout.commitinfo import CommitInfo
-from webkitpy.common.checkout.scm import CommitMessage
-from webkitpy.common.memoized import memoized
-from webkitpy.common.net.bugzilla import parse_bug_id
-from webkitpy.common.system.executive import Executive, run_command, ScriptError
-from webkitpy.common.system.deprecated_logging import log
-
-
-# This class represents the WebKit-specific parts of the checkout (like
-# ChangeLogs).
-# FIXME: Move a bunch of ChangeLog-specific processing from SCM to this object.
-class Checkout(object):
- def __init__(self, scm):
- self._scm = scm
-
- def _is_path_to_changelog(self, path):
- return os.path.basename(path) == "ChangeLog"
-
- def _latest_entry_for_changelog_at_revision(self, changelog_path, revision):
- changelog_contents = self._scm.contents_at_revision(changelog_path, revision)
- # contents_at_revision returns a byte array (str()), but we know
- # that ChangeLog files are utf-8. parse_latest_entry_from_file
- # expects a file-like object which vends unicode(), so we decode here.
- changelog_file = StringIO.StringIO(changelog_contents.decode("utf-8"))
- return ChangeLog.parse_latest_entry_from_file(changelog_file)
-
- def changelog_entries_for_revision(self, revision):
- changed_files = self._scm.changed_files_for_revision(revision)
- return [self._latest_entry_for_changelog_at_revision(path, revision) for path in changed_files if self._is_path_to_changelog(path)]
-
- @memoized
- def commit_info_for_revision(self, revision):
- committer_email = self._scm.committer_email_for_revision(revision)
- changelog_entries = self.changelog_entries_for_revision(revision)
- # Assume for now that the first entry has everything we need:
- # FIXME: This will throw an exception if there were no ChangeLogs.
- if not len(changelog_entries):
- return None
- changelog_entry = changelog_entries[0]
- changelog_data = {
- "bug_id": parse_bug_id(changelog_entry.contents()),
- "author_name": changelog_entry.author_name(),
- "author_email": changelog_entry.author_email(),
- "author": changelog_entry.author(),
- "reviewer_text": changelog_entry.reviewer_text(),
- "reviewer": changelog_entry.reviewer(),
- }
- # We could pass the changelog_entry instead of a dictionary here, but that makes
- # mocking slightly more involved, and would make aggregating data from multiple
- # entries more difficult to wire in if we need to do that in the future.
- return CommitInfo(revision, committer_email, changelog_data)
-
- def bug_id_for_revision(self, revision):
- return self.commit_info_for_revision(revision).bug_id()
-
- def _modified_files_matching_predicate(self, git_commit, predicate, changed_files=None):
- # SCM returns paths relative to scm.checkout_root
- # Callers (especially those using the ChangeLog class) may
- # expect absolute paths, so this method returns absolute paths.
- if not changed_files:
- changed_files = self._scm.changed_files(git_commit)
- absolute_paths = [os.path.join(self._scm.checkout_root, path) for path in changed_files]
- return [path for path in absolute_paths if predicate(path)]
-
- def modified_changelogs(self, git_commit, changed_files=None):
- return self._modified_files_matching_predicate(git_commit, self._is_path_to_changelog, changed_files=changed_files)
-
- def modified_non_changelogs(self, git_commit, changed_files=None):
- return self._modified_files_matching_predicate(git_commit, lambda path: not self._is_path_to_changelog(path), changed_files=changed_files)
-
- def commit_message_for_this_commit(self, git_commit, changed_files=None):
- changelog_paths = self.modified_changelogs(git_commit, changed_files)
- if not len(changelog_paths):
- raise ScriptError(message="Found no modified ChangeLogs, cannot create a commit message.\n"
- "All changes require a ChangeLog. See:\n"
- "http://webkit.org/coding/contributing.html")
-
- changelog_messages = []
- for changelog_path in changelog_paths:
- log("Parsing ChangeLog: %s" % changelog_path)
- changelog_entry = ChangeLog(changelog_path).latest_entry()
- if not changelog_entry:
- raise ScriptError(message="Failed to parse ChangeLog: %s" % os.path.abspath(changelog_path))
- changelog_messages.append(changelog_entry.contents())
-
- # FIXME: We should sort and label the ChangeLog messages like commit-log-editor does.
- return CommitMessage("".join(changelog_messages).splitlines())
-
- def recent_commit_infos_for_files(self, paths):
- revisions = set(sum(map(self._scm.revisions_changing_file, paths), []))
- return set(map(self.commit_info_for_revision, revisions))
-
- def suggested_reviewers(self, git_commit, changed_files=None):
- changed_files = self.modified_non_changelogs(git_commit, changed_files)
- commit_infos = self.recent_commit_infos_for_files(changed_files)
- reviewers = [commit_info.reviewer() for commit_info in commit_infos if commit_info.reviewer()]
- reviewers.extend([commit_info.author() for commit_info in commit_infos if commit_info.author() and commit_info.author().can_review])
- return sorted(set(reviewers))
-
- def bug_id_for_this_commit(self, git_commit, changed_files=None):
- try:
- return parse_bug_id(self.commit_message_for_this_commit(git_commit, changed_files).message())
- except ScriptError, e:
- pass # We might not have ChangeLogs.
-
- def apply_patch(self, patch, force=False):
- # It's possible that the patch was not made from the root directory.
- # We should detect and handle that case.
- # FIXME: Move _scm.script_path here once we get rid of all the dependencies.
- args = [self._scm.script_path('svn-apply')]
- if patch.reviewer():
- args += ['--reviewer', patch.reviewer().full_name]
- if force:
- args.append('--force')
- run_command(args, input=patch.contents())
-
- def apply_reverse_diff(self, revision):
- self._scm.apply_reverse_diff(revision)
-
- # We revert the ChangeLogs because removing lines from a ChangeLog
- # doesn't make sense. ChangeLogs are append only.
- changelog_paths = self.modified_changelogs(git_commit=None)
- if len(changelog_paths):
- self._scm.revert_files(changelog_paths)
-
- conflicts = self._scm.conflicted_files()
- if len(conflicts):
- raise ScriptError(message="Failed to apply reverse diff for revision %s because of the following conflicts:\n%s" % (revision, "\n".join(conflicts)))
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/api_unittest.py b/WebKitTools/Scripts/webkitpy/common/checkout/api_unittest.py
deleted file mode 100644
index 1f97abd..0000000
--- a/WebKitTools/Scripts/webkitpy/common/checkout/api_unittest.py
+++ /dev/null
@@ -1,196 +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.
-
-from __future__ import with_statement
-
-import codecs
-import os
-import shutil
-import tempfile
-import unittest
-
-from webkitpy.common.checkout.api import Checkout
-from webkitpy.common.checkout.changelog import ChangeLogEntry
-from webkitpy.common.checkout.scm import detect_scm_system, CommitMessage
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.thirdparty.mock import Mock
-
-
-# FIXME: Copied from scm_unittest.py
-def write_into_file_at_path(file_path, contents, encoding="utf-8"):
- with codecs.open(file_path, "w", encoding) as file:
- file.write(contents)
-
-
-_changelog1entry1 = u"""2010-03-25 Tor Arne Vestb\u00f8 <vestbo@webkit.org>
-
- Unreviewed build fix to un-break webkit-patch land.
-
- Move commit_message_for_this_commit from scm to checkout
- https://bugs.webkit.org/show_bug.cgi?id=36629
-
- * Scripts/webkitpy/common/checkout/api.py: import scm.CommitMessage
-"""
-_changelog1entry2 = u"""2010-03-25 Adam Barth <abarth@webkit.org>
-
- Reviewed by Eric Seidel.
-
- Move commit_message_for_this_commit from scm to checkout
- https://bugs.webkit.org/show_bug.cgi?id=36629
-
- * Scripts/webkitpy/common/checkout/api.py:
-"""
-_changelog1 = u"\n".join([_changelog1entry1, _changelog1entry2])
-_changelog2 = u"""2010-03-25 Tor Arne Vestb\u00f8 <vestbo@webkit.org>
-
- Unreviewed build fix to un-break webkit-patch land.
-
- Second part of this complicated change.
-
- * Path/To/Complicated/File: Added.
-
-2010-03-25 Adam Barth <abarth@webkit.org>
-
- Reviewed by Eric Seidel.
-
- Filler change.
-"""
-
-class CommitMessageForThisCommitTest(unittest.TestCase):
- expected_commit_message = u"""2010-03-25 Tor Arne Vestb\u00f8 <vestbo@webkit.org>
-
- Unreviewed build fix to un-break webkit-patch land.
-
- Move commit_message_for_this_commit from scm to checkout
- https://bugs.webkit.org/show_bug.cgi?id=36629
-
- * Scripts/webkitpy/common/checkout/api.py: import scm.CommitMessage
-2010-03-25 Tor Arne Vestb\u00f8 <vestbo@webkit.org>
-
- Unreviewed build fix to un-break webkit-patch land.
-
- Second part of this complicated change.
-
- * Path/To/Complicated/File: Added.
-"""
-
- def setUp(self):
- self.temp_dir = tempfile.mkdtemp(suffix="changelogs")
- self.old_cwd = os.getcwd()
- os.chdir(self.temp_dir)
- write_into_file_at_path("ChangeLog1", _changelog1)
- write_into_file_at_path("ChangeLog2", _changelog2)
-
- def tearDown(self):
- shutil.rmtree(self.temp_dir, ignore_errors=True)
- os.chdir(self.old_cwd)
-
- # FIXME: This should not need to touch the file system, however
- # ChangeLog is difficult to mock at current.
- def test_commit_message_for_this_commit(self):
- checkout = Checkout(None)
- checkout.modified_changelogs = lambda git_commit, changed_files=None: ["ChangeLog1", "ChangeLog2"]
- output = OutputCapture()
- expected_stderr = "Parsing ChangeLog: ChangeLog1\nParsing ChangeLog: ChangeLog2\n"
- commit_message = output.assert_outputs(self, checkout.commit_message_for_this_commit,
- kwargs={"git_commit": None}, expected_stderr=expected_stderr)
- self.assertEqual(commit_message.message(), self.expected_commit_message)
-
-
-class CheckoutTest(unittest.TestCase):
- def test_latest_entry_for_changelog_at_revision(self):
- scm = Mock()
- def mock_contents_at_revision(changelog_path, revision):
- self.assertEqual(changelog_path, "foo")
- self.assertEqual(revision, "bar")
- # contents_at_revision is expected to return a byte array (str)
- # so we encode our unicode ChangeLog down to a utf-8 stream.
- return _changelog1.encode("utf-8")
- scm.contents_at_revision = mock_contents_at_revision
- checkout = Checkout(scm)
- entry = checkout._latest_entry_for_changelog_at_revision("foo", "bar")
- self.assertEqual(entry.contents(), _changelog1entry1)
-
- def test_commit_info_for_revision(self):
- scm = Mock()
- scm.committer_email_for_revision = lambda revision: "committer@example.com"
- checkout = Checkout(scm)
- checkout.changelog_entries_for_revision = lambda revision: [ChangeLogEntry(_changelog1entry1)]
- commitinfo = checkout.commit_info_for_revision(4)
- self.assertEqual(commitinfo.bug_id(), 36629)
- self.assertEqual(commitinfo.author_name(), u"Tor Arne Vestb\u00f8")
- self.assertEqual(commitinfo.author_email(), "vestbo@webkit.org")
- self.assertEqual(commitinfo.reviewer_text(), None)
- self.assertEqual(commitinfo.reviewer(), None)
- self.assertEqual(commitinfo.committer_email(), "committer@example.com")
- self.assertEqual(commitinfo.committer(), None)
-
- checkout.changelog_entries_for_revision = lambda revision: []
- self.assertEqual(checkout.commit_info_for_revision(1), None)
-
- def test_bug_id_for_revision(self):
- scm = Mock()
- scm.committer_email_for_revision = lambda revision: "committer@example.com"
- checkout = Checkout(scm)
- checkout.changelog_entries_for_revision = lambda revision: [ChangeLogEntry(_changelog1entry1)]
- self.assertEqual(checkout.bug_id_for_revision(4), 36629)
-
- def test_bug_id_for_this_commit(self):
- scm = Mock()
- checkout = Checkout(scm)
- checkout.commit_message_for_this_commit = lambda git_commit, changed_files=None: CommitMessage(ChangeLogEntry(_changelog1entry1).contents().splitlines())
- self.assertEqual(checkout.bug_id_for_this_commit(git_commit=None), 36629)
-
- def test_modified_changelogs(self):
- scm = Mock()
- scm.checkout_root = "/foo/bar"
- scm.changed_files = lambda git_commit: ["file1", "ChangeLog", "relative/path/ChangeLog"]
- checkout = Checkout(scm)
- expected_changlogs = ["/foo/bar/ChangeLog", "/foo/bar/relative/path/ChangeLog"]
- self.assertEqual(checkout.modified_changelogs(git_commit=None), expected_changlogs)
-
- def test_suggested_reviewers(self):
- def mock_changelog_entries_for_revision(revision):
- if revision % 2 == 0:
- return [ChangeLogEntry(_changelog1entry1)]
- return [ChangeLogEntry(_changelog1entry2)]
-
- def mock_revisions_changing_file(path, limit=5):
- if path.endswith("ChangeLog"):
- return [3]
- return [4, 8]
-
- scm = Mock()
- scm.checkout_root = "/foo/bar"
- scm.changed_files = lambda git_commit: ["file1", "file2", "relative/path/ChangeLog"]
- scm.revisions_changing_file = mock_revisions_changing_file
- checkout = Checkout(scm)
- checkout.changelog_entries_for_revision = mock_changelog_entries_for_revision
- reviewers = checkout.suggested_reviewers(git_commit=None)
- reviewer_names = [reviewer.full_name for reviewer in reviewers]
- self.assertEqual(reviewer_names, [u'Tor Arne Vestb\xf8'])
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/changelog.py b/WebKitTools/Scripts/webkitpy/common/checkout/changelog.py
deleted file mode 100644
index 40657eb..0000000
--- a/WebKitTools/Scripts/webkitpy/common/checkout/changelog.py
+++ /dev/null
@@ -1,195 +0,0 @@
-# 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
-# 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.
-#
-# WebKit's Python module for parsing and modifying ChangeLog files
-
-import codecs
-import fileinput # inplace file editing for set_reviewer_in_changelog
-import os.path
-import re
-import textwrap
-
-from webkitpy.common.system.deprecated_logging import log
-from webkitpy.common.config.committers import CommitterList
-from webkitpy.common.net.bugzilla import parse_bug_id
-
-
-def view_source_url(revision_number):
- # FIMXE: This doesn't really belong in this file, but we don't have a
- # better home for it yet.
- # Maybe eventually a webkit_config.py?
- return "http://trac.webkit.org/changeset/%s" % revision_number
-
-
-class ChangeLogEntry(object):
- # e.g. 2009-06-03 Eric Seidel <eric@webkit.org>
- date_line_regexp = r'^(?P<date>\d{4}-\d{2}-\d{2})\s+(?P<name>.+?)\s+<(?P<email>[^<>]+)>$'
-
- def __init__(self, contents, committer_list=CommitterList()):
- self._contents = contents
- self._committer_list = committer_list
- self._parse_entry()
-
- def _parse_entry(self):
- match = re.match(self.date_line_regexp, self._contents, re.MULTILINE)
- if not match:
- log("WARNING: Creating invalid ChangeLogEntry:\n%s" % self._contents)
-
- # FIXME: group("name") does not seem to be Unicode? Probably due to self._contents not being unicode.
- self._author_name = match.group("name") if match else None
- self._author_email = match.group("email") if match else None
-
- match = re.search("^\s+Reviewed by (?P<reviewer>.*?)[\.,]?\s*$", self._contents, re.MULTILINE) # Discard everything after the first period
- self._reviewer_text = match.group("reviewer") if match else None
-
- self._reviewer = self._committer_list.committer_by_name(self._reviewer_text)
- self._author = self._committer_list.committer_by_email(self._author_email) or self._committer_list.committer_by_name(self._author_name)
-
- def author_name(self):
- return self._author_name
-
- def author_email(self):
- return self._author_email
-
- def author(self):
- return self._author # Might be None
-
- # FIXME: Eventually we would like to map reviwer names to reviewer objects.
- # See https://bugs.webkit.org/show_bug.cgi?id=26533
- def reviewer_text(self):
- return self._reviewer_text
-
- def reviewer(self):
- return self._reviewer # Might be None
-
- def contents(self):
- return self._contents
-
- def bug_id(self):
- return parse_bug_id(self._contents)
-
-
-# FIXME: Various methods on ChangeLog should move into ChangeLogEntry instead.
-class ChangeLog(object):
-
- def __init__(self, path):
- self.path = path
-
- _changelog_indent = " " * 8
-
- @staticmethod
- def parse_latest_entry_from_file(changelog_file):
- """changelog_file must be a file-like object which returns
- unicode strings. Use codecs.open or StringIO(unicode())
- to pass file objects to this class."""
- date_line_regexp = re.compile(ChangeLogEntry.date_line_regexp)
- entry_lines = []
- # The first line should be a date line.
- first_line = changelog_file.readline()
- assert(isinstance(first_line, unicode))
- if not date_line_regexp.match(first_line):
- return None
- entry_lines.append(first_line)
-
- for line in changelog_file:
- # If we've hit the next entry, return.
- if date_line_regexp.match(line):
- # Remove the extra newline at the end
- return ChangeLogEntry(''.join(entry_lines[:-1]))
- entry_lines.append(line)
- return None # We never found a date line!
-
- def latest_entry(self):
- # ChangeLog files are always UTF-8, we read them in as such to support Reviewers with unicode in their names.
- changelog_file = codecs.open(self.path, "r", "utf-8")
- try:
- return self.parse_latest_entry_from_file(changelog_file)
- finally:
- changelog_file.close()
-
- # _wrap_line and _wrap_lines exist to work around
- # http://bugs.python.org/issue1859
-
- def _wrap_line(self, line):
- return textwrap.fill(line,
- width=70,
- initial_indent=self._changelog_indent,
- # Don't break urls which may be longer than width.
- break_long_words=False,
- subsequent_indent=self._changelog_indent)
-
- # Workaround as suggested by guido in
- # http://bugs.python.org/issue1859#msg60040
-
- def _wrap_lines(self, message):
- lines = [self._wrap_line(line) for line in message.splitlines()]
- return "\n".join(lines)
-
- # This probably does not belong in changelogs.py
- def _message_for_revert(self, revision, reason, bug_url):
- message = "Unreviewed, rolling out r%s.\n" % revision
- message += "%s\n" % view_source_url(revision)
- if bug_url:
- message += "%s\n" % bug_url
- # Add an extra new line after the rollout links, before any reason.
- message += "\n"
- if reason:
- message += "%s\n\n" % reason
- return self._wrap_lines(message)
-
- def update_for_revert(self, revision, reason, bug_url=None):
- reviewed_by_regexp = re.compile(
- "%sReviewed by NOBODY \(OOPS!\)\." % self._changelog_indent)
- removing_boilerplate = False
- # inplace=1 creates a backup file and re-directs stdout to the file
- for line in fileinput.FileInput(self.path, inplace=1):
- if reviewed_by_regexp.search(line):
- message_lines = self._message_for_revert(revision,
- reason,
- bug_url)
- print reviewed_by_regexp.sub(message_lines, line),
- # Remove all the ChangeLog boilerplate between the Reviewed by
- # line and the first changed file.
- removing_boilerplate = True
- elif removing_boilerplate:
- if line.find('*') >= 0: # each changed file is preceded by a *
- removing_boilerplate = False
-
- if not removing_boilerplate:
- print line,
-
- def set_reviewer(self, reviewer):
- # inplace=1 creates a backup file and re-directs stdout to the file
- for line in fileinput.FileInput(self.path, inplace=1):
- # Trailing comma suppresses printing newline
- print line.replace("NOBODY (OOPS!)", reviewer.encode("utf-8")),
-
- def set_short_description_and_bug_url(self, short_description, bug_url):
- message = "%s\n %s" % (short_description, bug_url)
- for line in fileinput.FileInput(self.path, inplace=1):
- print line.replace("Need a short description and bug URL (OOPS!)", message.encode("utf-8")),
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/changelog_unittest.py b/WebKitTools/Scripts/webkitpy/common/checkout/changelog_unittest.py
deleted file mode 100644
index 6aeb1f8..0000000
--- a/WebKitTools/Scripts/webkitpy/common/checkout/changelog_unittest.py
+++ /dev/null
@@ -1,203 +0,0 @@
-# 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
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import with_statement
-
-import codecs
-import os
-import tempfile
-import unittest
-
-from StringIO import StringIO
-
-from webkitpy.common.checkout.changelog import *
-
-
-class ChangeLogTest(unittest.TestCase):
-
- _example_entry = u'''2009-08-17 Peter Kasting <pkasting@google.com>
-
- Reviewed by Tor Arne Vestb\xf8.
-
- https://bugs.webkit.org/show_bug.cgi?id=27323
- Only add Cygwin to the path when it isn't already there. This avoids
- causing problems for people who purposefully have non-Cygwin versions of
- executables like svn in front of the Cygwin ones in their paths.
-
- * DumpRenderTree/win/DumpRenderTree.vcproj:
- * DumpRenderTree/win/ImageDiff.vcproj:
- * DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.vcproj:
-'''
-
- # More example text than we need. Eventually we need to support parsing this all and write tests for the parsing.
- _example_changelog = u"""2009-08-17 Tor Arne Vestb\xf8 <vestbo@webkit.org>
-
- <http://webkit.org/b/28393> check-webkit-style: add check for use of std::max()/std::min() instead of MAX()/MIN()
-
- Reviewed by David Levin.
-
- * Scripts/modules/cpp_style.py:
- (_ERROR_CATEGORIES): Added 'runtime/max_min_macros'.
- (check_max_min_macros): Added. Returns level 4 error when MAX()
- and MIN() macros are used in header files and C++ source files.
- (check_style): Added call to check_max_min_macros().
- * Scripts/modules/cpp_style_unittest.py: Added unit tests.
- (test_max_macro): Added.
- (test_min_macro): Added.
-
-2009-08-16 David Kilzer <ddkilzer@apple.com>
-
- Backed out r47343 which was mistakenly committed
-
- * Scripts/bugzilla-tool:
- * Scripts/modules/scm.py:
-
-2009-06-18 Darin Adler <darin@apple.com>
-
- Rubber stamped by Mark Rowe.
-
- * DumpRenderTree/mac/DumpRenderTreeWindow.mm:
- (-[DumpRenderTreeWindow close]): Resolved crashes seen during regression
- tests. The close method can be called on a window that's already closed
- so we can't assert here.
-
-== Rolled over to ChangeLog-2009-06-16 ==
-"""
-
- def test_latest_entry_parse(self):
- changelog_contents = u"%s\n%s" % (self._example_entry, self._example_changelog)
- changelog_file = StringIO(changelog_contents)
- latest_entry = ChangeLog.parse_latest_entry_from_file(changelog_file)
- self.assertEquals(latest_entry.contents(), self._example_entry)
- self.assertEquals(latest_entry.author_name(), "Peter Kasting")
- self.assertEquals(latest_entry.author_email(), "pkasting@google.com")
- self.assertEquals(latest_entry.reviewer_text(), u"Tor Arne Vestb\xf8")
- self.assertTrue(latest_entry.reviewer()) # Make sure that our UTF8-based lookup of Tor works.
-
- @staticmethod
- def _write_tmp_file_with_contents(byte_array):
- assert(isinstance(byte_array, str))
- (file_descriptor, file_path) = tempfile.mkstemp() # NamedTemporaryFile always deletes the file on close in python < 2.6
- with os.fdopen(file_descriptor, "w") as file:
- file.write(byte_array)
- return file_path
-
- @staticmethod
- def _read_file_contents(file_path, encoding):
- with codecs.open(file_path, "r", encoding) as file:
- return file.read()
-
- _new_entry_boilerplate = '''2009-08-19 Eric Seidel <eric@webkit.org>
-
- Reviewed by NOBODY (OOPS!).
-
- Need a short description and bug URL (OOPS!)
-
- * Scripts/bugzilla-tool:
-'''
-
- def test_set_reviewer(self):
- changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate, self._example_changelog)
- changelog_path = self._write_tmp_file_with_contents(changelog_contents.encode("utf-8"))
- reviewer_name = 'Test Reviewer'
- ChangeLog(changelog_path).set_reviewer(reviewer_name)
- actual_contents = self._read_file_contents(changelog_path, "utf-8")
- expected_contents = changelog_contents.replace('NOBODY (OOPS!)', reviewer_name)
- os.remove(changelog_path)
- self.assertEquals(actual_contents, expected_contents)
-
- def test_set_short_description_and_bug_url(self):
- changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate, self._example_changelog)
- changelog_path = self._write_tmp_file_with_contents(changelog_contents.encode("utf-8"))
- short_description = "A short description"
- bug_url = "http://example.com/b/2344"
- ChangeLog(changelog_path).set_short_description_and_bug_url(short_description, bug_url)
- actual_contents = self._read_file_contents(changelog_path, "utf-8")
- expected_message = "%s\n %s" % (short_description, bug_url)
- expected_contents = changelog_contents.replace("Need a short description and bug URL (OOPS!)", expected_message)
- os.remove(changelog_path)
- self.assertEquals(actual_contents, expected_contents)
-
- _revert_message = """ Unreviewed, rolling out r12345.
- http://trac.webkit.org/changeset/12345
- http://example.com/123
-
- This is a very long reason which should be long enough so that
- _message_for_revert will need to wrap it. We'll also include
- a
- https://veryveryveryveryverylongbugurl.com/reallylongbugthingy.cgi?bug_id=12354
- link so that we can make sure we wrap that right too.
-"""
-
- def test_message_for_revert(self):
- changelog = ChangeLog("/fake/path")
- long_reason = "This is a very long reason which should be long enough so that _message_for_revert will need to wrap it. We'll also include a https://veryveryveryveryverylongbugurl.com/reallylongbugthingy.cgi?bug_id=12354 link so that we can make sure we wrap that right too."
- message = changelog._message_for_revert(12345, long_reason, "http://example.com/123")
- self.assertEquals(message, self._revert_message)
-
- _revert_entry_with_bug_url = '''2009-08-19 Eric Seidel <eric@webkit.org>
-
- Unreviewed, rolling out r12345.
- http://trac.webkit.org/changeset/12345
- http://example.com/123
-
- Reason
-
- * Scripts/bugzilla-tool:
-'''
-
- _revert_entry_without_bug_url = '''2009-08-19 Eric Seidel <eric@webkit.org>
-
- Unreviewed, rolling out r12345.
- http://trac.webkit.org/changeset/12345
-
- Reason
-
- * Scripts/bugzilla-tool:
-'''
-
- def _assert_update_for_revert_output(self, args, expected_entry):
- changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate, self._example_changelog)
- changelog_path = self._write_tmp_file_with_contents(changelog_contents.encode("utf-8"))
- changelog = ChangeLog(changelog_path)
- changelog.update_for_revert(*args)
- actual_entry = changelog.latest_entry()
- os.remove(changelog_path)
- self.assertEquals(actual_entry.contents(), expected_entry)
- self.assertEquals(actual_entry.reviewer_text(), None)
- # These checks could be removed to allow this to work on other entries:
- self.assertEquals(actual_entry.author_name(), "Eric Seidel")
- self.assertEquals(actual_entry.author_email(), "eric@webkit.org")
-
- def test_update_for_revert(self):
- self._assert_update_for_revert_output([12345, "Reason"], self._revert_entry_without_bug_url)
- self._assert_update_for_revert_output([12345, "Reason", "http://example.com/123"], self._revert_entry_with_bug_url)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/commitinfo.py b/WebKitTools/Scripts/webkitpy/common/checkout/commitinfo.py
deleted file mode 100644
index 448d530..0000000
--- a/WebKitTools/Scripts/webkitpy/common/checkout/commitinfo.py
+++ /dev/null
@@ -1,93 +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.
-#
-# WebKit's python module for holding information on a commit
-
-from webkitpy.common.checkout.changelog import view_source_url
-from webkitpy.common.config.committers import CommitterList
-
-
-class CommitInfo(object):
- def __init__(self, revision, committer_email, changelog_data, committer_list=CommitterList()):
- self._revision = revision
- self._committer_email = committer_email
- self._bug_id = changelog_data["bug_id"]
- self._author_name = changelog_data["author_name"]
- self._author_email = changelog_data["author_email"]
- self._author = changelog_data["author"]
- self._reviewer_text = changelog_data["reviewer_text"]
- self._reviewer = changelog_data["reviewer"]
-
- # Derived values:
- self._committer = committer_list.committer_by_email(committer_email)
-
- def revision(self):
- return self._revision
-
- def committer(self):
- return self._committer # None if committer isn't in committers.py
-
- def committer_email(self):
- return self._committer_email
-
- def bug_id(self):
- return self._bug_id # May be None
-
- def author(self):
- return self._author # May be None
-
- def author_name(self):
- return self._author_name
-
- def author_email(self):
- return self._author_email
-
- def reviewer(self):
- return self._reviewer # May be None
-
- def reviewer_text(self):
- return self._reviewer_text # May be None
-
- def responsible_parties(self):
- responsible_parties = [
- self.committer(),
- self.author(),
- self.reviewer(),
- ]
- return set([party for party in responsible_parties if party]) # Filter out None
-
- # FIXME: It is slightly lame that this "view" method is on this "model" class (in MVC terms)
- def blame_string(self, bugs):
- string = "r%s:\n" % self.revision()
- string += " %s\n" % view_source_url(self.revision())
- string += " Bug: %s (%s)\n" % (self.bug_id(), bugs.bug_url_for_bug_id(self.bug_id()))
- author_line = "\"%s\" <%s>" % (self.author_name(), self.author_email())
- string += " Author: %s\n" % (self.author() or author_line)
- string += " Reviewer: %s\n" % (self.reviewer() or self.reviewer_text())
- string += " Committer: %s" % self.committer()
- return string
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/commitinfo_unittest.py b/WebKitTools/Scripts/webkitpy/common/checkout/commitinfo_unittest.py
deleted file mode 100644
index f58e6f1..0000000
--- a/WebKitTools/Scripts/webkitpy/common/checkout/commitinfo_unittest.py
+++ /dev/null
@@ -1,61 +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.
-
-import unittest
-
-from webkitpy.common.checkout.commitinfo import CommitInfo
-from webkitpy.common.config.committers import CommitterList, Committer, Reviewer
-
-class CommitInfoTest(unittest.TestCase):
-
- def test_commit_info_creation(self):
- author = Committer("Author", "author@example.com")
- committer = Committer("Committer", "committer@example.com")
- reviewer = Reviewer("Reviewer", "reviewer@example.com")
- committer_list = CommitterList(committers=[author, committer], reviewers=[reviewer])
-
- changelog_data = {
- "bug_id": 1234,
- "author_name": "Committer",
- "author_email": "author@example.com",
- "author": author,
- "reviewer_text": "Reviewer",
- "reviewer": reviewer,
- }
- commit = CommitInfo(123, "committer@example.com", changelog_data, committer_list)
-
- self.assertEqual(commit.revision(), 123)
- self.assertEqual(commit.bug_id(), 1234)
- self.assertEqual(commit.author_name(), "Committer")
- self.assertEqual(commit.author_email(), "author@example.com")
- self.assertEqual(commit.author(), author)
- self.assertEqual(commit.reviewer_text(), "Reviewer")
- self.assertEqual(commit.reviewer(), reviewer)
- self.assertEqual(commit.committer(), committer)
- self.assertEqual(commit.committer_email(), "committer@example.com")
- self.assertEqual(commit.responsible_parties(), set([author, committer, reviewer]))
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/diff_parser.py b/WebKitTools/Scripts/webkitpy/common/checkout/diff_parser.py
deleted file mode 100644
index a6ea756..0000000
--- a/WebKitTools/Scripts/webkitpy/common/checkout/diff_parser.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# 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
-# 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.
-
-"""WebKit's Python module for interacting with patches."""
-
-import logging
-import re
-
-_log = logging.getLogger("webkitpy.common.checkout.diff_parser")
-
-
-# FIXME: This is broken. We should compile our regexps up-front
-# instead of using a custom cache.
-_regexp_compile_cache = {}
-
-
-# FIXME: This function should be removed.
-def match(pattern, string):
- """Matches the string with the pattern, caching the compiled regexp."""
- if not pattern in _regexp_compile_cache:
- _regexp_compile_cache[pattern] = re.compile(pattern)
- return _regexp_compile_cache[pattern].match(string)
-
-
-# FIXME: This belongs on DiffParser (e.g. as to_svn_diff()).
-def git_diff_to_svn_diff(line):
- """Converts a git formatted diff line to a svn formatted line.
-
- Args:
- line: A string representing a line of the diff.
- """
- # FIXME: This list should be a class member on DiffParser.
- # These regexp patterns should be compiled once instead of every time.
- conversion_patterns = (("^diff --git \w/(.+) \w/(?P<FilePath>.+)", lambda matched: "Index: " + matched.group('FilePath') + "\n"),
- ("^new file.*", lambda matched: "\n"),
- ("^index [0-9a-f]{7}\.\.[0-9a-f]{7} [0-9]{6}", lambda matched: "===================================================================\n"),
- ("^--- \w/(?P<FilePath>.+)", lambda matched: "--- " + matched.group('FilePath') + "\n"),
- ("^\+\+\+ \w/(?P<FilePath>.+)", lambda matched: "+++ " + matched.group('FilePath') + "\n"))
-
- for pattern, conversion in conversion_patterns:
- matched = match(pattern, line)
- if matched:
- return conversion(matched)
- return line
-
-
-# FIXME: This method belongs on DiffParser
-def get_diff_converter(first_diff_line):
- """Gets a converter function of diff lines.
-
- Args:
- first_diff_line: The first filename line of a diff file.
- If this line is git formatted, we'll return a
- converter from git to SVN.
- """
- if match(r"^diff --git \w/", first_diff_line):
- return git_diff_to_svn_diff
- return lambda input: input
-
-
-_INITIAL_STATE = 1
-_DECLARED_FILE_PATH = 2
-_PROCESSING_CHUNK = 3
-
-
-class DiffFile(object):
- """Contains the information for one file in a patch.
-
- The field "lines" is a list which contains tuples in this format:
- (deleted_line_number, new_line_number, line_string)
- If deleted_line_number is zero, it means this line is newly added.
- If new_line_number is zero, it means this line is deleted.
- """
- # FIXME: Tuples generally grow into classes. We should consider
- # adding a DiffLine object.
-
- def added_or_modified_line_numbers(self):
- # This logic was moved from patchreader.py, but may not be
- # the right API for this object long-term.
- return [line[1] for line in self.lines if not line[0]]
-
- def __init__(self, filename):
- self.filename = filename
- self.lines = []
-
- def add_new_line(self, line_number, line):
- self.lines.append((0, line_number, line))
-
- def add_deleted_line(self, line_number, line):
- self.lines.append((line_number, 0, line))
-
- def add_unchanged_line(self, deleted_line_number, new_line_number, line):
- self.lines.append((deleted_line_number, new_line_number, line))
-
-
-class DiffParser(object):
- """A parser for a patch file.
-
- The field "files" is a dict whose key is the filename and value is
- a DiffFile object.
- """
-
- # FIXME: This function is way too long and needs to be broken up.
- def __init__(self, diff_input):
- """Parses a diff.
-
- Args:
- diff_input: An iterable object.
- """
- state = _INITIAL_STATE
-
- self.files = {}
- current_file = None
- old_diff_line = None
- new_diff_line = None
- for line in diff_input:
- line = line.rstrip("\n")
- if state == _INITIAL_STATE:
- transform_line = get_diff_converter(line)
- line = transform_line(line)
-
- file_declaration = match(r"^Index: (?P<FilePath>.+)", line)
- if file_declaration:
- filename = file_declaration.group('FilePath')
- current_file = DiffFile(filename)
- self.files[filename] = current_file
- state = _DECLARED_FILE_PATH
- continue
-
- lines_changed = match(r"^@@ -(?P<OldStartLine>\d+)(,\d+)? \+(?P<NewStartLine>\d+)(,\d+)? @@", line)
- if lines_changed:
- if state != _DECLARED_FILE_PATH and state != _PROCESSING_CHUNK:
- _log.error('Unexpected line change without file path '
- 'declaration: %r' % line)
- old_diff_line = int(lines_changed.group('OldStartLine'))
- new_diff_line = int(lines_changed.group('NewStartLine'))
- state = _PROCESSING_CHUNK
- continue
-
- if state == _PROCESSING_CHUNK:
- if line.startswith('+'):
- current_file.add_new_line(new_diff_line, line[1:])
- new_diff_line += 1
- elif line.startswith('-'):
- current_file.add_deleted_line(old_diff_line, line[1:])
- old_diff_line += 1
- elif line.startswith(' '):
- current_file.add_unchanged_line(old_diff_line, new_diff_line, line[1:])
- old_diff_line += 1
- new_diff_line += 1
- elif line == '\\ No newline at end of file':
- # Nothing to do. We may still have some added lines.
- pass
- else:
- _log.error('Unexpected diff format when parsing a '
- 'chunk: %r' % line)
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/diff_parser_unittest.py b/WebKitTools/Scripts/webkitpy/common/checkout/diff_parser_unittest.py
deleted file mode 100644
index 7eb0eab..0000000
--- a/WebKitTools/Scripts/webkitpy/common/checkout/diff_parser_unittest.py
+++ /dev/null
@@ -1,146 +0,0 @@
-# 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
-# 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 unittest
-import diff_parser
-import re
-
-
-class DiffParserTest(unittest.TestCase):
-
- _PATCH = '''diff --git a/WebCore/rendering/style/StyleFlexibleBoxData.h b/WebCore/rendering/style/StyleFlexibleBoxData.h
-index f5d5e74..3b6aa92 100644
---- a/WebCore/rendering/style/StyleFlexibleBoxData.h
-+++ b/WebCore/rendering/style/StyleFlexibleBoxData.h
-@@ -47,7 +47,6 @@ public:
-
- unsigned align : 3; // EBoxAlignment
- unsigned pack: 3; // EBoxAlignment
-- unsigned orient: 1; // EBoxOrient
- unsigned lines : 1; // EBoxLines
-
- private:
-diff --git a/WebCore/rendering/style/StyleRareInheritedData.cpp b/WebCore/rendering/style/StyleRareInheritedData.cpp
-index ce21720..324929e 100644
---- a/WebCore/rendering/style/StyleRareInheritedData.cpp
-+++ b/WebCore/rendering/style/StyleRareInheritedData.cpp
-@@ -39,6 +39,7 @@ StyleRareInheritedData::StyleRareInheritedData()
- , textSizeAdjust(RenderStyle::initialTextSizeAdjust())
- , resize(RenderStyle::initialResize())
- , userSelect(RenderStyle::initialUserSelect())
-+ , boxOrient(RenderStyle::initialBoxOrient())
- {
- }
-
-@@ -58,6 +59,7 @@ StyleRareInheritedData::StyleRareInheritedData(const StyleRareInheritedData& o)
- , textSizeAdjust(o.textSizeAdjust)
- , resize(o.resize)
- , userSelect(o.userSelect)
-+ , boxOrient(o.boxOrient)
- {
- }
-
-@@ -81,7 +83,8 @@ bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const
- && khtmlLineBreak == o.khtmlLineBreak
- && textSizeAdjust == o.textSizeAdjust
- && resize == o.resize
-- && userSelect == o.userSelect;
-+ && userSelect == o.userSelect
-+ && boxOrient == o.boxOrient;
- }
-
- bool StyleRareInheritedData::shadowDataEquivalent(const StyleRareInheritedData& o) const
-diff --git a/LayoutTests/platform/mac/fast/flexbox/box-orient-button-expected.checksum b/LayoutTests/platform/mac/fast/flexbox/box-orient-button-expected.checksum
-new file mode 100644
-index 0000000..6db26bd
---- /dev/null
-+++ b/LayoutTests/platform/mac/fast/flexbox/box-orient-button-expected.checksum
-@@ -0,0 +1 @@
-+61a373ee739673a9dcd7bac62b9f182e
-\ No newline at end of file
-'''
-
- def test_diff_parser(self, parser = None):
- if not parser:
- parser = diff_parser.DiffParser(self._PATCH.splitlines())
- self.assertEquals(3, len(parser.files))
-
- self.assertTrue('WebCore/rendering/style/StyleFlexibleBoxData.h' in parser.files)
- diff = parser.files['WebCore/rendering/style/StyleFlexibleBoxData.h']
- self.assertEquals(7, len(diff.lines))
- # The first two unchaged lines.
- self.assertEquals((47, 47), diff.lines[0][0:2])
- self.assertEquals('', diff.lines[0][2])
- self.assertEquals((48, 48), diff.lines[1][0:2])
- self.assertEquals(' unsigned align : 3; // EBoxAlignment', diff.lines[1][2])
- # The deleted line
- self.assertEquals((50, 0), diff.lines[3][0:2])
- self.assertEquals(' unsigned orient: 1; // EBoxOrient', diff.lines[3][2])
-
- # The first file looks OK. Let's check the next, more complicated file.
- self.assertTrue('WebCore/rendering/style/StyleRareInheritedData.cpp' in parser.files)
- diff = parser.files['WebCore/rendering/style/StyleRareInheritedData.cpp']
- # There are 3 chunks.
- self.assertEquals(7 + 7 + 9, len(diff.lines))
- # Around an added line.
- self.assertEquals((60, 61), diff.lines[9][0:2])
- self.assertEquals((0, 62), diff.lines[10][0:2])
- self.assertEquals((61, 63), diff.lines[11][0:2])
- # Look through the last chunk, which contains both add's and delete's.
- self.assertEquals((81, 83), diff.lines[14][0:2])
- self.assertEquals((82, 84), diff.lines[15][0:2])
- self.assertEquals((83, 85), diff.lines[16][0:2])
- self.assertEquals((84, 0), diff.lines[17][0:2])
- self.assertEquals((0, 86), diff.lines[18][0:2])
- self.assertEquals((0, 87), diff.lines[19][0:2])
- self.assertEquals((85, 88), diff.lines[20][0:2])
- self.assertEquals((86, 89), diff.lines[21][0:2])
- self.assertEquals((87, 90), diff.lines[22][0:2])
-
- # Check if a newly added file is correctly handled.
- diff = parser.files['LayoutTests/platform/mac/fast/flexbox/box-orient-button-expected.checksum']
- self.assertEquals(1, len(diff.lines))
- self.assertEquals((0, 1), diff.lines[0][0:2])
-
- def test_git_mnemonicprefix(self):
- p = re.compile(r' ([a|b])/')
-
- prefixes = [
- { 'a' : 'i', 'b' : 'w' }, # git-diff (compares the (i)ndex and the (w)ork tree)
- { 'a' : 'c', 'b' : 'w' }, # git-diff HEAD (compares a (c)ommit and the (w)ork tree)
- { 'a' : 'c', 'b' : 'i' }, # git diff --cached (compares a (c)ommit and the (i)ndex)
- { 'a' : 'o', 'b' : 'w' }, # git-diff HEAD:file1 file2 (compares an (o)bject and a (w)ork tree entity)
- { 'a' : '1', 'b' : '2' }, # git diff --no-index a b (compares two non-git things (1) and (2))
- ]
-
- for prefix in prefixes:
- patch = p.sub(lambda x: " %s/" % prefix[x.group(1)], self._PATCH)
- self.test_diff_parser(diff_parser.DiffParser(patch.splitlines()))
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/scm.py b/WebKitTools/Scripts/webkitpy/common/checkout/scm.py
deleted file mode 100644
index d39b8b4..0000000
--- a/WebKitTools/Scripts/webkitpy/common/checkout/scm.py
+++ /dev/null
@@ -1,915 +0,0 @@
-# Copyright (c) 2009, Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple 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.
-#
-# Python module for interacting with an SCM system (like SVN or Git)
-
-import os
-import re
-import sys
-import shutil
-
-from webkitpy.common.system.executive import Executive, run_command, ScriptError
-from webkitpy.common.system.deprecated_logging import error, log
-from webkitpy.common.memoized import memoized
-
-
-def find_checkout_root():
- """Returns the current checkout root (as determined by default_scm().
-
- Returns the absolute path to the top of the WebKit checkout, or None
- if it cannot be determined.
-
- """
- scm_system = default_scm()
- if scm_system:
- return scm_system.checkout_root
- return None
-
-
-def default_scm():
- """Return the default SCM object as determined by the CWD and running code.
-
- Returns the default SCM object for the current working directory; if the
- CWD is not in a checkout, then we attempt to figure out if the SCM module
- itself is part of a checkout, and return that one. If neither is part of
- a checkout, None is returned.
-
- """
- cwd = os.getcwd()
- scm_system = detect_scm_system(cwd)
- if not scm_system:
- script_directory = os.path.dirname(os.path.abspath(__file__))
- scm_system = detect_scm_system(script_directory)
- if scm_system:
- log("The current directory (%s) is not a WebKit checkout, using %s" % (cwd, scm_system.checkout_root))
- else:
- error("FATAL: Failed to determine the SCM system for either %s or %s" % (cwd, script_directory))
- return scm_system
-
-
-def detect_scm_system(path):
- absolute_path = os.path.abspath(path)
-
- if SVN.in_working_directory(absolute_path):
- return SVN(cwd=absolute_path)
-
- if Git.in_working_directory(absolute_path):
- return Git(cwd=absolute_path)
-
- return None
-
-
-def first_non_empty_line_after_index(lines, index=0):
- first_non_empty_line = index
- for line in lines[index:]:
- if re.match("^\s*$", line):
- first_non_empty_line += 1
- else:
- break
- return first_non_empty_line
-
-
-class CommitMessage:
- def __init__(self, message):
- self.message_lines = message[first_non_empty_line_after_index(message, 0):]
-
- def body(self, lstrip=False):
- lines = self.message_lines[first_non_empty_line_after_index(self.message_lines, 1):]
- if lstrip:
- lines = [line.lstrip() for line in lines]
- return "\n".join(lines) + "\n"
-
- def description(self, lstrip=False, strip_url=False):
- line = self.message_lines[0]
- if lstrip:
- line = line.lstrip()
- if strip_url:
- line = re.sub("^(\s*)<.+> ", "\1", line)
- return line
-
- def message(self):
- return "\n".join(self.message_lines) + "\n"
-
-
-class CheckoutNeedsUpdate(ScriptError):
- def __init__(self, script_args, exit_code, output, cwd):
- ScriptError.__init__(self, script_args=script_args, exit_code=exit_code, output=output, cwd=cwd)
-
-
-def commit_error_handler(error):
- if re.search("resource out of date", error.output):
- raise CheckoutNeedsUpdate(script_args=error.script_args, exit_code=error.exit_code, output=error.output, cwd=error.cwd)
- Executive.default_error_handler(error)
-
-
-class AuthenticationError(Exception):
- def __init__(self, server_host):
- self.server_host = server_host
-
-
-class AmbiguousCommitError(Exception):
- def __init__(self, num_local_commits, working_directory_is_clean):
- self.num_local_commits = num_local_commits
- self.working_directory_is_clean = working_directory_is_clean
-
-
-# SCM methods are expected to return paths relative to self.checkout_root.
-class SCM:
- def __init__(self, cwd):
- self.cwd = cwd
- 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):
- return os.path.join(self.checkout_root, repository_relative_path)
-
- # FIXME: This belongs in Checkout, not SCM.
- def scripts_directory(self):
- return os.path.join(self.checkout_root, "WebKitTools", "Scripts")
-
- # FIXME: This belongs in Checkout, not SCM.
- def script_path(self, script_name):
- return os.path.join(self.scripts_directory(), script_name)
-
- 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 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")
- self.clean_working_directory()
-
- def ensure_no_local_commits(self, force):
- if not self.supports_local_commits():
- return
- commits = self.local_commits()
- if not len(commits):
- return
- if not force:
- error("Working directory has local commits, pass --force-clean to continue.")
- self.discard_local_commits()
-
- 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 self.run(status_command, cwd=self.checkout_root).splitlines():
- match = re.search(status_regexp, line)
- if not match:
- continue
- # status = match.group('status')
- filename = match.group('filename')
- filenames.append(filename)
- return filenames
-
- def strip_r_from_svn_revision(self, svn_revision):
- match = re.match("^r(?P<svn_revision>\d+)", unicode(svn_revision))
- if (match):
- return match.group('svn_revision')
- return svn_revision
-
- def svn_revision_from_commit_text(self, commit_text):
- match = re.search(self.commit_success_regexp(), commit_text, re.MULTILINE)
- return match.group('svn_revision')
-
- @staticmethod
- def _subclass_must_implement():
- raise NotImplementedError("subclasses must implement")
-
- @staticmethod
- def in_working_directory(path):
- SCM._subclass_must_implement()
-
- @staticmethod
- def find_checkout_root(path):
- SCM._subclass_must_implement()
-
- @staticmethod
- def commit_success_regexp():
- SCM._subclass_must_implement()
-
- def working_directory_is_clean(self):
- self._subclass_must_implement()
-
- def clean_working_directory(self):
- self._subclass_must_implement()
-
- def status_command(self):
- self._subclass_must_implement()
-
- def add(self, path, return_exit_code=False):
- self._subclass_must_implement()
-
- def delete(self, path):
- self._subclass_must_implement()
-
- def changed_files(self, git_commit=None):
- self._subclass_must_implement()
-
- def changed_files_for_revision(self, revision):
- self._subclass_must_implement()
-
- def revisions_changing_file(self, path, limit=5):
- self._subclass_must_implement()
-
- def added_files(self):
- self._subclass_must_implement()
-
- def conflicted_files(self):
- self._subclass_must_implement()
-
- def display_name(self):
- self._subclass_must_implement()
-
- def create_patch(self, git_commit=None, changed_files=[]):
- self._subclass_must_implement()
-
- def committer_email_for_revision(self, revision):
- self._subclass_must_implement()
-
- def contents_at_revision(self, path, revision):
- self._subclass_must_implement()
-
- def diff_for_revision(self, revision):
- self._subclass_must_implement()
-
- def diff_for_file(self, path, log=None):
- self._subclass_must_implement()
-
- def show_head(self, path):
- self._subclass_must_implement()
-
- def apply_reverse_diff(self, revision):
- self._subclass_must_implement()
-
- def revert_files(self, file_paths):
- self._subclass_must_implement()
-
- def commit_with_message(self, message, username=None, git_commit=None, force_squash=False):
- self._subclass_must_implement()
-
- def svn_commit_log(self, svn_revision):
- self._subclass_must_implement()
-
- def last_svn_commit_log(self):
- self._subclass_must_implement()
-
- # Subclasses must indicate if they support local commits,
- # but the SCM baseclass will only call local_commits methods when this is true.
- @staticmethod
- def supports_local_commits():
- SCM._subclass_must_implement()
-
- def remote_merge_base():
- SCM._subclass_must_implement()
-
- def commit_locally_with_message(self, message):
- error("Your source control manager does not support local commits.")
-
- def discard_local_commits(self):
- pass
-
- def local_commits(self):
- return []
-
-
-class SVN(SCM):
- # FIXME: We should move these values to a WebKit-specific config. file.
- svn_server_host = "svn.webkit.org"
- svn_server_realm = "<http://svn.webkit.org:80> Mac OS Forge"
-
- def __init__(self, cwd):
- SCM.__init__(self, cwd)
- self._bogus_dir = None
-
- @staticmethod
- def in_working_directory(path):
- return os.path.isdir(os.path.join(path, '.svn'))
-
- @classmethod
- def find_uuid(cls, path):
- if not cls.in_working_directory(path):
- return None
- return cls.value_from_svn_info(path, 'Repository UUID')
-
- @classmethod
- def value_from_svn_info(cls, path, field_name):
- svn_info_args = ['svn', 'info', path]
- info_output = run_command(svn_info_args).rstrip()
- match = re.search("^%s: (?P<value>.+)$" % field_name, info_output, re.MULTILINE)
- if not match:
- raise ScriptError(script_args=svn_info_args, message='svn info did not contain a %s.' % field_name)
- return match.group('value')
-
- @staticmethod
- def find_checkout_root(path):
- uuid = SVN.find_uuid(path)
- # If |path| is not in a working directory, we're supposed to return |path|.
- if not uuid:
- return path
- # Search up the directory hierarchy until we find a different UUID.
- last_path = None
- while True:
- if uuid != SVN.find_uuid(path):
- return last_path
- last_path = path
- (path, last_component) = os.path.split(path)
- if last_path == path:
- return None
-
- @staticmethod
- def commit_success_regexp():
- return "^Committed revision (?P<svn_revision>\d+)\.$"
-
- def has_authorization_for_realm(self, realm=svn_server_realm, home_directory=os.getenv("HOME")):
- # Assumes find and grep are installed.
- 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 = 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))
-
- @memoized
- def svn_version(self):
- return self.run(['svn', '--version', '--quiet'])
-
- def working_directory_is_clean(self):
- return self.run(["svn", "diff"], cwd=self.checkout_root, decode_output=False) == ""
-
- def clean_working_directory(self):
- # Make sure there are no locks lying around from a previously aborted svn invocation.
- # This is slightly dangerous, as it's possible the user is running another svn process
- # on this checkout at the same time. However, it's much more likely that we're running
- # under windows and svn just sucks (or the user interrupted svn and it failed to clean up).
- self.run(["svn", "cleanup"], cwd=self.checkout_root)
-
- # svn revert -R is not as awesome as git reset --hard.
- # It will leave added files around, causing later svn update
- # calls to fail on the bots. We make this mirror git reset --hard
- # by deleting any added files as well.
- 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.
- 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)
- # Completely lame that there is no easy way to remove both types with one call.
- if os.path.isdir(path):
- os.rmdir(absolute_path)
- else:
- os.remove(absolute_path)
-
- def status_command(self):
- return ['svn', 'status']
-
- def _status_regexp(self, expected_types):
- field_count = 6 if self.svn_version() > "1.6" else 5
- return "^(?P<status>[%s]).{%s} (?P<filename>.+)$" % (expected_types, field_count)
-
- def _add_parent_directories(self, path):
- """Does 'svn add' to the path and its parents."""
- if self.in_working_directory(path):
- return
- dirname = os.path.dirname(path)
- # We have dirname directry - ensure it added.
- if dirname != path:
- self._add_parent_directories(dirname)
- self.add(path)
-
- def add(self, path, return_exit_code=False):
- self._add_parent_directories(os.path.dirname(os.path.abspath(path)))
- return self.run(["svn", "add", path], return_exit_code=return_exit_code)
-
- def delete(self, path):
- parent, base = os.path.split(os.path.abspath(path))
- return self.run(["svn", "delete", "--force", base], cwd=parent)
-
- def changed_files(self, git_commit=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 self.run is fine.
- status_command = ["svn", "diff", "--summarize", "-c", revision]
- return self.run_status_and_extract_filenames(status_command, self._status_regexp("ACDMR"))
-
- def revisions_changing_file(self, path, limit=5):
- revisions = []
- # svn log will exit(1) (and thus self.run will raise) if the path does not exist.
- log_command = ['svn', 'log', '--quiet', '--limit=%s' % limit, path]
- for line in self.run(log_command, cwd=self.checkout_root).splitlines():
- match = re.search('^r(?P<revision>\d+) ', line)
- if not match:
- continue
- revisions.append(int(match.group('revision')))
- return revisions
-
- def conflicted_files(self):
- return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("C"))
-
- def added_files(self):
- return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("A"))
-
- def deleted_files(self):
- return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("D"))
-
- @staticmethod
- def supports_local_commits():
- return False
-
- def display_name(self):
- return "svn"
-
- # FIXME: This method should be on Checkout.
- def create_patch(self, git_commit=None, changed_files=[]):
- """Returns a byte array (str()) representing the patch file.
- Patch files are effectively binary since they may contain
- files of multiple different encodings."""
- return self.run([self.script_path("svn-create-patch")] + changed_files,
- cwd=self.checkout_root, return_stderr=False,
- decode_output=False)
-
- def committer_email_for_revision(self, revision):
- 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 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 self.run(['svn', 'diff', '-c', revision])
-
- def _bogus_dir_name(self):
- if sys.platform.startswith("win"):
- parent_dir = tempfile.gettempdir()
- else:
- parent_dir = sys.path[0] # tempdir is not secure.
- return os.path.join(parent_dir, "temp_svn_config")
-
- def _setup_bogus_dir(self, log):
- self._bogus_dir = self._bogus_dir_name()
- if not os.path.exists(self._bogus_dir):
- os.mkdir(self._bogus_dir)
- self._delete_bogus_dir = True
- else:
- self._delete_bogus_dir = False
- if log:
- log.debug(' Html: temp config dir: "%s".', self._bogus_dir)
-
- def _teardown_bogus_dir(self, log):
- if self._delete_bogus_dir:
- shutil.rmtree(self._bogus_dir, True)
- if log:
- log.debug(' Html: removed temp config dir: "%s".', self._bogus_dir)
- self._bogus_dir = None
-
- def diff_for_file(self, path, log=None):
- self._setup_bogus_dir(log)
- try:
- args = ['svn', 'diff']
- if self._bogus_dir:
- args += ['--config-dir', self._bogus_dir]
- args.append(path)
- return self.run(args)
- finally:
- self._teardown_bogus_dir(log)
-
- def show_head(self, path):
- return self.run(['svn', 'cat', '-r', 'BASE', path], decode_output=False)
-
- def _repository_url(self):
- return self.value_from_svn_info(self.checkout_root, 'URL')
-
- def apply_reverse_diff(self, revision):
- # '-c -revision' applies the inverse diff of 'revision'
- svn_merge_args = ['svn', 'merge', '--non-interactive', '-c', '-%s' % revision, self._repository_url()]
- 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?
- self.run(svn_merge_args)
-
- def revert_files(self, file_paths):
- # FIXME: This should probably use cwd=self.checkout_root.
- self.run(['svn', 'revert'] + file_paths)
-
- def commit_with_message(self, message, username=None, git_commit=None, force_squash=False):
- # git-commit and force are not used by SVN.
- if self.dryrun:
- # Return a string which looks like a commit so that things which parse this output will succeed.
- return "Dry run, no commit.\nCommitted revision 0."
-
- svn_commit_args = ["svn", "commit"]
-
- if not username and not self.has_authorization_for_realm():
- raise AuthenticationError(self.svn_server_host)
- if username:
- svn_commit_args.extend(["--username", username])
-
- svn_commit_args.extend(["-m", message])
- # FIXME: Should this use cwd=self.checkout_root?
- 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 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
- # http://svnbook.red-bean.com/en/1.0/ch03s03.html
- return self.svn_commit_log('BASE')
-
- def propset(self, pname, pvalue, path):
- dir, base = os.path.split(path)
- return self.run(['svn', 'pset', pname, pvalue, base], cwd=dir)
-
- def propget(self, pname, path):
- dir, base = os.path.split(path)
- return self.run(['svn', 'pget', pname, base], cwd=dir).encode('utf-8').rstrip("\n")
-
-
-# All git-specific logic should go here.
-class Git(SCM):
- def __init__(self, cwd):
- SCM.__init__(self, cwd)
-
- @classmethod
- def in_working_directory(cls, path):
- return run_command(['git', 'rev-parse', '--is-inside-work-tree'], cwd=path, error_handler=Executive.ignore_error).rstrip() == "true"
-
- @classmethod
- def find_checkout_root(cls, path):
- # "git rev-parse --show-cdup" would be another way to get to the root
- (checkout_root, dot_git) = os.path.split(run_command(['git', 'rev-parse', '--git-dir'], cwd=(path or "./")))
- # If we were using 2.6 # checkout_root = os.path.relpath(checkout_root, path)
- if not os.path.isabs(checkout_root): # Sometimes git returns relative paths
- checkout_root = os.path.join(path, checkout_root)
- return checkout_root
-
- @classmethod
- def to_object_name(cls, filepath):
- root_end_with_slash = os.path.join(cls.find_checkout_root(os.path.dirname(filepath)), '')
- return filepath.replace(root_end_with_slash, '')
-
- @classmethod
- def read_git_config(cls, key):
- # FIXME: This should probably use cwd=self.checkout_root.
- # Pass --get-all for cases where the config has multiple values
- return run_command(["git", "config", "--get-all", key],
- error_handler=Executive.ignore_error).rstrip('\n')
-
- @staticmethod
- def commit_success_regexp():
- return "^Committed r(?P<svn_revision>\d+)$"
-
- def discard_local_commits(self):
- # FIXME: This should probably use cwd=self.checkout_root
- self.run(['git', 'reset', '--hard', self.remote_branch_ref()])
-
- def local_commits(self):
- # FIXME: This should probably use cwd=self.checkout_root
- return self.run(['git', 'log', '--pretty=oneline', 'HEAD...' + self.remote_branch_ref()]).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 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
- self.run(['git', 'reset', '--hard', 'HEAD'])
- # Aborting rebase even though this does not match working_directory_is_clean
- if self.rebase_in_progress():
- 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 self.run is fine.
- return ["git", "diff", "--name-status", "HEAD"]
-
- def _status_regexp(self, expected_types):
- return '^(?P<status>[%s])\t(?P<filename>.+)$' % expected_types
-
- def add(self, path, return_exit_code=False):
- return self.run(["git", "add", path], return_exit_code=return_exit_code)
-
- def delete(self, path):
- return self.run(["git", "rm", "-f", path])
-
- def _assert_synced(self):
- if len(run_command(['git', 'rev-list', '--max-count=1', self.remote_branch_ref(), '^HEAD'])):
- raise ScriptError(message="Not fully merged/rebased to %s. This branch needs to be synced first." % self.remote_branch_ref())
-
- def merge_base(self, git_commit):
- if git_commit:
- # Special-case HEAD.. to mean working-copy changes only.
- if git_commit.upper() == 'HEAD..':
- return 'HEAD'
-
- if '..' not in git_commit:
- git_commit = git_commit + "^.." + git_commit
- return git_commit
-
- self._assert_synced()
- return self.remote_merge_base()
-
- def changed_files(self, git_commit=None):
- status_command = ['git', 'diff', '-r', '--name-status', '-C', '-M', "--no-ext-diff", "--full-index", self.merge_base(git_commit)]
- return self.run_status_and_extract_filenames(status_command, self._status_regexp("ADM"))
-
- def _changes_files_for_commit(self, git_commit):
- # --pretty="format:" makes git show not print the commit log header,
- 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:]
-
- def changed_files_for_revision(self, revision):
- commit_id = self.git_commit_from_svn_revision(revision)
- return self._changes_files_for_commit(commit_id)
-
- def revisions_changing_file(self, path, limit=5):
- # git rev-list head --remove-empty --limit=5 -- path would be equivalent.
- commit_ids = self.run(["git", "log", "--remove-empty", "--pretty=format:%H", "-%s" % limit, "--", path]).splitlines()
- return filter(lambda revision: revision, map(self.svn_revision_from_git_commit, commit_ids))
-
- def conflicted_files(self):
- # We do not need to pass decode_output for this diff command
- # as we're passing --name-status which does not output any data.
- status_command = ['git', 'diff', '--name-status', '-C', '-M', '--diff-filter=U']
- return self.run_status_and_extract_filenames(status_command, self._status_regexp("U"))
-
- def added_files(self):
- return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("A"))
-
- def deleted_files(self):
- return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("D"))
-
- @staticmethod
- def supports_local_commits():
- return True
-
- def display_name(self):
- return "git"
-
- def create_patch(self, git_commit=None, changed_files=[]):
- """Returns a byte array (str()) representing the patch file.
- Patch files are effectively binary since they may contain
- files of multiple different encodings."""
- return self.run(['git', 'diff', '--binary', "--no-ext-diff", "--full-index", "-M", self.merge_base(git_commit), "--"] + changed_files, decode_output=False, cwd=self.checkout_root)
-
- def _run_git_svn_find_rev(self, arg):
- # git svn find-rev always exits 0, even when the revision or commit is not found.
- return self.run(['git', 'svn', 'find-rev', arg], cwd=self.checkout_root).rstrip()
-
- def _string_to_int_or_none(self, string):
- try:
- return int(string)
- except ValueError, e:
- return None
-
- @memoized
- def git_commit_from_svn_revision(self, svn_revision):
- git_commit = self._run_git_svn_find_rev('r%s' % svn_revision)
- if not git_commit:
- # FIXME: Alternatively we could offer to update the checkout? Or return None?
- raise ScriptError(message='Failed to find git commit for revision %s, your checkout likely needs an update.' % svn_revision)
- return git_commit
-
- @memoized
- def svn_revision_from_git_commit(self, git_commit):
- svn_revision = self._run_git_svn_find_rev(git_commit)
- return self._string_to_int_or_none(svn_revision)
-
- def contents_at_revision(self, path, revision):
- """Returns a byte array (str()) containing the contents
- of path @ revision in the repository."""
- 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)
- return self.create_patch(git_commit)
-
- def diff_for_file(self, path, log=None):
- return self.run(['git', 'diff', 'HEAD', '--', path])
-
- def show_head(self, path):
- return self.run(['git', 'show', 'HEAD:' + self.to_object_name(path)], decode_output=False)
-
- def committer_email_for_revision(self, revision):
- git_commit = self.git_commit_from_svn_revision(revision)
- 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]
-
- def apply_reverse_diff(self, revision):
- # 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.
- self.run(['git', 'revert', '--no-commit', git_commit], error_handler=Executive.ignore_error)
-
- def revert_files(self, file_paths):
- self.run(['git', 'checkout', 'HEAD'] + file_paths)
-
- def _assert_can_squash(self, working_directory_is_clean):
- squash = Git.read_git_config('webkit-patch.commit-should-always-squash')
- should_squash = squash and squash.lower() == "true"
-
- if not should_squash:
- # Only warn if there are actually multiple commits to squash.
- num_local_commits = len(self.local_commits())
- if num_local_commits > 1 or (num_local_commits > 0 and not working_directory_is_clean):
- raise AmbiguousCommitError(num_local_commits, working_directory_is_clean)
-
- def commit_with_message(self, message, username=None, git_commit=None, force_squash=False):
- # Username is ignored during Git commits.
- working_directory_is_clean = self.working_directory_is_clean()
-
- if git_commit:
- # Special-case HEAD.. to mean working-copy changes only.
- if git_commit.upper() == 'HEAD..':
- if working_directory_is_clean:
- raise ScriptError(message="The working copy is not modified. --git-commit=HEAD.. only commits working copy changes.")
- self.commit_locally_with_message(message)
- return self._commit_on_branch(message, 'HEAD')
-
- # Need working directory changes to be committed so we can checkout the merge branch.
- if not working_directory_is_clean:
- # FIXME: webkit-patch land will modify the ChangeLogs to correct the reviewer.
- # That will modify the working-copy and cause us to hit this error.
- # The ChangeLog modification could be made to modify the existing local commit.
- raise ScriptError(message="Working copy is modified. Cannot commit individual git_commits.")
- return self._commit_on_branch(message, git_commit)
-
- if not force_squash:
- self._assert_can_squash(working_directory_is_clean)
- self._assert_synced()
- self.run(['git', 'reset', '--soft', self.remote_branch_ref()])
- self.commit_locally_with_message(message)
- return self.push_local_commits_to_server()
-
- def _commit_on_branch(self, message, git_commit):
- 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])
-
- # We want to squash all this branch's commits into one commit with the proper description.
- # We do this by doing a "merge --squash" into a new commit branch, then dcommitting that.
- MERGE_BRANCH_NAME = 'webkit-patch-land'
- self.delete_branch(MERGE_BRANCH_NAME)
-
- # We might be in a directory that's present in this branch but not in the
- # trunk. Move up to the top of the tree so that git commands that expect a
- # valid CWD won't fail after we check out the merge branch.
- os.chdir(self.checkout_root)
-
- # Stuff our change into the merge branch.
- # We wrap in a try...finally block so if anything goes wrong, we clean up the branches.
- commit_succeeded = True
- try:
- self.run(['git', 'checkout', '-q', '-b', MERGE_BRANCH_NAME, self.remote_branch_ref()])
-
- 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.
- self.run(['git', 'cherry-pick', '--no-commit', commit])
-
- self.run(['git', 'commit', '-m', message])
- output = self.push_local_commits_to_server()
- except Exception, e:
- log("COMMIT FAILED: " + str(e))
- output = "Commit failed."
- commit_succeeded = False
- finally:
- # And then swap back to the original branch and clean up.
- self.clean_working_directory()
- self.run(['git', 'checkout', '-q', branch_name])
- self.delete_branch(MERGE_BRANCH_NAME)
-
- return output
-
- def svn_commit_log(self, svn_revision):
- svn_revision = self.strip_r_from_svn_revision(svn_revision)
- return self.run(['git', 'svn', 'log', '-r', svn_revision])
-
- def last_svn_commit_log(self):
- return self.run(['git', 'svn', 'log', '--limit=1'])
-
- # Git-specific methods:
- def _branch_ref_exists(self, branch_ref):
- return self.run(['git', 'show-ref', '--quiet', '--verify', branch_ref], return_exit_code=True) == 0
-
- def delete_branch(self, branch_name):
- if self._branch_ref_exists('refs/heads/' + branch_name):
- self.run(['git', 'branch', '-D', branch_name])
-
- def remote_merge_base(self):
- return self.run(['git', 'merge-base', self.remote_branch_ref(), 'HEAD']).strip()
-
- def remote_branch_ref(self):
- # Use references so that we can avoid collisions, e.g. we don't want to operate on refs/heads/trunk if it exists.
- remote_branch_refs = Git.read_git_config('svn-remote.svn.fetch')
- if not remote_branch_refs:
- remote_master_ref = 'refs/remotes/origin/master'
- if not self._branch_ref_exists(remote_master_ref):
- raise ScriptError(message="Can't find a branch to diff against. svn-remote.svn.fetch is not in the git config and %s does not exist" % remote_master_ref)
- return remote_master_ref
-
- # FIXME: What's the right behavior when there are multiple svn-remotes listed?
- # For now, just use the first one.
- first_remote_branch_ref = remote_branch_refs.split('\n')[0]
- return first_remote_branch_ref.split(':')[1]
-
- def commit_locally_with_message(self, 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 = 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"
- return output
-
- # This function supports the following argument formats:
- # no args : rev-list trunk..HEAD
- # A..B : rev-list A..B
- # A...B : error!
- # A B : [A, B] (different from git diff, which would use "rev-list A..B")
- def commit_ids_from_commitish_arguments(self, args):
- if not len(args):
- args.append('%s..HEAD' % self.remote_branch_ref())
-
- commit_ids = []
- for commitish in args:
- if '...' in commitish:
- raise ScriptError(message="'...' is not supported (found in '%s'). Did you mean '..'?" % commitish)
- elif '..' in commitish:
- commit_ids += reversed(self.run(['git', 'rev-list', commitish]).splitlines())
- else:
- # Turn single commits or branch or tag names into commit ids.
- 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 = self.run(['git', 'cat-file', 'commit', commit_id]).splitlines()
-
- # Skip the git headers.
- first_line_after_headers = 0
- for line in commit_lines:
- first_line_after_headers += 1
- if line == "":
- break
- return CommitMessage(commit_lines[first_line_after_headers:])
-
- def files_changed_summary_for_commit(self, commit_id):
- return self.run(['git', 'diff-tree', '--shortstat', '--no-commit-id', commit_id])
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/scm_unittest.py b/WebKitTools/Scripts/webkitpy/common/checkout/scm_unittest.py
deleted file mode 100644
index 46a2acf..0000000
--- a/WebKitTools/Scripts/webkitpy/common/checkout/scm_unittest.py
+++ /dev/null
@@ -1,1291 +0,0 @@
-# Copyright (C) 2009 Google Inc. All rights reserved.
-# Copyright (C) 2009 Apple Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import with_statement
-
-import base64
-import codecs
-import getpass
-import os
-import os.path
-import re
-import stat
-import sys
-import subprocess
-import tempfile
-import unittest
-import urllib
-import shutil
-
-from datetime import date
-from webkitpy.common.checkout.api import Checkout
-from webkitpy.common.checkout.scm import detect_scm_system, SCM, SVN, CheckoutNeedsUpdate, commit_error_handler, AuthenticationError, AmbiguousCommitError, find_checkout_root, default_scm
-from webkitpy.common.config.committers import Committer # FIXME: This should not be needed
-from webkitpy.common.net.bugzilla import Attachment # FIXME: This should not be needed
-from webkitpy.common.system.executive import Executive, run_command, ScriptError
-from webkitpy.common.system.outputcapture import OutputCapture
-
-# Eventually we will want to write tests which work for both scms. (like update_webkit, changed_files, etc.)
-# Perhaps through some SCMTest base-class which both SVNTest and GitTest inherit from.
-
-# FIXME: This should be unified into one of the executive.py commands!
-# Callers could use run_and_throw_if_fail(args, cwd=cwd, quiet=True)
-def run_silent(args, cwd=None):
- # Note: Not thread safe: http://bugs.python.org/issue2320
- process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd)
- process.communicate() # ignore output
- exit_code = process.wait()
- if exit_code:
- raise ScriptError('Failed to run "%s" exit_code: %d cwd: %s' % (args, exit_code, cwd))
-
-
-def write_into_file_at_path(file_path, contents, encoding="utf-8"):
- if encoding:
- with codecs.open(file_path, "w", encoding) as file:
- file.write(contents)
- else:
- with open(file_path, "w") as file:
- file.write(contents)
-
-
-def read_from_path(file_path, encoding="utf-8"):
- with codecs.open(file_path, "r", encoding) as file:
- return file.read()
-
-
-def _make_diff(command, *args):
- # We use this wrapper to disable output decoding. diffs should be treated as
- # binary files since they may include text files of multiple differnet encodings.
- return run_command([command, "diff"] + list(args), decode_output=False)
-
-
-def _svn_diff(*args):
- return _make_diff("svn", *args)
-
-
-def _git_diff(*args):
- return _make_diff("git", *args)
-
-
-# Exists to share svn repository creation code between the git and svn tests
-class SVNTestRepository:
- @classmethod
- def _svn_add(cls, path):
- run_command(["svn", "add", path])
-
- @classmethod
- def _svn_commit(cls, message):
- run_command(["svn", "commit", "--quiet", "--message", message])
-
- @classmethod
- def _setup_test_commits(cls, test_object):
- # Add some test commits
- os.chdir(test_object.svn_checkout_path)
-
- write_into_file_at_path("test_file", "test1")
- cls._svn_add("test_file")
- cls._svn_commit("initial commit")
-
- write_into_file_at_path("test_file", "test1test2")
- # This used to be the last commit, but doing so broke
- # GitTest.test_apply_git_patch which use the inverse diff of the last commit.
- # svn-apply fails to remove directories in Git, see:
- # https://bugs.webkit.org/show_bug.cgi?id=34871
- os.mkdir("test_dir")
- # Slash should always be the right path separator since we use cygwin on Windows.
- test_file3_path = "test_dir/test_file3"
- write_into_file_at_path(test_file3_path, "third file")
- cls._svn_add("test_dir")
- cls._svn_commit("second commit")
-
- write_into_file_at_path("test_file", "test1test2test3\n")
- write_into_file_at_path("test_file2", "second file")
- cls._svn_add("test_file2")
- cls._svn_commit("third commit")
-
- # This 4th commit is used to make sure that our patch file handling
- # code correctly treats patches as binary and does not attempt to
- # decode them assuming they're utf-8.
- write_into_file_at_path("test_file", u"latin1 test: \u00A0\n", "latin1")
- write_into_file_at_path("test_file2", u"utf-8 test: \u00A0\n", "utf-8")
- cls._svn_commit("fourth commit")
-
- # svn does not seem to update after commit as I would expect.
- run_command(['svn', 'update'])
-
- @classmethod
- def setup(cls, test_object):
- # Create an test SVN repository
- test_object.svn_repo_path = tempfile.mkdtemp(suffix="svn_test_repo")
- test_object.svn_repo_url = "file://%s" % test_object.svn_repo_path # Not sure this will work on windows
- # git svn complains if we don't pass --pre-1.5-compatible, not sure why:
- # Expected FS format '2'; found format '3' at /usr/local/libexec/git-core//git-svn line 1477
- run_command(['svnadmin', 'create', '--pre-1.5-compatible', test_object.svn_repo_path])
-
- # Create a test svn checkout
- test_object.svn_checkout_path = tempfile.mkdtemp(suffix="svn_test_checkout")
- run_command(['svn', 'checkout', '--quiet', test_object.svn_repo_url, test_object.svn_checkout_path])
-
- # Create and checkout a trunk dir to match the standard svn configuration to match git-svn's expectations
- os.chdir(test_object.svn_checkout_path)
- os.mkdir('trunk')
- cls._svn_add('trunk')
- # We can add tags and branches as well if we ever need to test those.
- cls._svn_commit('add trunk')
-
- # Change directory out of the svn checkout so we can delete the checkout directory.
- # _setup_test_commits will CD back to the svn checkout directory.
- os.chdir('/')
- run_command(['rm', '-rf', test_object.svn_checkout_path])
- run_command(['svn', 'checkout', '--quiet', test_object.svn_repo_url + '/trunk', test_object.svn_checkout_path])
-
- cls._setup_test_commits(test_object)
-
- @classmethod
- def tear_down(cls, test_object):
- run_command(['rm', '-rf', test_object.svn_repo_path])
- run_command(['rm', '-rf', test_object.svn_checkout_path])
-
- # Now that we've deleted the checkout paths, cwddir may be invalid
- # Change back to a valid directory so that later calls to os.getcwd() do not fail.
- os.chdir(detect_scm_system(os.path.dirname(__file__)).checkout_root)
-
-
-class StandaloneFunctionsTest(unittest.TestCase):
- """This class tests any standalone/top-level functions in the package."""
- def setUp(self):
- self.orig_cwd = os.path.abspath(os.getcwd())
- self.orig_abspath = os.path.abspath
-
- # We capture but ignore the output from stderr to reduce unwanted
- # logging.
- self.output = OutputCapture()
- self.output.capture_output()
-
- def tearDown(self):
- os.chdir(self.orig_cwd)
- os.path.abspath = self.orig_abspath
- self.output.restore_output()
-
- def test_find_checkout_root(self):
- # Test from inside the tree.
- os.chdir(sys.path[0])
- dir = find_checkout_root()
- self.assertNotEqual(dir, None)
- self.assertTrue(os.path.exists(dir))
-
- # Test from outside the tree.
- os.chdir(os.path.expanduser("~"))
- dir = find_checkout_root()
- self.assertNotEqual(dir, None)
- self.assertTrue(os.path.exists(dir))
-
- # Mock out abspath() to test being not in a checkout at all.
- os.path.abspath = lambda x: "/"
- self.assertRaises(SystemExit, find_checkout_root)
- os.path.abspath = self.orig_abspath
-
- def test_default_scm(self):
- # Test from inside the tree.
- os.chdir(sys.path[0])
- scm = default_scm()
- self.assertNotEqual(scm, None)
-
- # Test from outside the tree.
- os.chdir(os.path.expanduser("~"))
- dir = find_checkout_root()
- self.assertNotEqual(dir, None)
-
- # Mock out abspath() to test being not in a checkout at all.
- os.path.abspath = lambda x: "/"
- self.assertRaises(SystemExit, default_scm)
- os.path.abspath = self.orig_abspath
-
-# For testing the SCM baseclass directly.
-class SCMClassTests(unittest.TestCase):
- def setUp(self):
- self.dev_null = open(os.devnull, "w") # Used to make our Popen calls quiet.
-
- def tearDown(self):
- self.dev_null.close()
-
- def test_run_command_with_pipe(self):
- input_process = subprocess.Popen(['echo', 'foo\nbar'], stdout=subprocess.PIPE, stderr=self.dev_null)
- self.assertEqual(run_command(['grep', 'bar'], input=input_process.stdout), "bar\n")
-
- # Test the non-pipe case too:
- self.assertEqual(run_command(['grep', 'bar'], input="foo\nbar"), "bar\n")
-
- command_returns_non_zero = ['/bin/sh', '--invalid-option']
- # Test when the input pipe process fails.
- input_process = subprocess.Popen(command_returns_non_zero, stdout=subprocess.PIPE, stderr=self.dev_null)
- self.assertTrue(input_process.poll() != 0)
- self.assertRaises(ScriptError, run_command, ['grep', 'bar'], input=input_process.stdout)
-
- # Test when the run_command process fails.
- input_process = subprocess.Popen(['echo', 'foo\nbar'], stdout=subprocess.PIPE, stderr=self.dev_null) # grep shows usage and calls exit(2) when called w/o arguments.
- self.assertRaises(ScriptError, run_command, command_returns_non_zero, input=input_process.stdout)
-
- def test_error_handlers(self):
- git_failure_message="Merge conflict during commit: Your file or directory 'WebCore/ChangeLog' is probably out-of-date: resource out of date; try updating at /usr/local/libexec/git-core//git-svn line 469"
- svn_failure_message="""svn: Commit failed (details follow):
-svn: File or directory 'ChangeLog' is out of date; try updating
-svn: resource out of date; try updating
-"""
- command_does_not_exist = ['does_not_exist', 'invalid_option']
- self.assertRaises(OSError, run_command, command_does_not_exist)
- self.assertRaises(OSError, run_command, command_does_not_exist, error_handler=Executive.ignore_error)
-
- command_returns_non_zero = ['/bin/sh', '--invalid-option']
- self.assertRaises(ScriptError, run_command, command_returns_non_zero)
- # Check if returns error text:
- self.assertTrue(run_command(command_returns_non_zero, error_handler=Executive.ignore_error))
-
- self.assertRaises(CheckoutNeedsUpdate, commit_error_handler, ScriptError(output=git_failure_message))
- self.assertRaises(CheckoutNeedsUpdate, commit_error_handler, ScriptError(output=svn_failure_message))
- self.assertRaises(ScriptError, commit_error_handler, ScriptError(output='blah blah blah'))
-
-
-# GitTest and SVNTest inherit from this so any test_ methods here will be run once for this class and then once for each subclass.
-class SCMTest(unittest.TestCase):
- def _create_patch(self, patch_contents):
- # FIXME: This code is brittle if the Attachment API changes.
- attachment = Attachment({"bug_id": 12345}, None)
- attachment.contents = lambda: patch_contents
-
- joe_cool = Committer(name="Joe Cool", email_or_emails=None)
- attachment.reviewer = lambda: joe_cool
-
- return attachment
-
- def _setup_webkittools_scripts_symlink(self, local_scm):
- webkit_scm = detect_scm_system(os.path.dirname(os.path.abspath(__file__)))
- webkit_scripts_directory = webkit_scm.scripts_directory()
- local_scripts_directory = local_scm.scripts_directory()
- os.mkdir(os.path.dirname(local_scripts_directory))
- os.symlink(webkit_scripts_directory, local_scripts_directory)
-
- # Tests which both GitTest and SVNTest should run.
- # FIXME: There must be a simpler way to add these w/o adding a wrapper method to both subclasses
-
- def _shared_test_changed_files(self):
- write_into_file_at_path("test_file", "changed content")
- self.assertEqual(self.scm.changed_files(), ["test_file"])
- write_into_file_at_path("test_dir/test_file3", "new stuff")
- self.assertEqual(self.scm.changed_files(), ["test_dir/test_file3", "test_file"])
- old_cwd = os.getcwd()
- os.chdir("test_dir")
- # Validate that changed_files does not change with our cwd, see bug 37015.
- self.assertEqual(self.scm.changed_files(), ["test_dir/test_file3", "test_file"])
- os.chdir(old_cwd)
-
- def _shared_test_added_files(self):
- write_into_file_at_path("test_file", "changed content")
- self.assertEqual(self.scm.added_files(), [])
-
- write_into_file_at_path("added_file", "new stuff")
- self.scm.add("added_file")
-
- os.mkdir("added_dir")
- write_into_file_at_path("added_dir/added_file2", "new stuff")
- self.scm.add("added_dir")
-
- # SVN reports directory changes, Git does not.
- added_files = self.scm.added_files()
- if "added_dir" in added_files:
- added_files.remove("added_dir")
- self.assertEqual(added_files, ["added_dir/added_file2", "added_file"])
-
- # Test also to make sure clean_working_directory removes added files
- self.scm.clean_working_directory()
- self.assertEqual(self.scm.added_files(), [])
- self.assertFalse(os.path.exists("added_file"))
- self.assertFalse(os.path.exists("added_dir"))
-
- def _shared_test_changed_files_for_revision(self):
- # SVN reports directory changes, Git does not.
- changed_files = self.scm.changed_files_for_revision(3)
- if "test_dir" in changed_files:
- changed_files.remove("test_dir")
- self.assertEqual(changed_files, ["test_dir/test_file3", "test_file"])
- self.assertEqual(sorted(self.scm.changed_files_for_revision(4)), sorted(["test_file", "test_file2"])) # Git and SVN return different orders.
- self.assertEqual(self.scm.changed_files_for_revision(2), ["test_file"])
-
- def _shared_test_contents_at_revision(self):
- self.assertEqual(self.scm.contents_at_revision("test_file", 3), "test1test2")
- self.assertEqual(self.scm.contents_at_revision("test_file", 4), "test1test2test3\n")
-
- # Verify that contents_at_revision returns a byte array, aka str():
- self.assertEqual(self.scm.contents_at_revision("test_file", 5), u"latin1 test: \u00A0\n".encode("latin1"))
- self.assertEqual(self.scm.contents_at_revision("test_file2", 5), u"utf-8 test: \u00A0\n".encode("utf-8"))
-
- self.assertEqual(self.scm.contents_at_revision("test_file2", 4), "second file")
- # Files which don't exist:
- # Currently we raise instead of returning None because detecting the difference between
- # "file not found" and any other error seems impossible with svn (git seems to expose such through the return code).
- self.assertRaises(ScriptError, self.scm.contents_at_revision, "test_file2", 2)
- self.assertRaises(ScriptError, self.scm.contents_at_revision, "does_not_exist", 2)
-
- def _shared_test_revisions_changing_file(self):
- self.assertEqual(self.scm.revisions_changing_file("test_file"), [5, 4, 3, 2])
- self.assertRaises(ScriptError, self.scm.revisions_changing_file, "non_existent_file")
-
- def _shared_test_committer_email_for_revision(self):
- self.assertEqual(self.scm.committer_email_for_revision(3), getpass.getuser()) # Committer "email" will be the current user
-
- def _shared_test_reverse_diff(self):
- self._setup_webkittools_scripts_symlink(self.scm) # Git's apply_reverse_diff uses resolve-ChangeLogs
- # Only test the simple case, as any other will end up with conflict markers.
- self.scm.apply_reverse_diff('5')
- self.assertEqual(read_from_path('test_file'), "test1test2test3\n")
-
- def _shared_test_diff_for_revision(self):
- # Patch formats are slightly different between svn and git, so just regexp for things we know should be there.
- r3_patch = self.scm.diff_for_revision(4)
- self.assertTrue(re.search('test3', r3_patch))
- self.assertFalse(re.search('test4', r3_patch))
- self.assertTrue(re.search('test2', r3_patch))
- self.assertTrue(re.search('test2', self.scm.diff_for_revision(3)))
-
- def _shared_test_svn_apply_git_patch(self):
- self._setup_webkittools_scripts_symlink(self.scm)
- git_binary_addition = """diff --git a/fizzbuzz7.gif b/fizzbuzz7.gif
-new file mode 100644
-index 0000000000000000000000000000000000000000..64a9532e7794fcd791f6f12157406d90
-60151690
-GIT binary patch
-literal 512
-zcmZ?wbhEHbRAx|MU|?iW{Kxc~?KofD;ckY;H+&5HnHl!!GQMD7h+sU{_)e9f^V3c?
-zhJP##HdZC#4K}7F68@!1jfWQg2daCm-gs#3|JREDT>c+pG4L<_2;w##WMO#ysPPap
-zLqpAf1OE938xAsSp4!5f-o><?VKe(#0jEcwfHGF4%M1^kRs14oVBp2ZEL{E1N<-zJ
-zsfLmOtKta;2_;2c#^S1-8cf<nb!QnGl>c!Xe6RXvrEtAWBvSDTgTO1j3vA31Puw!A
-zs(87q)j_mVDTqBo-P+03-P5mHCEnJ+x}YdCuS7#bCCyePUe(ynK+|4b-3qK)T?Z&)
-zYG+`tl4h?GZv_$t82}X4*DTE|$;{DEiPyF@)U-1+FaX++T9H{&%cag`W1|zVP@`%b
-zqiSkp6{BTpWTkCr!=<C6Q=?#~R8^JfrliAF6Q^gV9Iup8RqCXqqhqC`qsyhk<-nlB
-z00f{QZvfK&|Nm#oZ0TQl`Yr$BIa6A@16O26ud7H<QM=xl`toLKnz-3h@9c9q&wm|X
-z{89I|WPyD!*M?gv?q`;L=2YFeXrJQNti4?}s!zFo=5CzeBxC69xA<zrjP<wUcCRh4
-ptUl-ZG<%a~#LwkIWv&q!KSCH7tQ8cJDiw+|GV?MN)RjY50RTb-xvT&H
-
-literal 0
-HcmV?d00001
-
-"""
- self.checkout.apply_patch(self._create_patch(git_binary_addition))
- added = read_from_path('fizzbuzz7.gif', encoding=None)
- self.assertEqual(512, len(added))
- self.assertTrue(added.startswith('GIF89a'))
- self.assertTrue('fizzbuzz7.gif' in self.scm.changed_files())
-
- # The file already exists.
- self.assertRaises(ScriptError, self.checkout.apply_patch, self._create_patch(git_binary_addition))
-
- git_binary_modification = """diff --git a/fizzbuzz7.gif b/fizzbuzz7.gif
-index 64a9532e7794fcd791f6f12157406d9060151690..323fae03f4606ea9991df8befbb2fca7
-GIT binary patch
-literal 7
-OcmYex&reD$;sO8*F9L)B
-
-literal 512
-zcmZ?wbhEHbRAx|MU|?iW{Kxc~?KofD;ckY;H+&5HnHl!!GQMD7h+sU{_)e9f^V3c?
-zhJP##HdZC#4K}7F68@!1jfWQg2daCm-gs#3|JREDT>c+pG4L<_2;w##WMO#ysPPap
-zLqpAf1OE938xAsSp4!5f-o><?VKe(#0jEcwfHGF4%M1^kRs14oVBp2ZEL{E1N<-zJ
-zsfLmOtKta;2_;2c#^S1-8cf<nb!QnGl>c!Xe6RXvrEtAWBvSDTgTO1j3vA31Puw!A
-zs(87q)j_mVDTqBo-P+03-P5mHCEnJ+x}YdCuS7#bCCyePUe(ynK+|4b-3qK)T?Z&)
-zYG+`tl4h?GZv_$t82}X4*DTE|$;{DEiPyF@)U-1+FaX++T9H{&%cag`W1|zVP@`%b
-zqiSkp6{BTpWTkCr!=<C6Q=?#~R8^JfrliAF6Q^gV9Iup8RqCXqqhqC`qsyhk<-nlB
-z00f{QZvfK&|Nm#oZ0TQl`Yr$BIa6A@16O26ud7H<QM=xl`toLKnz-3h@9c9q&wm|X
-z{89I|WPyD!*M?gv?q`;L=2YFeXrJQNti4?}s!zFo=5CzeBxC69xA<zrjP<wUcCRh4
-ptUl-ZG<%a~#LwkIWv&q!KSCH7tQ8cJDiw+|GV?MN)RjY50RTb-xvT&H
-
-"""
- self.checkout.apply_patch(self._create_patch(git_binary_modification))
- modified = read_from_path('fizzbuzz7.gif', encoding=None)
- self.assertEqual('foobar\n', modified)
- self.assertTrue('fizzbuzz7.gif' in self.scm.changed_files())
-
- # Applying the same modification should fail.
- self.assertRaises(ScriptError, self.checkout.apply_patch, self._create_patch(git_binary_modification))
-
- git_binary_deletion = """diff --git a/fizzbuzz7.gif b/fizzbuzz7.gif
-deleted file mode 100644
-index 323fae0..0000000
-GIT binary patch
-literal 0
-HcmV?d00001
-
-literal 7
-OcmYex&reD$;sO8*F9L)B
-
-"""
- self.checkout.apply_patch(self._create_patch(git_binary_deletion))
- self.assertFalse(os.path.exists('fizzbuzz7.gif'))
- self.assertFalse('fizzbuzz7.gif' in self.scm.changed_files())
-
- # Cannot delete again.
- self.assertRaises(ScriptError, self.checkout.apply_patch, self._create_patch(git_binary_deletion))
-
- def _shared_test_add_recursively(self):
- os.mkdir("added_dir")
- write_into_file_at_path("added_dir/added_file", "new stuff")
- self.scm.add("added_dir/added_file")
- self.assertTrue("added_dir/added_file" in self.scm.added_files())
-
-class SVNTest(SCMTest):
-
- @staticmethod
- def _set_date_and_reviewer(changelog_entry):
- # Joe Cool matches the reviewer set in SCMTest._create_patch
- changelog_entry = changelog_entry.replace('REVIEWER_HERE', 'Joe Cool')
- # svn-apply will update ChangeLog entries with today's date.
- return changelog_entry.replace('DATE_HERE', date.today().isoformat())
-
- def test_svn_apply(self):
- first_entry = """2009-10-26 Eric Seidel <eric@webkit.org>
-
- Reviewed by Foo Bar.
-
- Most awesome change ever.
-
- * scm_unittest.py:
-"""
- intermediate_entry = """2009-10-27 Eric Seidel <eric@webkit.org>
-
- Reviewed by Baz Bar.
-
- A more awesomer change yet!
-
- * scm_unittest.py:
-"""
- one_line_overlap_patch = """Index: ChangeLog
-===================================================================
---- ChangeLog (revision 5)
-+++ ChangeLog (working copy)
-@@ -1,5 +1,13 @@
- 2009-10-26 Eric Seidel <eric@webkit.org>
-
-+ Reviewed by NOBODY (OOPS!).
-+
-+ Second most awesome change ever.
-+
-+ * scm_unittest.py:
-+
-+2009-10-26 Eric Seidel <eric@webkit.org>
-+
- Reviewed by Foo Bar.
-
- Most awesome change ever.
-"""
- one_line_overlap_entry = """DATE_HERE Eric Seidel <eric@webkit.org>
-
- Reviewed by REVIEWER_HERE.
-
- Second most awesome change ever.
-
- * scm_unittest.py:
-"""
- two_line_overlap_patch = """Index: ChangeLog
-===================================================================
---- ChangeLog (revision 5)
-+++ ChangeLog (working copy)
-@@ -2,6 +2,14 @@
-
- Reviewed by Foo Bar.
-
-+ Second most awesome change ever.
-+
-+ * scm_unittest.py:
-+
-+2009-10-26 Eric Seidel <eric@webkit.org>
-+
-+ Reviewed by Foo Bar.
-+
- Most awesome change ever.
-
- * scm_unittest.py:
-"""
- two_line_overlap_entry = """DATE_HERE Eric Seidel <eric@webkit.org>
-
- Reviewed by Foo Bar.
-
- Second most awesome change ever.
-
- * scm_unittest.py:
-"""
- write_into_file_at_path('ChangeLog', first_entry)
- run_command(['svn', 'add', 'ChangeLog'])
- run_command(['svn', 'commit', '--quiet', '--message', 'ChangeLog commit'])
-
- # Patch files were created against just 'first_entry'.
- # Add a second commit to make svn-apply have to apply the patches with fuzz.
- changelog_contents = "%s\n%s" % (intermediate_entry, first_entry)
- write_into_file_at_path('ChangeLog', changelog_contents)
- run_command(['svn', 'commit', '--quiet', '--message', 'Intermediate commit'])
-
- self._setup_webkittools_scripts_symlink(self.scm)
- self.checkout.apply_patch(self._create_patch(one_line_overlap_patch))
- expected_changelog_contents = "%s\n%s" % (self._set_date_and_reviewer(one_line_overlap_entry), changelog_contents)
- self.assertEquals(read_from_path('ChangeLog'), expected_changelog_contents)
-
- self.scm.revert_files(['ChangeLog'])
- self.checkout.apply_patch(self._create_patch(two_line_overlap_patch))
- expected_changelog_contents = "%s\n%s" % (self._set_date_and_reviewer(two_line_overlap_entry), changelog_contents)
- self.assertEquals(read_from_path('ChangeLog'), expected_changelog_contents)
-
- def setUp(self):
- SVNTestRepository.setup(self)
- os.chdir(self.svn_checkout_path)
- self.scm = detect_scm_system(self.svn_checkout_path)
- # For historical reasons, we test some checkout code here too.
- self.checkout = Checkout(self.scm)
-
- def tearDown(self):
- SVNTestRepository.tear_down(self)
-
- def test_detect_scm_system_relative_url(self):
- scm = detect_scm_system(".")
- # I wanted to assert that we got the right path, but there was some
- # crazy magic with temp folder names that I couldn't figure out.
- self.assertTrue(scm.checkout_root)
-
- def test_create_patch_is_full_patch(self):
- test_dir_path = os.path.join(self.svn_checkout_path, "test_dir2")
- os.mkdir(test_dir_path)
- test_file_path = os.path.join(test_dir_path, 'test_file2')
- write_into_file_at_path(test_file_path, 'test content')
- run_command(['svn', 'add', 'test_dir2'])
-
- # create_patch depends on 'svn-create-patch', so make a dummy version.
- scripts_path = os.path.join(self.svn_checkout_path, 'WebKitTools', 'Scripts')
- os.makedirs(scripts_path)
- create_patch_path = os.path.join(scripts_path, 'svn-create-patch')
- write_into_file_at_path(create_patch_path, '#!/bin/sh\necho $PWD') # We could pass -n to prevent the \n, but not all echo accept -n.
- os.chmod(create_patch_path, stat.S_IXUSR | stat.S_IRUSR)
-
- # Change into our test directory and run the create_patch command.
- os.chdir(test_dir_path)
- scm = detect_scm_system(test_dir_path)
- self.assertEqual(scm.checkout_root, self.svn_checkout_path) # Sanity check that detection worked right.
- patch_contents = scm.create_patch()
- # Our fake 'svn-create-patch' returns $PWD instead of a patch, check that it was executed from the root of the repo.
- self.assertEqual("%s\n" % os.path.realpath(scm.checkout_root), patch_contents) # Add a \n because echo adds a \n.
-
- def test_detection(self):
- scm = detect_scm_system(self.svn_checkout_path)
- self.assertEqual(scm.display_name(), "svn")
- self.assertEqual(scm.supports_local_commits(), False)
-
- def test_apply_small_binary_patch(self):
- patch_contents = """Index: test_file.swf
-===================================================================
-Cannot display: file marked as a binary type.
-svn:mime-type = application/octet-stream
-
-Property changes on: test_file.swf
-___________________________________________________________________
-Name: svn:mime-type
- + application/octet-stream
-
-
-Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==
-"""
- expected_contents = base64.b64decode("Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==")
- self._setup_webkittools_scripts_symlink(self.scm)
- patch_file = self._create_patch(patch_contents)
- self.checkout.apply_patch(patch_file)
- actual_contents = read_from_path("test_file.swf", encoding=None)
- self.assertEqual(actual_contents, expected_contents)
-
- def test_apply_svn_patch(self):
- scm = detect_scm_system(self.svn_checkout_path)
- patch = self._create_patch(_svn_diff("-r5:4"))
- self._setup_webkittools_scripts_symlink(scm)
- Checkout(scm).apply_patch(patch)
-
- def test_apply_svn_patch_force(self):
- scm = detect_scm_system(self.svn_checkout_path)
- patch = self._create_patch(_svn_diff("-r3:5"))
- self._setup_webkittools_scripts_symlink(scm)
- self.assertRaises(ScriptError, Checkout(scm).apply_patch, patch, force=True)
-
- def test_commit_logs(self):
- # Commits have dates and usernames in them, so we can't just direct compare.
- self.assertTrue(re.search('fourth commit', self.scm.last_svn_commit_log()))
- self.assertTrue(re.search('second commit', self.scm.svn_commit_log(3)))
-
- def _shared_test_commit_with_message(self, username=None):
- write_into_file_at_path('test_file', 'more test content')
- commit_text = self.scm.commit_with_message("another test commit", username)
- self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6')
-
- self.scm.dryrun = True
- write_into_file_at_path('test_file', 'still more test content')
- commit_text = self.scm.commit_with_message("yet another test commit", username)
- self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '0')
-
- def test_commit_text_parsing(self):
- self._shared_test_commit_with_message()
-
- def test_commit_with_username(self):
- self._shared_test_commit_with_message("dbates@webkit.org")
-
- def test_commit_without_authorization(self):
- self.scm.has_authorization_for_realm = lambda: False
- self.assertRaises(AuthenticationError, self._shared_test_commit_with_message)
-
- def test_has_authorization_for_realm(self):
- scm = detect_scm_system(self.svn_checkout_path)
- fake_home_dir = tempfile.mkdtemp(suffix="fake_home_dir")
- svn_config_dir_path = os.path.join(fake_home_dir, ".subversion")
- os.mkdir(svn_config_dir_path)
- fake_webkit_auth_file = os.path.join(svn_config_dir_path, "fake_webkit_auth_file")
- write_into_file_at_path(fake_webkit_auth_file, SVN.svn_server_realm)
- self.assertTrue(scm.has_authorization_for_realm(home_directory=fake_home_dir))
- os.remove(fake_webkit_auth_file)
- os.rmdir(svn_config_dir_path)
- os.rmdir(fake_home_dir)
-
- def test_not_have_authorization_for_realm(self):
- scm = detect_scm_system(self.svn_checkout_path)
- fake_home_dir = tempfile.mkdtemp(suffix="fake_home_dir")
- svn_config_dir_path = os.path.join(fake_home_dir, ".subversion")
- os.mkdir(svn_config_dir_path)
- self.assertFalse(scm.has_authorization_for_realm(home_directory=fake_home_dir))
- os.rmdir(svn_config_dir_path)
- os.rmdir(fake_home_dir)
-
- def test_reverse_diff(self):
- self._shared_test_reverse_diff()
-
- def test_diff_for_revision(self):
- self._shared_test_diff_for_revision()
-
- def test_svn_apply_git_patch(self):
- self._shared_test_svn_apply_git_patch()
-
- def test_changed_files(self):
- self._shared_test_changed_files()
-
- def test_changed_files_for_revision(self):
- self._shared_test_changed_files_for_revision()
-
- def test_added_files(self):
- self._shared_test_added_files()
-
- def test_contents_at_revision(self):
- self._shared_test_contents_at_revision()
-
- def test_revisions_changing_file(self):
- self._shared_test_revisions_changing_file()
-
- def test_committer_email_for_revision(self):
- self._shared_test_committer_email_for_revision()
-
- def test_add_recursively(self):
- self._shared_test_add_recursively()
-
- def test_delete(self):
- os.chdir(self.svn_checkout_path)
- self.scm.delete("test_file")
- self.assertTrue("test_file" in self.scm.deleted_files())
-
- def test_propset_propget(self):
- filepath = os.path.join(self.svn_checkout_path, "test_file")
- expected_mime_type = "x-application/foo-bar"
- self.scm.propset("svn:mime-type", expected_mime_type, filepath)
- self.assertEqual(expected_mime_type, self.scm.propget("svn:mime-type", filepath))
-
- def test_show_head(self):
- write_into_file_at_path("test_file", u"Hello!", "utf-8")
- SVNTestRepository._svn_commit("fourth commit")
- self.assertEqual("Hello!", self.scm.show_head('test_file'))
-
- def test_show_head_binary(self):
- data = "\244"
- write_into_file_at_path("binary_file", data, encoding=None)
- self.scm.add("binary_file")
- self.scm.commit_with_message("a test commit")
- self.assertEqual(data, self.scm.show_head('binary_file'))
-
- def do_test_diff_for_file(self):
- write_into_file_at_path('test_file', 'some content')
- self.scm.commit_with_message("a test commit")
- diff = self.scm.diff_for_file('test_file')
- self.assertEqual(diff, "")
-
- write_into_file_at_path("test_file", "changed content")
- diff = self.scm.diff_for_file('test_file')
- self.assertTrue("-some content" in diff)
- self.assertTrue("+changed content" in diff)
-
- def clean_bogus_dir(self):
- self.bogus_dir = self.scm._bogus_dir_name()
- if os.path.exists(self.bogus_dir):
- shutil.rmtree(self.bogus_dir)
-
- def test_diff_for_file_with_existing_bogus_dir(self):
- self.clean_bogus_dir()
- os.mkdir(self.bogus_dir)
- self.do_test_diff_for_file()
- self.assertTrue(os.path.exists(self.bogus_dir))
- shutil.rmtree(self.bogus_dir)
-
- def test_diff_for_file_with_missing_bogus_dir(self):
- self.clean_bogus_dir()
- self.do_test_diff_for_file()
- self.assertFalse(os.path.exists(self.bogus_dir))
-
- def test_svn_lock(self):
- svn_root_lock_path = ".svn/lock"
- write_into_file_at_path(svn_root_lock_path, "", "utf-8")
- # webkit-patch uses a Checkout object and runs update-webkit, just use svn update here.
- self.assertRaises(ScriptError, run_command, ['svn', 'update'])
- self.scm.clean_working_directory()
- self.assertFalse(os.path.exists(svn_root_lock_path))
- run_command(['svn', 'update']) # Should succeed and not raise.
-
-
-class GitTest(SCMTest):
-
- def setUp(self):
- """Sets up fresh git repository with one commit. Then setups a second git
- repo that tracks the first one."""
- self.original_dir = os.getcwd()
-
- self.untracking_checkout_path = tempfile.mkdtemp(suffix="git_test_checkout2")
- run_command(['git', 'init', self.untracking_checkout_path])
-
- os.chdir(self.untracking_checkout_path)
- write_into_file_at_path('foo_file', 'foo')
- run_command(['git', 'add', 'foo_file'])
- run_command(['git', 'commit', '-am', 'dummy commit'])
- self.untracking_scm = detect_scm_system(self.untracking_checkout_path)
-
- self.tracking_git_checkout_path = tempfile.mkdtemp(suffix="git_test_checkout")
- run_command(['git', 'clone', '--quiet', self.untracking_checkout_path, self.tracking_git_checkout_path])
- os.chdir(self.tracking_git_checkout_path)
- self.tracking_scm = detect_scm_system(self.tracking_git_checkout_path)
-
- def tearDown(self):
- # Change back to a valid directory so that later calls to os.getcwd() do not fail.
- os.chdir(self.original_dir)
- run_command(['rm', '-rf', self.tracking_git_checkout_path])
- run_command(['rm', '-rf', self.untracking_checkout_path])
-
- def test_remote_branch_ref(self):
- self.assertEqual(self.tracking_scm.remote_branch_ref(), 'refs/remotes/origin/master')
-
- os.chdir(self.untracking_checkout_path)
- self.assertRaises(ScriptError, self.untracking_scm.remote_branch_ref)
-
- def test_multiple_remotes(self):
- run_command(['git', 'config', '--add', 'svn-remote.svn.fetch', 'trunk:remote1'])
- run_command(['git', 'config', '--add', 'svn-remote.svn.fetch', 'trunk:remote2'])
- self.assertEqual(self.tracking_scm.remote_branch_ref(), 'remote1')
-
-class GitSVNTest(SCMTest):
-
- def _setup_git_checkout(self):
- self.git_checkout_path = tempfile.mkdtemp(suffix="git_test_checkout")
- # --quiet doesn't make git svn silent, so we use run_silent to redirect output
- run_silent(['git', 'svn', 'clone', '-T', 'trunk', self.svn_repo_url, self.git_checkout_path])
- os.chdir(self.git_checkout_path)
-
- def _tear_down_git_checkout(self):
- # Change back to a valid directory so that later calls to os.getcwd() do not fail.
- os.chdir(self.original_dir)
- run_command(['rm', '-rf', self.git_checkout_path])
-
- def setUp(self):
- self.original_dir = os.getcwd()
-
- SVNTestRepository.setup(self)
- self._setup_git_checkout()
- self.scm = detect_scm_system(self.git_checkout_path)
- # For historical reasons, we test some checkout code here too.
- self.checkout = Checkout(self.scm)
-
- def tearDown(self):
- SVNTestRepository.tear_down(self)
- self._tear_down_git_checkout()
-
- def test_detection(self):
- scm = detect_scm_system(self.git_checkout_path)
- self.assertEqual(scm.display_name(), "git")
- self.assertEqual(scm.supports_local_commits(), True)
-
- def test_read_git_config(self):
- key = 'test.git-config'
- value = 'git-config value'
- run_command(['git', 'config', key, value])
- self.assertEqual(self.scm.read_git_config(key), value)
-
- def test_local_commits(self):
- test_file = os.path.join(self.git_checkout_path, 'test_file')
- write_into_file_at_path(test_file, 'foo')
- run_command(['git', 'commit', '-a', '-m', 'local commit'])
-
- self.assertEqual(len(self.scm.local_commits()), 1)
-
- def test_discard_local_commits(self):
- test_file = os.path.join(self.git_checkout_path, 'test_file')
- write_into_file_at_path(test_file, 'foo')
- run_command(['git', 'commit', '-a', '-m', 'local commit'])
-
- self.assertEqual(len(self.scm.local_commits()), 1)
- self.scm.discard_local_commits()
- self.assertEqual(len(self.scm.local_commits()), 0)
-
- def test_delete_branch(self):
- new_branch = 'foo'
-
- run_command(['git', 'checkout', '-b', new_branch])
- self.assertEqual(run_command(['git', 'symbolic-ref', 'HEAD']).strip(), 'refs/heads/' + new_branch)
-
- run_command(['git', 'checkout', '-b', 'bar'])
- self.scm.delete_branch(new_branch)
-
- self.assertFalse(re.search(r'foo', run_command(['git', 'branch'])))
-
- def test_remote_merge_base(self):
- # Diff to merge-base should include working-copy changes,
- # which the diff to svn_branch.. doesn't.
- test_file = os.path.join(self.git_checkout_path, 'test_file')
- write_into_file_at_path(test_file, 'foo')
-
- diff_to_common_base = _git_diff(self.scm.remote_branch_ref() + '..')
- diff_to_merge_base = _git_diff(self.scm.remote_merge_base())
-
- self.assertFalse(re.search(r'foo', diff_to_common_base))
- self.assertTrue(re.search(r'foo', diff_to_merge_base))
-
- def test_rebase_in_progress(self):
- svn_test_file = os.path.join(self.svn_checkout_path, 'test_file')
- write_into_file_at_path(svn_test_file, "svn_checkout")
- run_command(['svn', 'commit', '--message', 'commit to conflict with git commit'], cwd=self.svn_checkout_path)
-
- git_test_file = os.path.join(self.git_checkout_path, 'test_file')
- write_into_file_at_path(git_test_file, "git_checkout")
- run_command(['git', 'commit', '-a', '-m', 'commit to be thrown away by rebase abort'])
-
- # --quiet doesn't make git svn silent, so use run_silent to redirect output
- self.assertRaises(ScriptError, run_silent, ['git', 'svn', '--quiet', 'rebase']) # Will fail due to a conflict leaving us mid-rebase.
-
- scm = detect_scm_system(self.git_checkout_path)
- self.assertTrue(scm.rebase_in_progress())
-
- # Make sure our cleanup works.
- scm.clean_working_directory()
- self.assertFalse(scm.rebase_in_progress())
-
- # Make sure cleanup doesn't throw when no rebase is in progress.
- scm.clean_working_directory()
-
- def test_commitish_parsing(self):
- scm = detect_scm_system(self.git_checkout_path)
-
- # Multiple revisions are cherry-picked.
- self.assertEqual(len(scm.commit_ids_from_commitish_arguments(['HEAD~2'])), 1)
- self.assertEqual(len(scm.commit_ids_from_commitish_arguments(['HEAD', 'HEAD~2'])), 2)
-
- # ... is an invalid range specifier
- self.assertRaises(ScriptError, scm.commit_ids_from_commitish_arguments, ['trunk...HEAD'])
-
- def test_commitish_order(self):
- scm = detect_scm_system(self.git_checkout_path)
-
- commit_range = 'HEAD~3..HEAD'
-
- actual_commits = scm.commit_ids_from_commitish_arguments([commit_range])
- expected_commits = []
- expected_commits += reversed(run_command(['git', 'rev-list', commit_range]).splitlines())
-
- self.assertEqual(actual_commits, expected_commits)
-
- def test_apply_git_patch(self):
- scm = detect_scm_system(self.git_checkout_path)
- # We carefullly pick a diff which does not have a directory addition
- # as currently svn-apply will error out when trying to remove directories
- # in Git: https://bugs.webkit.org/show_bug.cgi?id=34871
- patch = self._create_patch(_git_diff('HEAD..HEAD^'))
- self._setup_webkittools_scripts_symlink(scm)
- Checkout(scm).apply_patch(patch)
-
- def test_apply_git_patch_force(self):
- scm = detect_scm_system(self.git_checkout_path)
- patch = self._create_patch(_git_diff('HEAD~2..HEAD'))
- self._setup_webkittools_scripts_symlink(scm)
- self.assertRaises(ScriptError, Checkout(scm).apply_patch, patch, force=True)
-
- def test_commit_text_parsing(self):
- write_into_file_at_path('test_file', 'more test content')
- commit_text = self.scm.commit_with_message("another test commit")
- self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6')
-
- self.scm.dryrun = True
- write_into_file_at_path('test_file', 'still more test content')
- commit_text = self.scm.commit_with_message("yet another test commit")
- self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '0')
-
- def test_commit_with_message_working_copy_only(self):
- write_into_file_at_path('test_file_commit1', 'more test content')
- run_command(['git', 'add', 'test_file_commit1'])
- scm = detect_scm_system(self.git_checkout_path)
- commit_text = scm.commit_with_message("yet another test commit")
-
- self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertTrue(re.search(r'test_file_commit1', svn_log))
-
- def _one_local_commit(self):
- write_into_file_at_path('test_file_commit1', 'more test content')
- run_command(['git', 'add', 'test_file_commit1'])
- self.scm.commit_locally_with_message("another test commit")
-
- def _one_local_commit_plus_working_copy_changes(self):
- self._one_local_commit()
- write_into_file_at_path('test_file_commit2', 'still more test content')
- run_command(['git', 'add', 'test_file_commit2'])
-
- def _two_local_commits(self):
- self._one_local_commit()
- write_into_file_at_path('test_file_commit2', 'still more test content')
- run_command(['git', 'add', 'test_file_commit2'])
- self.scm.commit_locally_with_message("yet another test commit")
-
- def _three_local_commits(self):
- write_into_file_at_path('test_file_commit0', 'more test content')
- run_command(['git', 'add', 'test_file_commit0'])
- self.scm.commit_locally_with_message("another test commit")
- self._two_local_commits()
-
- def test_revisions_changing_files_with_local_commit(self):
- self._one_local_commit()
- self.assertEquals(self.scm.revisions_changing_file('test_file_commit1'), [])
-
- def test_commit_with_message(self):
- self._one_local_commit_plus_working_copy_changes()
- scm = detect_scm_system(self.git_checkout_path)
- self.assertRaises(AmbiguousCommitError, scm.commit_with_message, "yet another test commit")
- commit_text = scm.commit_with_message("yet another test commit", force_squash=True)
-
- self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertTrue(re.search(r'test_file_commit2', svn_log))
- self.assertTrue(re.search(r'test_file_commit1', svn_log))
-
- def test_commit_with_message_git_commit(self):
- self._two_local_commits()
-
- scm = detect_scm_system(self.git_checkout_path)
- commit_text = scm.commit_with_message("another test commit", git_commit="HEAD^")
- self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
-
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertTrue(re.search(r'test_file_commit1', svn_log))
- self.assertFalse(re.search(r'test_file_commit2', svn_log))
-
- def test_commit_with_message_git_commit_range(self):
- self._three_local_commits()
-
- scm = detect_scm_system(self.git_checkout_path)
- commit_text = scm.commit_with_message("another test commit", git_commit="HEAD~2..HEAD")
- self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
-
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertFalse(re.search(r'test_file_commit0', svn_log))
- self.assertTrue(re.search(r'test_file_commit1', svn_log))
- self.assertTrue(re.search(r'test_file_commit2', svn_log))
-
- def test_changed_files_working_copy_only(self):
- self._one_local_commit_plus_working_copy_changes()
- scm = detect_scm_system(self.git_checkout_path)
- commit_text = scm.commit_with_message("another test commit", git_commit="HEAD..")
- self.assertFalse(re.search(r'test_file_commit1', svn_log))
- self.assertTrue(re.search(r'test_file_commit2', svn_log))
-
- def test_commit_with_message_only_local_commit(self):
- self._one_local_commit()
- scm = detect_scm_system(self.git_checkout_path)
- commit_text = scm.commit_with_message("another test commit")
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertTrue(re.search(r'test_file_commit1', svn_log))
-
- def test_commit_with_message_multiple_local_commits_and_working_copy(self):
- self._two_local_commits()
- write_into_file_at_path('test_file_commit1', 'working copy change')
- scm = detect_scm_system(self.git_checkout_path)
-
- self.assertRaises(AmbiguousCommitError, scm.commit_with_message, "another test commit")
- commit_text = scm.commit_with_message("another test commit", force_squash=True)
-
- self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertTrue(re.search(r'test_file_commit2', svn_log))
- self.assertTrue(re.search(r'test_file_commit1', svn_log))
-
- def test_commit_with_message_git_commit_and_working_copy(self):
- self._two_local_commits()
- write_into_file_at_path('test_file_commit1', 'working copy change')
- scm = detect_scm_system(self.git_checkout_path)
- self.assertRaises(ScriptError, scm.commit_with_message, "another test commit", git_commit="HEAD^")
-
- def test_commit_with_message_multiple_local_commits_always_squash(self):
- self._two_local_commits()
- scm = detect_scm_system(self.git_checkout_path)
- scm._assert_can_squash = lambda working_directory_is_clean: True
- commit_text = scm.commit_with_message("yet another test commit")
- self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
-
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertTrue(re.search(r'test_file_commit2', svn_log))
- self.assertTrue(re.search(r'test_file_commit1', svn_log))
-
- def test_commit_with_message_multiple_local_commits(self):
- self._two_local_commits()
- scm = detect_scm_system(self.git_checkout_path)
- self.assertRaises(AmbiguousCommitError, scm.commit_with_message, "yet another test commit")
- commit_text = scm.commit_with_message("yet another test commit", force_squash=True)
-
- self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
-
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertTrue(re.search(r'test_file_commit2', svn_log))
- self.assertTrue(re.search(r'test_file_commit1', svn_log))
-
- def test_commit_with_message_not_synced(self):
- run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3'])
- self._two_local_commits()
- scm = detect_scm_system(self.git_checkout_path)
- self.assertRaises(AmbiguousCommitError, scm.commit_with_message, "another test commit")
- self.assertRaises(ScriptError, scm.commit_with_message, "another test commit", force_squash=True)
-
- def test_remote_branch_ref(self):
- self.assertEqual(self.scm.remote_branch_ref(), 'refs/remotes/trunk')
-
- def test_reverse_diff(self):
- self._shared_test_reverse_diff()
-
- def test_diff_for_revision(self):
- self._shared_test_diff_for_revision()
-
- def test_svn_apply_git_patch(self):
- self._shared_test_svn_apply_git_patch()
-
- def test_create_patch_local_plus_working_copy(self):
- self._one_local_commit_plus_working_copy_changes()
- scm = detect_scm_system(self.git_checkout_path)
- patch = scm.create_patch()
- self.assertTrue(re.search(r'test_file_commit1', patch))
- self.assertTrue(re.search(r'test_file_commit2', patch))
-
- def test_create_patch(self):
- self._one_local_commit_plus_working_copy_changes()
- scm = detect_scm_system(self.git_checkout_path)
- patch = scm.create_patch()
- self.assertTrue(re.search(r'test_file_commit2', patch))
- self.assertTrue(re.search(r'test_file_commit1', patch))
-
- def test_create_patch_with_changed_files(self):
- self._one_local_commit_plus_working_copy_changes()
- scm = detect_scm_system(self.git_checkout_path)
- patch = scm.create_patch(changed_files=['test_file_commit2'])
- self.assertTrue(re.search(r'test_file_commit2', patch))
-
- def test_create_patch_with_rm_and_changed_files(self):
- self._one_local_commit_plus_working_copy_changes()
- scm = detect_scm_system(self.git_checkout_path)
- os.remove('test_file_commit1')
- patch = scm.create_patch()
- patch_with_changed_files = scm.create_patch(changed_files=['test_file_commit1', 'test_file_commit2'])
- self.assertEquals(patch, patch_with_changed_files)
-
- def test_create_patch_git_commit(self):
- self._two_local_commits()
- scm = detect_scm_system(self.git_checkout_path)
- patch = scm.create_patch(git_commit="HEAD^")
- self.assertTrue(re.search(r'test_file_commit1', patch))
- self.assertFalse(re.search(r'test_file_commit2', patch))
-
- def test_create_patch_git_commit_range(self):
- self._three_local_commits()
- scm = detect_scm_system(self.git_checkout_path)
- patch = scm.create_patch(git_commit="HEAD~2..HEAD")
- self.assertFalse(re.search(r'test_file_commit0', patch))
- self.assertTrue(re.search(r'test_file_commit2', patch))
- self.assertTrue(re.search(r'test_file_commit1', patch))
-
- def test_create_patch_working_copy_only(self):
- self._one_local_commit_plus_working_copy_changes()
- scm = detect_scm_system(self.git_checkout_path)
- patch = scm.create_patch(git_commit="HEAD..")
- self.assertFalse(re.search(r'test_file_commit1', patch))
- self.assertTrue(re.search(r'test_file_commit2', patch))
-
- def test_create_patch_multiple_local_commits(self):
- self._two_local_commits()
- scm = detect_scm_system(self.git_checkout_path)
- patch = scm.create_patch()
- self.assertTrue(re.search(r'test_file_commit2', patch))
- self.assertTrue(re.search(r'test_file_commit1', patch))
-
- def test_create_patch_not_synced(self):
- run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3'])
- self._two_local_commits()
- scm = detect_scm_system(self.git_checkout_path)
- self.assertRaises(ScriptError, scm.create_patch)
-
- def test_create_binary_patch(self):
- # Create a git binary patch and check the contents.
- scm = detect_scm_system(self.git_checkout_path)
- test_file_name = 'binary_file'
- test_file_path = os.path.join(self.git_checkout_path, test_file_name)
- file_contents = ''.join(map(chr, range(256)))
- write_into_file_at_path(test_file_path, file_contents, encoding=None)
- run_command(['git', 'add', test_file_name])
- patch = scm.create_patch()
- self.assertTrue(re.search(r'\nliteral 0\n', patch))
- self.assertTrue(re.search(r'\nliteral 256\n', patch))
-
- # Check if we can apply the created patch.
- run_command(['git', 'rm', '-f', test_file_name])
- self._setup_webkittools_scripts_symlink(scm)
- self.checkout.apply_patch(self._create_patch(patch))
- self.assertEqual(file_contents, read_from_path(test_file_path, encoding=None))
-
- # Check if we can create a patch from a local commit.
- write_into_file_at_path(test_file_path, file_contents, encoding=None)
- run_command(['git', 'add', test_file_name])
- run_command(['git', 'commit', '-m', 'binary diff'])
- patch_from_local_commit = scm.create_patch('HEAD')
- self.assertTrue(re.search(r'\nliteral 0\n', patch_from_local_commit))
- self.assertTrue(re.search(r'\nliteral 256\n', patch_from_local_commit))
-
- def test_changed_files_local_plus_working_copy(self):
- self._one_local_commit_plus_working_copy_changes()
- scm = detect_scm_system(self.git_checkout_path)
- files = scm.changed_files()
- self.assertTrue('test_file_commit1' in files)
- self.assertTrue('test_file_commit2' in files)
-
- def test_changed_files_git_commit(self):
- self._two_local_commits()
- scm = detect_scm_system(self.git_checkout_path)
- files = scm.changed_files(git_commit="HEAD^")
- self.assertTrue('test_file_commit1' in files)
- self.assertFalse('test_file_commit2' in files)
-
- def test_changed_files_git_commit_range(self):
- self._three_local_commits()
- scm = detect_scm_system(self.git_checkout_path)
- files = scm.changed_files(git_commit="HEAD~2..HEAD")
- self.assertTrue('test_file_commit0' not in files)
- self.assertTrue('test_file_commit1' in files)
- self.assertTrue('test_file_commit2' in files)
-
- def test_changed_files_working_copy_only(self):
- self._one_local_commit_plus_working_copy_changes()
- scm = detect_scm_system(self.git_checkout_path)
- files = scm.changed_files(git_commit="HEAD..")
- self.assertFalse('test_file_commit1' in files)
- self.assertTrue('test_file_commit2' in files)
-
- def test_changed_files_multiple_local_commits(self):
- self._two_local_commits()
- scm = detect_scm_system(self.git_checkout_path)
- files = scm.changed_files()
- self.assertTrue('test_file_commit2' in files)
- self.assertTrue('test_file_commit1' in files)
-
- def test_changed_files_not_synced(self):
- run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3'])
- self._two_local_commits()
- scm = detect_scm_system(self.git_checkout_path)
- self.assertRaises(ScriptError, scm.changed_files)
-
- def test_changed_files(self):
- self._shared_test_changed_files()
-
- def test_changed_files_for_revision(self):
- self._shared_test_changed_files_for_revision()
-
- def test_contents_at_revision(self):
- self._shared_test_contents_at_revision()
-
- def test_revisions_changing_file(self):
- self._shared_test_revisions_changing_file()
-
- def test_added_files(self):
- self._shared_test_added_files()
-
- def test_committer_email_for_revision(self):
- self._shared_test_committer_email_for_revision()
-
- def test_add_recursively(self):
- self._shared_test_add_recursively()
-
- def test_delete(self):
- self._two_local_commits()
- self.scm.delete('test_file_commit1')
- self.assertTrue("test_file_commit1" in self.scm.deleted_files())
-
- def test_to_object_name(self):
- relpath = 'test_file_commit1'
- fullpath = os.path.join(self.git_checkout_path, relpath)
- self._two_local_commits()
- self.assertEqual(relpath, self.scm.to_object_name(fullpath))
-
- def test_show_head(self):
- self._two_local_commits()
- self.assertEqual("more test content", self.scm.show_head('test_file_commit1'))
-
- def test_show_head_binary(self):
- self._two_local_commits()
- data = "\244"
- write_into_file_at_path("binary_file", data, encoding=None)
- self.scm.add("binary_file")
- self.scm.commit_locally_with_message("a test commit")
- self.assertEqual(data, self.scm.show_head('binary_file'))
-
- def test_diff_for_file(self):
- self._two_local_commits()
- write_into_file_at_path('test_file_commit1', "Updated", encoding=None)
-
- diff = self.scm.diff_for_file('test_file_commit1')
- cached_diff = self.scm.diff_for_file('test_file_commit1')
- self.assertTrue("+Updated" in diff)
- self.assertTrue("-more test content" in diff)
-
- self.scm.add('test_file_commit1')
-
- cached_diff = self.scm.diff_for_file('test_file_commit1')
- self.assertTrue("+Updated" in cached_diff)
- self.assertTrue("-more test content" in cached_diff)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/config/__init__.py b/WebKitTools/Scripts/webkitpy/common/config/__init__.py
deleted file mode 100644
index ef65bee..0000000
--- a/WebKitTools/Scripts/webkitpy/common/config/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/common/config/build.py b/WebKitTools/Scripts/webkitpy/common/config/build.py
deleted file mode 100644
index c45f122..0000000
--- a/WebKitTools/Scripts/webkitpy/common/config/build.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# Copyright (C) 2010 Apple 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.
-#
-# 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.
-
-"""Functions relating to building WebKit"""
-
-import re
-
-
-def _should_file_trigger_build(target_platform, file):
- # The directories and patterns lists below map directory names or
- # regexp patterns to the bot platforms for which they should trigger a
- # build. Mapping to the empty list means that no builds should be
- # triggered on any platforms. Earlier directories/patterns take
- # precendence over later ones.
-
- # FIXME: The patterns below have only been verified to be correct on
- # Windows. We should implement this for other platforms and start using
- # it for their bots. Someone familiar with each platform will have to
- # figure out what the right set of directories/patterns is for that
- # platform.
- assert(target_platform == "win")
-
- directories = [
- # Directories that shouldn't trigger builds on any bots.
- ("BugsSite", []),
- ("PageLoadTests", []),
- ("PlanetWebKit", []),
- ("WebCore/manual-tests", []),
- ("WebKitExamplePlugins", []),
- ("WebKitSite", []),
- ("android", []),
- ("brew", []),
- ("efl", []),
- ("haiku", []),
- ("iphone", []),
- ("opengl", []),
- ("opentype", []),
- ("openvg", []),
- ("wx", []),
- ("wince", []),
-
- # Directories that should trigger builds on only some bots.
- ("JavaScriptGlue", ["mac"]),
- ("LayoutTests/platform/mac", ["mac", "win"]),
- ("LayoutTests/platform/mac-snowleopard", ["mac-snowleopard", "win"]),
- ("WebCore/image-decoders", ["chromium"]),
- ("cairo", ["gtk", "wincairo"]),
- ("cf", ["chromium-mac", "mac", "qt", "win"]),
- ("chromium", ["chromium"]),
- ("cocoa", ["chromium-mac", "mac"]),
- ("curl", ["gtk", "wincairo"]),
- ("gobject", ["gtk"]),
- ("gpu", ["chromium", "mac"]),
- ("gstreamer", ["gtk"]),
- ("gtk", ["gtk"]),
- ("mac", ["chromium-mac", "mac"]),
- ("mac-leopard", ["mac-leopard"]),
- ("mac-snowleopard", ["mac-snowleopard"]),
- ("mac-wk2", ["mac-snowleopard", "win"]),
- ("objc", ["mac"]),
- ("qt", ["qt"]),
- ("skia", ["chromium"]),
- ("soup", ["gtk"]),
- ("v8", ["chromium"]),
- ("win", ["chromium-win", "win"]),
- ]
- patterns = [
- # Patterns that shouldn't trigger builds on any bots.
- (r"(?:^|/)Makefile$", []),
- (r"/ARM", []),
- (r"/CMake.*", []),
- (r"/ChangeLog.*$", []),
- (r"/LICENSE[^/]+$", []),
- (r"ARM(?:v7)?\.(?:cpp|h)$", []),
- (r"MIPS\.(?:cpp|h)$", []),
- (r"WinCE\.(?:cpp|h|mm)$", []),
- (r"\.(?:bkl|mk)$", []),
-
- # Patterns that should trigger builds on only some bots.
- (r"/GNUmakefile\.am$", ["gtk"]),
- (r"/\w+Chromium\w*\.(?:cpp|h|mm)$", ["chromium"]),
- (r"Mac\.(?:cpp|h|mm)$", ["mac"]),
- (r"\.exp$", ["mac"]),
- (r"\.gypi?", ["chromium"]),
- (r"\.order$", ["mac"]),
- (r"\.pr[io]$", ["qt"]),
- (r"\.xcconfig$", ["mac"]),
- (r"\.xcodeproj/", ["mac"]),
- ]
-
- base_platform = target_platform.split("-")[0]
-
- # See if the file is in one of the known directories.
- for directory, platforms in directories:
- if re.search(r"(?:^|/)%s/" % directory, file):
- return target_platform in platforms or base_platform in platforms
-
- # See if the file matches a known pattern.
- for pattern, platforms in patterns:
- if re.search(pattern, file):
- return target_platform in platforms or base_platform in platforms
-
- # See if the file is a platform-specific test result.
- match = re.match("LayoutTests/platform/(?P<platform>[^/]+)/", file)
- if match:
- # See if the file is a test result for this platform, our base
- # platform, or one of our sub-platforms.
- return match.group("platform") in (target_platform, base_platform) or match.group("platform").startswith("%s-" % target_platform)
-
- # The file isn't one we know about specifically, so we should assume we
- # have to build.
- return True
-
-
-def should_build(target_platform, changed_files):
- """Returns true if the changed files affect the given platform, and
- thus a build should be performed. target_platform should be one of the
- platforms used in the build.webkit.org master's config.json file."""
- return any(_should_file_trigger_build(target_platform, file) for file in changed_files)
diff --git a/WebKitTools/Scripts/webkitpy/common/config/build_unittest.py b/WebKitTools/Scripts/webkitpy/common/config/build_unittest.py
deleted file mode 100644
index 3e70ff0..0000000
--- a/WebKitTools/Scripts/webkitpy/common/config/build_unittest.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright (C) 2010 Apple 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.
-#
-# 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.
-
-import unittest
-
-from webkitpy.common.config import build
-
-
-class ShouldBuildTest(unittest.TestCase):
- _should_build_tests = [
- (["BugsSite/foo", "WebCore/bar"], ["*"]),
- (["BugsSite/foo"], []),
- (["JavaScriptCore/JavaScriptCore.xcodeproj/foo"], ["mac-leopard", "mac-snowleopard"]),
- (["JavaScriptGlue/foo", "WebCore/bar"], ["*"]),
- (["JavaScriptGlue/foo"], ["mac-leopard", "mac-snowleopard"]),
- (["LayoutTests/foo"], ["*"]),
- (["LayoutTests/platform/chromium-linux/foo"], ["chromium-linux"]),
- (["LayoutTests/platform/chromium-win/fast/compact/001-expected.txt"], ["chromium-win"]),
- (["LayoutTests/platform/mac-leopard/foo"], ["mac-leopard"]),
- (["LayoutTests/platform/mac-snowleopard/foo"], ["mac-snowleopard", "win"]),
- (["LayoutTests/platform/mac-wk2/Skipped"], ["mac-snowleopard", "win"]),
- (["LayoutTests/platform/mac/foo"], ["mac-leopard", "mac-snowleopard", "win"]),
- (["LayoutTests/platform/win-xp/foo"], ["win"]),
- (["LayoutTests/platform/win-wk2/foo"], ["win"]),
- (["LayoutTests/platform/win/foo"], ["win"]),
- (["WebCore/mac/foo"], ["chromium-mac", "mac-leopard", "mac-snowleopard"]),
- (["WebCore/win/foo"], ["chromium-win", "win"]),
- (["WebCore/platform/graphics/gpu/foo"], ["mac-leopard", "mac-snowleopard"]),
- (["WebCore/platform/wx/wxcode/win/foo"], []),
- (["WebCore/rendering/RenderThemeMac.mm", "WebCore/rendering/RenderThemeMac.h"], ["mac-leopard", "mac-snowleopard"]),
- (["WebCore/rendering/RenderThemeChromiumLinux.h"], ["chromium-linux"]),
- (["WebCore/rendering/RenderThemeWinCE.h"], []),
- ]
-
- def test_should_build(self):
- for files, platforms in self._should_build_tests:
- # FIXME: We should test more platforms here once
- # build._should_file_trigger_build is implemented for them.
- for platform in ["win"]:
- should_build = platform in platforms or "*" in platforms
- self.assertEqual(build.should_build(platform, files), should_build, "%s should%s have built but did%s (files: %s)" % (platform, "" if should_build else "n't", "n't" if should_build else "", str(files)))
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/config/committers.py b/WebKitTools/Scripts/webkitpy/common/config/committers.py
deleted file mode 100644
index b451721..0000000
--- a/WebKitTools/Scripts/webkitpy/common/config/committers.py
+++ /dev/null
@@ -1,331 +0,0 @@
-# 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
-# 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.
-#
-# WebKit's Python module for committer and reviewer validation
-
-
-class Committer:
-
- def __init__(self, name, email_or_emails, irc_nickname=None):
- self.full_name = name
- if isinstance(email_or_emails, str):
- self.emails = [email_or_emails]
- else:
- self.emails = email_or_emails
- self.irc_nickname = irc_nickname
- self.can_review = False
-
- def bugzilla_email(self):
- # FIXME: We're assuming the first email is a valid bugzilla email,
- # which might not be right.
- return self.emails[0]
-
- def __str__(self):
- return '"%s" <%s>' % (self.full_name, self.emails[0])
-
-
-class Reviewer(Committer):
-
- def __init__(self, name, email_or_emails, irc_nickname=None):
- Committer.__init__(self, name, email_or_emails, irc_nickname)
- self.can_review = True
-
-
-# This is intended as a canonical, machine-readable list of all non-reviewer
-# committers for WebKit. If your name is missing here and you are a committer,
-# please add it. No review needed. All reviewers are committers, so this list
-# is only of committers who are not reviewers.
-
-
-committers_unable_to_review = [
- Committer("Aaron Boodman", "aa@chromium.org", "aboodman"),
- Committer("Abhishek Arya", "inferno@chromium.org", "inferno-sec"),
- Committer("Adam Langley", "agl@chromium.org", "agl"),
- Committer("Albert J. Wong", "ajwong@chromium.org"),
- Committer("Alejandro G. Castro", ["alex@igalia.com", "alex@webkit.org"]),
- Committer("Alexander Kellett", ["lypanov@mac.com", "a-lists001@lypanov.net", "lypanov@kde.org"], "lypanov"),
- Committer("Alexander Pavlov", "apavlov@chromium.org", "apavlov"),
- Committer("Andre Boule", "aboule@apple.com"),
- Committer("Andrei Popescu", "andreip@google.com", "andreip"),
- Committer("Andrew Wellington", ["andrew@webkit.org", "proton@wiretapped.net"], "proton"),
- Committer("Andrey Kosyakov", "caseq@chromium.org", "caseq"),
- Committer("Andras Becsi", ["abecsi@webkit.org", "abecsi@inf.u-szeged.hu"], "bbandix"),
- Committer("Andy Estes", "aestes@apple.com", "estes"),
- Committer("Anthony Ricaud", "rik@webkit.org", "rik"),
- Committer("Anton Muhin", "antonm@chromium.org", "antonm"),
- Committer("Balazs Kelemen", "kbalazs@webkit.org", "kbalazs"),
- Committer("Ben Murdoch", "benm@google.com", "benm"),
- Committer("Benjamin C Meyer", ["ben@meyerhome.net", "ben@webkit.org"], "icefox"),
- Committer("Benjamin Otte", ["otte@gnome.org", "otte@webkit.org"], "otte"),
- Committer("Benjamin Poulain", ["benjamin.poulain@nokia.com", "ikipou@gmail.com"]),
- Committer("Brent Fulgham", "bfulgham@webkit.org", "bfulgham"),
- Committer("Brett Wilson", "brettw@chromium.org", "brettx"),
- Committer("Brian Weinstein", "bweinstein@apple.com", "bweinstein"),
- Committer("Cameron McCormack", "cam@webkit.org", "heycam"),
- Committer("Carol Szabo", "carol.szabo@nokia.com"),
- Committer("Chang Shu", "Chang.Shu@nokia.com"),
- Committer("Chris Evans", "cevans@google.com"),
- Committer("Chris Petersen", "cpetersen@apple.com", "cpetersen"),
- Committer("Chris Rogers", "crogers@google.com", "crogers"),
- Committer("Christian Dywan", ["christian@twotoasts.de", "christian@webkit.org"]),
- Committer("Collin Jackson", "collinj@webkit.org"),
- Committer("David Smith", ["catfish.man@gmail.com", "dsmith@webkit.org"], "catfishman"),
- Committer("Dean Jackson", "dino@apple.com", "dino"),
- Committer("Diego Gonzalez", ["diegohcg@webkit.org", "diego.gonzalez@openbossa.org"], "diegohcg"),
- Committer("Dirk Pranke", "dpranke@chromium.org"),
- Committer("Drew Wilson", "atwilson@chromium.org", "atwilson"),
- Committer("Eli Fidler", "eli@staikos.net", "QBin"),
- Committer("Enrica Casucci", "enrica@apple.com"),
- Committer("Erik Arvidsson", "arv@chromium.org", "arv"),
- Committer("Eric Roman", "eroman@chromium.org", "eroman"),
- Committer("Evan Martin", "evan@chromium.org", "evmar"),
- Committer("Evan Stade", "estade@chromium.org", "estade"),
- Committer("Fady Samuel", "fsamuel@chromium.org", "fsamuel"),
- Committer("Feng Qian", "feng@chromium.org"),
- Committer("Fumitoshi Ukai", "ukai@chromium.org", "ukai"),
- Committer("Gabor Loki", "loki@webkit.org", "loki04"),
- Committer("Girish Ramakrishnan", ["girish@forwardbias.in", "ramakrishnan.girish@gmail.com"]),
- Committer("Graham Dennis", ["Graham.Dennis@gmail.com", "gdennis@webkit.org"]),
- Committer("Greg Bolsinga", "bolsinga@apple.com"),
- Committer("Gyuyoung Kim", ["gyuyoung.kim@samsung.com", "gyuyoung@gmail.com", "gyuyoung@webkit.org"], "gyuyoung"),
- Committer("Hans Wennborg", "hans@chromium.org", "hwennborg"),
- Committer("Hayato Ito", "hayato@chromium.org", "hayato"),
- Committer("Hin-Chung Lam", ["hclam@google.com", "hclam@chromium.org"]),
- Committer("Ilya Tikhonovsky", "loislo@chromium.org", "loislo"),
- Committer("Jakob Petsovits", ["jpetsovits@rim.com", "jpetso@gmx.at"], "jpetso"),
- Committer("Jakub Wieczorek", "jwieczorek@webkit.org", "fawek"),
- Committer("James Hawkins", ["jhawkins@chromium.org", "jhawkins@google.com"], "jhawkins"),
- Committer("Jay Civelli", "jcivelli@chromium.org", "jcivelli"),
- 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_"),
- Committer("Jocelyn Turcotte", "jocelyn.turcotte@nokia.com", "jturcotte"),
- Committer("Jochen Eisinger", "jochen@chromium.org", "jochen__"),
- Committer("John Abd-El-Malek", "jam@chromium.org", "jam"),
- Committer("John Gregg", ["johnnyg@google.com", "johnnyg@chromium.org"], "johnnyg"),
- Committer("Joost de Valk", ["joost@webkit.org", "webkit-dev@joostdevalk.nl"], "Altha"),
- Committer("Julie Parent", ["jparent@google.com", "jparent@chromium.org"], "jparent"),
- Committer("Julien Chaffraix", ["jchaffraix@webkit.org", "julien.chaffraix@gmail.com"]),
- Committer("Jungshik Shin", "jshin@chromium.org"),
- Committer("Justin Schuh", "jschuh@chromium.org", "jschuh"),
- Committer("Keishi Hattori", "keishi@webkit.org", "keishi"),
- Committer("Kelly Norton", "knorton@google.com"),
- Committer("Kent Hansen", "kent.hansen@nokia.com", "khansen"),
- Committer("Kimmo Kinnunen", ["kimmo.t.kinnunen@nokia.com", "kimmok@iki.fi", "ktkinnun@webkit.org"], "kimmok"),
- Committer("Kinuko Yasuda", "kinuko@chromium.org", "kinuko"),
- Committer("Krzysztof Kowalczyk", "kkowalczyk@gmail.com"),
- Committer("Kwang Yul Seo", ["kwangyul.seo@gmail.com", "skyul@company100.net", "kseo@webkit.org"], "kwangseo"),
- Committer("Leandro Pereira", ["leandro@profusion.mobi", "leandro@webkit.org"], "acidx"),
- Committer("Levi Weintraub", "lweintraub@apple.com"),
- Committer("Lucas De Marchi", ["lucas.demarchi@profusion.mobi", "demarchi@webkit.org"], "demarchi"),
- Committer("Luiz Agostini", ["luiz@webkit.org", "luiz.agostini@openbossa.org"], "lca"),
- Committer("Mads Ager", "ager@chromium.org"),
- Committer("Marcus Voltis Bulach", "bulach@chromium.org"),
- Committer("Mario Sanchez Prada", ["msanchez@igalia.com", "mario@webkit.org"], "msanchez"),
- Committer("Matt Delaney", "mdelaney@apple.com"),
- Committer("Matt Lilek", ["webkit@mattlilek.com", "pewtermoose@webkit.org"]),
- Committer("Matt Perry", "mpcomplete@chromium.org"),
- Committer("Maxime Britto", ["maxime.britto@gmail.com", "britto@apple.com"]),
- Committer("Maxime Simon", ["simon.maxime@gmail.com", "maxime.simon@webkit.org"], "maxime.simon"),
- Committer("Michael Nordman", "michaeln@google.com", "michaeln"),
- Committer("Michael Saboff", "msaboff@apple.com"),
- Committer("Michelangelo De Simone", "michelangelo@webkit.org", "michelangelo"),
- Committer("Mihai Parparita", "mihaip@chromium.org", "mihaip"),
- Committer("Mike Belshe", ["mbelshe@chromium.org", "mike@belshe.com"]),
- Committer("Mike Fenton", ["mifenton@rim.com", "mike.fenton@torchmobile.com"], "mfenton"),
- Committer("Mike Thole", ["mthole@mikethole.com", "mthole@apple.com"]),
- Committer("Mikhail Naganov", "mnaganov@chromium.org"),
- Committer("MORITA Hajime", "morrita@google.com", "morrita"),
- Committer("Nico Weber", ["thakis@chromium.org", "thakis@google.com"], "thakis"),
- Committer("Noam Rosenthal", "noam.rosenthal@nokia.com", "noamr"),
- Committer("Pam Greene", "pam@chromium.org", "pamg"),
- Committer("Patrick Gansterer", ["paroga@paroga.com", "paroga@webkit.org"], "paroga"),
- Committer("Pavel Podivilov", "podivilov@chromium.org", "podivilov"),
- Committer("Peter Kasting", ["pkasting@google.com", "pkasting@chromium.org"], "pkasting"),
- 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("Renata Hodovan", "reni@webkit.org", "reni"),
- Committer("Robert Hogan", ["robert@webkit.org", "robert@roberthogan.net", "lists@roberthogan.net"], "mwenge"),
- Committer("Roland Steiner", "rolandsteiner@chromium.org"),
- Committer("Ryosuke Niwa", "rniwa@webkit.org", "rniwa"),
- Committer("Satish Sampath", "satish@chromium.org"),
- Committer("Scott Violet", "sky@chromium.org", "sky"),
- Committer("Stephen White", "senorblanco@chromium.org", "senorblanco"),
- Committer("Tony Gentilcore", "tonyg@chromium.org", "tonyg-cr"),
- Committer("Trey Matteson", "trey@usa.net", "trey"),
- Committer("Tristan O'Tierney", ["tristan@otierney.net", "tristan@apple.com"]),
- Committer("Vangelis Kokkevis", "vangelis@chromium.org", "vangelis"),
- Committer("Victor Wang", "victorw@chromium.org", "victorw"),
- Committer("Vitaly Repeshko", "vitalyr@chromium.org"),
- Committer("William Siegrist", "wsiegrist@apple.com", "wms"),
- Committer("Xiaomei Ji", "xji@chromium.org", "xji"),
- Committer("Yael Aharon", "yael.aharon@nokia.com"),
- Committer("Yaar Schnitman", ["yaar@chromium.org", "yaar@google.com"]),
- Committer("Yong Li", ["yong.li.webkit@gmail.com", "yong.li@torchmobile.com"], "yong"),
- Committer("Yongjun Zhang", "yongjun.zhang@nokia.com"),
- Committer("Yuta Kitamura", "yutak@chromium.org", "yutak"),
- Committer("Yuzo Fujishima", "yuzo@google.com", "yuzo"),
- Committer("Zhenyao Mo", "zmo@google.com", "zhenyao"),
- Committer("Zoltan Herczeg", "zherczeg@webkit.org", "zherczeg"),
- Committer("Zoltan Horvath", ["zoltan@webkit.org", "hzoltan@inf.u-szeged.hu", "horvath.zoltan.6@stud.u-szeged.hu"], "zoltan"),
-]
-
-
-# This is intended as a canonical, machine-readable list of all reviewers for
-# WebKit. If your name is missing here and you are a reviewer, please add it.
-# No review needed.
-
-
-reviewers_list = [
- Reviewer("Ada Chan", "adachan@apple.com", "chanada"),
- Reviewer("Adam Barth", "abarth@webkit.org", "abarth"),
- Reviewer("Adam Roben", "aroben@apple.com", "aroben"),
- Reviewer("Adam Treat", ["treat@kde.org", "treat@webkit.org", "atreat@rim.com"], "manyoso"),
- Reviewer("Adele Peterson", "adele@apple.com", "adele"),
- Reviewer("Alexey Proskuryakov", ["ap@webkit.org", "ap@apple.com"], "ap"),
- Reviewer("Alice Liu", "alice.liu@apple.com", "aliu"),
- Reviewer("Alp Toker", ["alp@nuanti.com", "alp@atoker.com", "alp@webkit.org"], "alp"),
- Reviewer("Anders Carlsson", ["andersca@apple.com", "acarlsson@apple.com"], "andersca"),
- Reviewer("Andreas Kling", ["kling@webkit.org", "andreas.kling@nokia.com"], "kling"),
- Reviewer("Antonio Gomes", ["tonikitoo@webkit.org", "agomes@rim.com"], "tonikitoo"),
- Reviewer("Antti Koivisto", ["koivisto@iki.fi", "antti@apple.com", "antti.j.koivisto@nokia.com"], "anttik"),
- Reviewer("Ariya Hidayat", ["ariya.hidayat@gmail.com", "ariya@sencha.com", "ariya@webkit.org"], "ariya"),
- Reviewer("Beth Dakin", "bdakin@apple.com", "dethbakin"),
- Reviewer("Brady Eidson", "beidson@apple.com", "bradee-oh"),
- Reviewer("Cameron Zwarich", ["zwarich@apple.com", "cwzwarich@apple.com", "cwzwarich@webkit.org"]),
- Reviewer("Chris Blumenberg", "cblu@apple.com", "cblu"),
- Reviewer("Chris Marrin", "cmarrin@apple.com", "cmarrin"),
- Reviewer("Chris Fleizach", "cfleizach@apple.com", "cfleizach"),
- Reviewer("Chris Jerdonek", "cjerdonek@webkit.org", "cjerdonek"),
- Reviewer(u"Csaba Osztrogon\u00e1c", "ossy@webkit.org", "ossy"),
- Reviewer("Dan Bernstein", ["mitz@webkit.org", "mitz@apple.com"], "mitzpettel"),
- Reviewer("Daniel Bates", "dbates@webkit.org", "dydz"),
- Reviewer("Darin Adler", "darin@apple.com", "darin"),
- Reviewer("Darin Fisher", ["fishd@chromium.org", "darin@chromium.org"], "fishd"),
- Reviewer("David Harrison", "harrison@apple.com", "harrison"),
- Reviewer("David Hyatt", "hyatt@apple.com", "hyatt"),
- Reviewer("David Kilzer", ["ddkilzer@webkit.org", "ddkilzer@apple.com"], "ddkilzer"),
- Reviewer("David Levin", "levin@chromium.org", "dave_levin"),
- Reviewer("Dimitri Glazkov", "dglazkov@chromium.org", "dglazkov"),
- Reviewer("Dirk Schulze", "krit@webkit.org", "krit"),
- Reviewer("Dmitry Titov", "dimich@chromium.org", "dimich"),
- Reviewer("Don Melton", "gramps@apple.com", "gramps"),
- Reviewer("Dumitru Daniliuc", "dumi@chromium.org", "dumi"),
- Reviewer("Eric Carlson", "eric.carlson@apple.com"),
- Reviewer("Eric Seidel", "eric@webkit.org", "eseidel"),
- Reviewer("Gavin Barraclough", "barraclough@apple.com", "gbarra"),
- Reviewer("Geoffrey Garen", "ggaren@apple.com", "ggaren"),
- Reviewer("George Staikos", ["staikos@kde.org", "staikos@webkit.org"]),
- Reviewer("Gustavo Noronha Silva", ["gns@gnome.org", "kov@webkit.org", "gustavo.noronha@collabora.co.uk"], "kov"),
- Reviewer("Holger Freyther", ["zecke@selfish.org", "zecke@webkit.org"], "zecke"),
- Reviewer("James Robinson", ["jamesr@chromium.org", "jamesr@google.com"], "jamesr"),
- Reviewer("Jan Alonzo", ["jmalonzo@gmail.com", "jmalonzo@webkit.org"], "janm"),
- Reviewer("Jeremy Orlow", "jorlow@chromium.org", "jorlow"),
- Reviewer("Jian Li", "jianli@chromium.org", "jianli"),
- Reviewer("John Sullivan", "sullivan@apple.com", "sullivan"),
- Reviewer("Jon Honeycutt", "jhoneycutt@apple.com", "jhoneycutt"),
- Reviewer("Joseph Pecoraro", ["joepeck@webkit.org", "pecoraro@apple.com"], "JoePeck"),
- 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", "kenneth.christiansen@gmail.com"], "kenne"),
- Reviewer("Kenneth Russell", "kbr@google.com", "kbr_google"),
- 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"),
- Reviewer("Lars Knoll", ["lars@trolltech.com", "lars@kde.org", "lars.knoll@nokia.com"], "lars"),
- Reviewer("Laszlo Gombos", "laszlo.1.gombos@nokia.com", "lgombos"),
- Reviewer("Maciej Stachowiak", "mjs@apple.com", "othermaciej"),
- Reviewer("Mark Rowe", "mrowe@apple.com", "bdash"),
- Reviewer("Martin Robinson", ["mrobinson@webkit.org", "mrobinson@igalia.com", "martin.james.robinson@gmail.com"], "mrobinson"),
- 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"),
- Reviewer("Rob Buis", ["rwlbuis@gmail.com", "rwlbuis@webkit.org"], "rwlbuis"),
- Reviewer("Sam Weinig", ["sam@webkit.org", "weinig@apple.com"], "weinig"),
- Reviewer("Shinichiro Hamaji", "hamaji@chromium.org", "hamaji"),
- Reviewer("Simon Fraser", "simon.fraser@apple.com", "smfr"),
- Reviewer("Simon Hausmann", ["hausmann@webkit.org", "hausmann@kde.org", "simon.hausmann@nokia.com"], "tronical"),
- Reviewer("Stephanie Lewis", "slewis@apple.com", "sundiamonde"),
- Reviewer("Steve Block", "steveblock@google.com", "steveblock"),
- Reviewer("Steve Falkenburg", "sfalken@apple.com", "sfalken"),
- Reviewer("Tim Omernick", "timo@apple.com"),
- Reviewer("Timothy Hatcher", ["timothy@apple.com", "timothy@hatcher.name"], "xenon"),
- Reviewer("Tony Chang", "tony@chromium.org", "tony^work"),
- Reviewer(u"Tor Arne Vestb\u00f8", ["vestbo@webkit.org", "tor.arne.vestbo@nokia.com"], "torarne"),
- Reviewer("Vicki Murley", "vicki@apple.com"),
- Reviewer("Xan Lopez", ["xan.lopez@gmail.com", "xan@gnome.org", "xan@webkit.org"], "xan"),
- Reviewer("Yury Semikhatsky", "yurys@chromium.org", "yurys"),
- Reviewer("Zack Rusin", "zack@kde.org", "zackr"),
-]
-
-
-class CommitterList:
-
- # Committers and reviewers are passed in to allow easy testing
-
- def __init__(self,
- committers=committers_unable_to_review,
- reviewers=reviewers_list):
- self._committers = committers + reviewers
- self._reviewers = reviewers
- self._committers_by_email = {}
-
- def committers(self):
- return self._committers
-
- def reviewers(self):
- return self._reviewers
-
- def _email_to_committer_map(self):
- if not len(self._committers_by_email):
- for committer in self._committers:
- for email in committer.emails:
- self._committers_by_email[email] = committer
- return self._committers_by_email
-
- def committer_by_name(self, name):
- # This could be made into a hash lookup if callers need it to be fast.
- for committer in self.committers():
- if committer.full_name == name:
- return committer
-
- def committer_by_email(self, email):
- return self._email_to_committer_map().get(email)
-
- def reviewer_by_email(self, email):
- committer = self.committer_by_email(email)
- if committer and not committer.can_review:
- return None
- return committer
diff --git a/WebKitTools/Scripts/webkitpy/common/config/committers_unittest.py b/WebKitTools/Scripts/webkitpy/common/config/committers_unittest.py
deleted file mode 100644
index 068c0ee..0000000
--- a/WebKitTools/Scripts/webkitpy/common/config/committers_unittest.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# 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
-# 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 unittest
-from webkitpy.common.config.committers import CommitterList, Committer, Reviewer
-
-class CommittersTest(unittest.TestCase):
-
- def test_committer_lookup(self):
- committer = Committer('Test One', 'one@test.com', 'one')
- reviewer = Reviewer('Test Two', ['two@test.com', 'two@rad.com', 'so_two@gmail.com'])
- committer_list = CommitterList(committers=[committer], reviewers=[reviewer])
-
- # Test valid committer and reviewer lookup
- self.assertEqual(committer_list.committer_by_email('one@test.com'), committer)
- self.assertEqual(committer_list.reviewer_by_email('two@test.com'), reviewer)
- self.assertEqual(committer_list.committer_by_email('two@test.com'), reviewer)
- self.assertEqual(committer_list.committer_by_email('two@rad.com'), reviewer)
- self.assertEqual(committer_list.reviewer_by_email('so_two@gmail.com'), reviewer)
-
- # Test valid committer and reviewer lookup
- self.assertEqual(committer_list.committer_by_name("Test One"), committer)
- self.assertEqual(committer_list.committer_by_name("Test Two"), reviewer)
- self.assertEqual(committer_list.committer_by_name("Test Three"), None)
-
- # Test that the first email is assumed to be the Bugzilla email address (for now)
- self.assertEqual(committer_list.committer_by_email('two@rad.com').bugzilla_email(), 'two@test.com')
-
- # Test that a known committer is not returned during reviewer lookup
- self.assertEqual(committer_list.reviewer_by_email('one@test.com'), None)
-
- # Test that unknown email address fail both committer and reviewer lookup
- self.assertEqual(committer_list.committer_by_email('bar@bar.com'), None)
- self.assertEqual(committer_list.reviewer_by_email('bar@bar.com'), None)
-
- # Test that emails returns a list.
- self.assertEqual(committer.emails, ['one@test.com'])
-
- self.assertEqual(committer.irc_nickname, 'one')
-
- # Test that committers returns committers and reviewers and reviewers() just reviewers.
- self.assertEqual(committer_list.committers(), [committer, reviewer])
- self.assertEqual(committer_list.reviewers(), [reviewer])
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/config/committervalidator.py b/WebKitTools/Scripts/webkitpy/common/config/committervalidator.py
deleted file mode 100644
index b7b2990..0000000
--- a/WebKitTools/Scripts/webkitpy/common/config/committervalidator.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple Inc. All rights reserved.
-# Copyright (c) 2010 Research In Motion Limited. 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 os
-
-from webkitpy.common.system.ospath import relpath
-from webkitpy.common.config import committers
-
-
-class CommitterValidator(object):
-
- def __init__(self, bugzilla):
- self._bugzilla = bugzilla
-
- # _view_source_url belongs in some sort of webkit_config.py module.
- def _view_source_url(self, local_path):
- return "http://trac.webkit.org/browser/trunk/%s" % local_path
-
- def _checkout_root(self):
- # FIXME: This is a hack, we would have this from scm.checkout_root
- # if we had any way to get to an scm object here.
- components = __file__.split(os.sep)
- tools_index = components.index("WebKitTools")
- return os.sep.join(components[:tools_index])
-
- def _committers_py_path(self):
- # extension can sometimes be .pyc, we always want .py
- (path, extension) = os.path.splitext(committers.__file__)
- # FIXME: When we're allowed to use python 2.6 we can use the real
- # os.path.relpath
- path = relpath(path, self._checkout_root())
- return ".".join([path, "py"])
-
- def _flag_permission_rejection_message(self, setter_email, flag_name):
- # Should come from some webkit_config.py
- contribution_guidlines = "http://webkit.org/coding/contributing.html"
- # This could be queried from the status_server.
- queue_administrator = "eseidel@chromium.org"
- # This could be queried from the tool.
- queue_name = "commit-queue"
- committers_list = self._committers_py_path()
- message = "%s does not have %s permissions according to %s." % (
- setter_email,
- flag_name,
- self._view_source_url(committers_list))
- message += "\n\n- If you do not have %s rights please read %s for instructions on how to use bugzilla flags." % (
- flag_name, contribution_guidlines)
- message += "\n\n- If you have %s rights please correct the error in %s by adding yourself to the file (no review needed). " % (
- flag_name, committers_list)
- message += "The %s restarts itself every 2 hours. After restart the %s will correctly respect your %s rights." % (
- queue_name, queue_name, flag_name)
- return message
-
- def _validate_setter_email(self, patch, result_key, rejection_function):
- committer = getattr(patch, result_key)()
- # If the flag is set, and we don't recognize the setter, reject the
- # flag!
- setter_email = patch._attachment_dictionary.get("%s_email" % result_key)
- if setter_email and not committer:
- rejection_function(patch.id(),
- self._flag_permission_rejection_message(setter_email,
- result_key))
- return False
- return True
-
- def _reject_patch_if_flags_are_invalid(self, patch):
- return (self._validate_setter_email(
- patch, "reviewer", self.reject_patch_from_review_queue)
- and self._validate_setter_email(
- patch, "committer", self.reject_patch_from_commit_queue))
-
- def patches_after_rejecting_invalid_commiters_and_reviewers(self, patches):
- return [patch for patch in patches if self._reject_patch_if_flags_are_invalid(patch)]
-
- def reject_patch_from_commit_queue(self,
- attachment_id,
- additional_comment_text=None):
- comment_text = "Rejecting patch %s from commit-queue." % attachment_id
- self._bugzilla.set_flag_on_attachment(attachment_id,
- "commit-queue",
- "-",
- comment_text,
- additional_comment_text)
-
- def reject_patch_from_review_queue(self,
- attachment_id,
- additional_comment_text=None):
- comment_text = "Rejecting patch %s from review queue." % attachment_id
- self._bugzilla.set_flag_on_attachment(attachment_id,
- 'review',
- '-',
- comment_text,
- additional_comment_text)
diff --git a/WebKitTools/Scripts/webkitpy/common/config/committervalidator_unittest.py b/WebKitTools/Scripts/webkitpy/common/config/committervalidator_unittest.py
deleted file mode 100644
index 61fa3bf..0000000
--- a/WebKitTools/Scripts/webkitpy/common/config/committervalidator_unittest.py
+++ /dev/null
@@ -1,43 +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.
-
-import unittest
-
-from .committervalidator import CommitterValidator
-
-
-class CommitterValidatorTest(unittest.TestCase):
- def test_flag_permission_rejection_message(self):
- validator = CommitterValidator(bugzilla=None)
- self.assertEqual(validator._committers_py_path(), "WebKitTools/Scripts/webkitpy/common/config/committers.py")
- expected_messsage = """foo@foo.com does not have review permissions according to http://trac.webkit.org/browser/trunk/WebKitTools/Scripts/webkitpy/common/config/committers.py.
-
-- If you do not have review rights please read http://webkit.org/coding/contributing.html for instructions on how to use bugzilla flags.
-
-- If you have review rights please correct the error in WebKitTools/Scripts/webkitpy/common/config/committers.py by adding yourself to the file (no review needed). The commit-queue restarts itself every 2 hours. After restart the commit-queue will correctly respect your review rights."""
- self.assertEqual(validator._flag_permission_rejection_message("foo@foo.com", "review"), expected_messsage)
diff --git a/WebKitTools/Scripts/webkitpy/common/config/irc.py b/WebKitTools/Scripts/webkitpy/common/config/irc.py
deleted file mode 100644
index 950c573..0000000
--- a/WebKitTools/Scripts/webkitpy/common/config/irc.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# 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
-# 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.
-
-server="irc.freenode.net"
-port=6667
-channel="#webkit"
diff --git a/WebKitTools/Scripts/webkitpy/common/config/ports.py b/WebKitTools/Scripts/webkitpy/common/config/ports.py
deleted file mode 100644
index d268865..0000000
--- a/WebKitTools/Scripts/webkitpy/common/config/ports.py
+++ /dev/null
@@ -1,249 +0,0 @@
-# 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
-# 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.
-#
-# WebKit's Python module for understanding the various ports
-
-import os
-import platform
-
-from webkitpy.common.system.executive import Executive
-
-
-class WebKitPort(object):
-
- # We might need to pass scm into this function for scm.checkout_root
- @classmethod
- def script_path(cls, script_name):
- return os.path.join("WebKitTools", "Scripts", script_name)
-
- @staticmethod
- def port(port_name):
- ports = {
- "chromium": ChromiumPort,
- "chromium-xvfb": ChromiumXVFBPort,
- "gtk": GtkPort,
- "mac": MacPort,
- "win": WinPort,
- "qt": QtPort,
- "efl": EflPort,
- }
- default_port = {
- "Windows": WinPort,
- "Darwin": MacPort,
- }
- # Do we really need MacPort as the ultimate default?
- return ports.get(port_name, default_port.get(platform.system(), MacPort))
-
- @staticmethod
- def makeArgs():
- args = '--makeargs="-j%s"' % Executive().cpu_count()
- if os.environ.has_key('MAKEFLAGS'):
- args = '--makeargs="%s"' % os.environ['MAKEFLAGS']
- return args
-
- @classmethod
- def name(cls):
- raise NotImplementedError("subclasses must implement")
-
- @classmethod
- def flag(cls):
- raise NotImplementedError("subclasses must implement")
-
- @classmethod
- def update_webkit_command(cls):
- return [cls.script_path("update-webkit")]
-
- @classmethod
- def build_webkit_command(cls, build_style=None):
- command = [cls.script_path("build-webkit")]
- if build_style == "debug":
- command.append("--debug")
- if build_style == "release":
- command.append("--release")
- return command
-
- @classmethod
- def run_javascriptcore_tests_command(cls):
- return [cls.script_path("run-javascriptcore-tests")]
-
- @classmethod
- def run_webkit_tests_command(cls):
- return [cls.script_path("run-webkit-tests")]
-
- @classmethod
- def run_python_unittests_command(cls):
- return [cls.script_path("test-webkitpy")]
-
- @classmethod
- def run_perl_unittests_command(cls):
- return [cls.script_path("test-webkitperl")]
-
- @classmethod
- def layout_tests_results_path(cls):
- return "/tmp/layout-test-results/results.html"
-
-
-class MacPort(WebKitPort):
-
- @classmethod
- def name(cls):
- return "Mac"
-
- @classmethod
- 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):
-
- @classmethod
- def name(cls):
- return "Win"
-
- @classmethod
- def flag(cls):
- # FIXME: This is lame. We should autogenerate this from a codename or something.
- return "--port=win"
-
-
-class GtkPort(WebKitPort):
-
- @classmethod
- def name(cls):
- return "Gtk"
-
- @classmethod
- def flag(cls):
- return "--port=gtk"
-
- @classmethod
- def build_webkit_command(cls, build_style=None):
- command = WebKitPort.build_webkit_command(build_style=build_style)
- command.append("--gtk")
- command.append(WebKitPort.makeArgs())
- return command
-
- @classmethod
- def run_webkit_tests_command(cls):
- command = WebKitPort.run_webkit_tests_command()
- command.append("--gtk")
- return command
-
-
-class QtPort(WebKitPort):
-
- @classmethod
- def name(cls):
- return "Qt"
-
- @classmethod
- def flag(cls):
- return "--port=qt"
-
- @classmethod
- def build_webkit_command(cls, build_style=None):
- command = WebKitPort.build_webkit_command(build_style=build_style)
- command.append("--qt")
- command.append(WebKitPort.makeArgs())
- return command
-
-
-class EflPort(WebKitPort):
-
- @classmethod
- def name(cls):
- return "Efl"
-
- @classmethod
- def flag(cls):
- return "--port=efl"
-
- @classmethod
- def build_webkit_command(cls, build_style=None):
- command = WebKitPort.build_webkit_command(build_style=build_style)
- command.append("--efl")
- command.append(WebKitPort.makeArgs())
- return command
-
-
-class ChromiumPort(WebKitPort):
-
- @classmethod
- def name(cls):
- return "Chromium"
-
- @classmethod
- def flag(cls):
- return "--port=chromium"
-
- @classmethod
- def update_webkit_command(cls):
- command = WebKitPort.update_webkit_command()
- command.append("--chromium")
- return command
-
- @classmethod
- def build_webkit_command(cls, build_style=None):
- command = WebKitPort.build_webkit_command(build_style=build_style)
- command.append("--chromium")
- return command
-
- @classmethod
- def run_webkit_tests_command(cls):
- return [
- cls.script_path("new-run-webkit-tests"),
- "--chromium",
- "--use-drt",
- "--no-pixel-tests",
- ]
-
- @classmethod
- def run_javascriptcore_tests_command(cls):
- return None
-
-
-class ChromiumXVFBPort(ChromiumPort):
-
- @classmethod
- def flag(cls):
- return "--port=chromium-xvfb"
-
- @classmethod
- def run_webkit_tests_command(cls):
- # FIXME: We should find a better way to do this.
- return ["xvfb-run"] + ChromiumPort.run_webkit_tests_command()
diff --git a/WebKitTools/Scripts/webkitpy/common/config/ports_unittest.py b/WebKitTools/Scripts/webkitpy/common/config/ports_unittest.py
deleted file mode 100644
index 3bdf0e6..0000000
--- a/WebKitTools/Scripts/webkitpy/common/config/ports_unittest.py
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/env python
-# 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
-# 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 unittest
-
-from webkitpy.common.config.ports import *
-
-
-class WebKitPortTest(unittest.TestCase):
- def test_mac_port(self):
- self.assertEquals(MacPort.name(), "Mac")
- self.assertEquals(MacPort.flag(), "--port=mac")
- self.assertEquals(MacPort.run_webkit_tests_command(), [WebKitPort.script_path("run-webkit-tests")])
- self.assertEquals(MacPort.build_webkit_command(), [WebKitPort.script_path("build-webkit")])
- 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")
- self.assertEquals(GtkPort.run_webkit_tests_command(), [WebKitPort.script_path("run-webkit-tests"), "--gtk"])
- self.assertEquals(GtkPort.build_webkit_command(), [WebKitPort.script_path("build-webkit"), "--gtk", WebKitPort.makeArgs()])
- self.assertEquals(GtkPort.build_webkit_command(build_style="debug"), [WebKitPort.script_path("build-webkit"), "--debug", "--gtk", WebKitPort.makeArgs()])
-
- def test_qt_port(self):
- self.assertEquals(QtPort.name(), "Qt")
- self.assertEquals(QtPort.flag(), "--port=qt")
- self.assertEquals(QtPort.run_webkit_tests_command(), [WebKitPort.script_path("run-webkit-tests")])
- self.assertEquals(QtPort.build_webkit_command(), [WebKitPort.script_path("build-webkit"), "--qt", WebKitPort.makeArgs()])
- self.assertEquals(QtPort.build_webkit_command(build_style="debug"), [WebKitPort.script_path("build-webkit"), "--debug", "--qt", WebKitPort.makeArgs()])
-
- def test_chromium_port(self):
- self.assertEquals(ChromiumPort.name(), "Chromium")
- self.assertEquals(ChromiumPort.flag(), "--port=chromium")
- self.assertEquals(ChromiumPort.run_webkit_tests_command(), [WebKitPort.script_path("new-run-webkit-tests"), "--chromium", "--use-drt", "--no-pixel-tests"])
- self.assertEquals(ChromiumPort.build_webkit_command(), [WebKitPort.script_path("build-webkit"), "--chromium"])
- self.assertEquals(ChromiumPort.build_webkit_command(build_style="debug"), [WebKitPort.script_path("build-webkit"), "--debug", "--chromium"])
- self.assertEquals(ChromiumPort.update_webkit_command(), [WebKitPort.script_path("update-webkit"), "--chromium"])
-
- def test_chromium_xvfb_port(self):
- self.assertEquals(ChromiumXVFBPort.run_webkit_tests_command(), ["xvfb-run", "WebKitTools/Scripts/new-run-webkit-tests", "--chromium", "--use-drt", "--no-pixel-tests"])
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/memoized.py b/WebKitTools/Scripts/webkitpy/common/memoized.py
deleted file mode 100644
index dc844a5..0000000
--- a/WebKitTools/Scripts/webkitpy/common/memoized.py
+++ /dev/null
@@ -1,55 +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.
-
-# Python does not (yet) seem to provide automatic memoization. So we've
-# written a small decorator to do so.
-
-import functools
-
-
-class memoized(object):
- def __init__(self, function):
- self._function = function
- self._results_cache = {}
-
- def __call__(self, *args):
- try:
- return self._results_cache[args]
- except KeyError:
- # If we didn't find the args in our cache, call and save the results.
- result = self._function(*args)
- self._results_cache[args] = result
- return result
- # FIXME: We may need to handle TypeError here in the case
- # that "args" is not a valid dictionary key.
-
- # Use python "descriptor" protocol __get__ to appear
- # invisible during property access.
- def __get__(self, instance, owner):
- # Return a function partial with obj already bound as self.
- return functools.partial(self.__call__, instance)
diff --git a/WebKitTools/Scripts/webkitpy/common/memoized_unittest.py b/WebKitTools/Scripts/webkitpy/common/memoized_unittest.py
deleted file mode 100644
index dd7c793..0000000
--- a/WebKitTools/Scripts/webkitpy/common/memoized_unittest.py
+++ /dev/null
@@ -1,65 +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.
-
-import unittest
-
-from webkitpy.common.memoized import memoized
-
-
-class _TestObject(object):
- def __init__(self):
- self.callCount = 0
-
- @memoized
- def memoized_add(self, argument):
- """testing docstring"""
- self.callCount += 1
- if argument is None:
- return None # Avoid the TypeError from None + 1
- return argument + 1
-
-
-class MemoizedTest(unittest.TestCase):
- def test_caching(self):
- test = _TestObject()
- test.callCount = 0
- self.assertEqual(test.memoized_add(1), 2)
- self.assertEqual(test.callCount, 1)
- self.assertEqual(test.memoized_add(1), 2)
- self.assertEqual(test.callCount, 1)
-
- # Validate that callCount is working as expected.
- self.assertEqual(test.memoized_add(2), 3)
- self.assertEqual(test.callCount, 2)
-
- def test_tearoff(self):
- test = _TestObject()
- # Make sure that get()/tear-offs work:
- tearoff = test.memoized_add
- self.assertEqual(tearoff(4), 5)
- self.assertEqual(test.callCount, 1)
diff --git a/WebKitTools/Scripts/webkitpy/common/net/__init__.py b/WebKitTools/Scripts/webkitpy/common/net/__init__.py
deleted file mode 100644
index ef65bee..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/common/net/bugzilla/__init__.py b/WebKitTools/Scripts/webkitpy/common/net/bugzilla/__init__.py
deleted file mode 100644
index cfaf3b1..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/bugzilla/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Required for Python to search this directory for module files
-
-# We only export public API here.
-# FIXME: parse_bug_id should not be a free function.
-from .bugzilla import Bugzilla, parse_bug_id
-# Unclear if Bug and Attachment need to be public classes.
-from .bug import Bug
-from .attachment import Attachment
diff --git a/WebKitTools/Scripts/webkitpy/common/net/bugzilla/attachment.py b/WebKitTools/Scripts/webkitpy/common/net/bugzilla/attachment.py
deleted file mode 100644
index 85761fe..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/bugzilla/attachment.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple Inc. All rights reserved.
-# Copyright (c) 2010 Research In Motion Limited. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from webkitpy.common.system.deprecated_logging import log
-
-
-class Attachment(object):
-
- rollout_preamble = "ROLLOUT of r"
-
- def __init__(self, attachment_dictionary, bug):
- self._attachment_dictionary = attachment_dictionary
- self._bug = bug
- self._reviewer = None
- self._committer = None
-
- def _bugzilla(self):
- return self._bug._bugzilla
-
- def id(self):
- return int(self._attachment_dictionary.get("id"))
-
- def attacher_is_committer(self):
- return self._bugzilla.committers.committer_by_email(
- patch.attacher_email())
-
- def attacher_email(self):
- return self._attachment_dictionary.get("attacher_email")
-
- def bug(self):
- return self._bug
-
- def bug_id(self):
- return int(self._attachment_dictionary.get("bug_id"))
-
- def is_patch(self):
- return not not self._attachment_dictionary.get("is_patch")
-
- def is_obsolete(self):
- return not not self._attachment_dictionary.get("is_obsolete")
-
- def is_rollout(self):
- return self.name().startswith(self.rollout_preamble)
-
- def name(self):
- return self._attachment_dictionary.get("name")
-
- def attach_date(self):
- return self._attachment_dictionary.get("attach_date")
-
- def review(self):
- return self._attachment_dictionary.get("review")
-
- def commit_queue(self):
- return self._attachment_dictionary.get("commit-queue")
-
- def url(self):
- # FIXME: This should just return
- # self._bugzilla().attachment_url_for_id(self.id()). scm_unittest.py
- # depends on the current behavior.
- return self._attachment_dictionary.get("url")
-
- def contents(self):
- # FIXME: We shouldn't be grabbing at _bugzilla.
- return self._bug._bugzilla.fetch_attachment_contents(self.id())
-
- def _validate_flag_value(self, flag):
- email = self._attachment_dictionary.get("%s_email" % flag)
- if not email:
- return None
- committer = getattr(self._bugzilla().committers,
- "%s_by_email" % flag)(email)
- if committer:
- return committer
- log("Warning, attachment %s on bug %s has invalid %s (%s)" % (
- self._attachment_dictionary['id'],
- self._attachment_dictionary['bug_id'], flag, email))
-
- def reviewer(self):
- if not self._reviewer:
- self._reviewer = self._validate_flag_value("reviewer")
- return self._reviewer
-
- def committer(self):
- if not self._committer:
- self._committer = self._validate_flag_value("committer")
- return self._committer
diff --git a/WebKitTools/Scripts/webkitpy/common/net/bugzilla/bug.py b/WebKitTools/Scripts/webkitpy/common/net/bugzilla/bug.py
deleted file mode 100644
index 2cb4bc8..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/bugzilla/bug.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple Inc. All rights reserved.
-# Copyright (c) 2010 Research In Motion Limited. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from .attachment import Attachment
-
-
-class Bug(object):
- # FIXME: This class is kinda a hack for now. It exists so we have one
- # place to hold bug logic, even if much of the code deals with
- # dictionaries still.
-
- def __init__(self, bug_dictionary, bugzilla):
- self.bug_dictionary = bug_dictionary
- self._bugzilla = bugzilla
-
- def id(self):
- return self.bug_dictionary["id"]
-
- def title(self):
- return self.bug_dictionary["title"]
-
- def assigned_to_email(self):
- return self.bug_dictionary["assigned_to_email"]
-
- # FIXME: This information should be stored in some sort of webkit_config.py instead of here.
- unassigned_emails = frozenset([
- "webkit-unassigned@lists.webkit.org",
- "webkit-qt-unassigned@trolltech.com",
- ])
-
- def is_unassigned(self):
- return self.assigned_to_email() in self.unassigned_emails
-
- def status(self):
- return self.bug_dictionary["bug_status"]
-
- # Bugzilla has many status states we don't really use in WebKit:
- # https://bugs.webkit.org/page.cgi?id=fields.html#status
- _open_states = ["UNCONFIRMED", "NEW", "ASSIGNED", "REOPENED"]
- _closed_states = ["RESOLVED", "VERIFIED", "CLOSED"]
-
- def is_open(self):
- return self.status() in self._open_states
-
- def is_closed(self):
- return not self.is_open()
-
- # Rarely do we actually want obsolete attachments
- def attachments(self, include_obsolete=False):
- attachments = self.bug_dictionary["attachments"]
- if not include_obsolete:
- attachments = filter(lambda attachment:
- not attachment["is_obsolete"], attachments)
- return [Attachment(attachment, self) for attachment in attachments]
-
- def patches(self, include_obsolete=False):
- return [patch for patch in self.attachments(include_obsolete)
- if patch.is_patch()]
-
- def unreviewed_patches(self):
- return [patch for patch in self.patches() if patch.review() == "?"]
-
- def reviewed_patches(self, include_invalid=False):
- patches = [patch for patch in self.patches() if patch.review() == "+"]
- if include_invalid:
- return patches
- # Checking reviewer() ensures that it was both reviewed and has a valid
- # reviewer.
- return filter(lambda patch: patch.reviewer(), patches)
-
- def commit_queued_patches(self, include_invalid=False):
- patches = [patch for patch in self.patches()
- if patch.commit_queue() == "+"]
- if include_invalid:
- return patches
- # Checking committer() ensures that it was both commit-queue+'d and has
- # a valid committer.
- return filter(lambda patch: patch.committer(), patches)
diff --git a/WebKitTools/Scripts/webkitpy/common/net/bugzilla/bug_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/bugzilla/bug_unittest.py
deleted file mode 100644
index d43d64f..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/bugzilla/bug_unittest.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# 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
-# 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 unittest
-
-from .bug import Bug
-
-
-class BugTest(unittest.TestCase):
- def test_is_unassigned(self):
- for email in Bug.unassigned_emails:
- bug = Bug({"assigned_to_email": email}, bugzilla=None)
- self.assertTrue(bug.is_unassigned())
- bug = Bug({"assigned_to_email": "test@test.com"}, bugzilla=None)
- self.assertFalse(bug.is_unassigned())
diff --git a/WebKitTools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py b/WebKitTools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py
deleted file mode 100644
index 9fa7fe5..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py
+++ /dev/null
@@ -1,674 +0,0 @@
-# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple Inc. All rights reserved.
-# Copyright (c) 2010 Research In Motion Limited. 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.
-#
-# WebKit's Python module for interacting with Bugzilla
-
-import os.path
-import re
-import StringIO
-
-from datetime import datetime # used in timestamp()
-
-from .attachment import Attachment
-from .bug import Bug
-
-from webkitpy.common.system.deprecated_logging import log
-from webkitpy.common.config import committers
-from webkitpy.common.net.credentials import Credentials
-from webkitpy.common.system.user import User
-from webkitpy.thirdparty.autoinstalled.mechanize import Browser
-from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup, SoupStrainer
-
-
-# FIXME: parse_bug_id should not be a free function.
-def parse_bug_id(message):
- if not message:
- return None
- match = re.search("http\://webkit\.org/b/(?P<bug_id>\d+)", message)
- if match:
- return int(match.group('bug_id'))
- match = re.search(
- Bugzilla.bug_server_regex + "show_bug\.cgi\?id=(?P<bug_id>\d+)",
- message)
- if match:
- return int(match.group('bug_id'))
- return None
-
-
-def timestamp():
- return datetime.now().strftime("%Y%m%d%H%M%S")
-
-
-# A container for all of the logic for making and parsing buzilla queries.
-class BugzillaQueries(object):
-
- def __init__(self, bugzilla):
- self._bugzilla = bugzilla
-
- # Note: _load_query and _fetch_bug are the only two methods which access
- # self._bugzilla.
-
- def _load_query(self, query):
- self._bugzilla.authenticate()
-
- full_url = "%s%s" % (self._bugzilla.bug_server_url, query)
- return self._bugzilla.browser.open(full_url)
-
- def _fetch_bug(self, bug_id):
- return self._bugzilla.fetch_bug(bug_id)
-
- def _fetch_bug_ids_advanced_query(self, query):
- soup = BeautifulSoup(self._load_query(query))
- # The contents of the <a> inside the cells in the first column happen
- # to be the bug id.
- return [int(bug_link_cell.find("a").string)
- for bug_link_cell in soup('td', "first-child")]
-
- def _parse_attachment_ids_request_query(self, page):
- digits = re.compile("\d+")
- attachment_href = re.compile("attachment.cgi\?id=\d+&action=review")
- attachment_links = SoupStrainer("a", href=attachment_href)
- return [int(digits.search(tag["href"]).group(0))
- for tag in BeautifulSoup(page, parseOnlyThese=attachment_links)]
-
- def _fetch_attachment_ids_request_query(self, query):
- return self._parse_attachment_ids_request_query(self._load_query(query))
-
- def _parse_quips(self, page):
- soup = BeautifulSoup(page, convertEntities=BeautifulSoup.HTML_ENTITIES)
- quips = soup.find(text=re.compile(r"Existing quips:")).findNext("ul").findAll("li")
- return [unicode(quip_entry.string) for quip_entry in quips]
-
- def fetch_quips(self):
- return self._parse_quips(self._load_query("/quips.cgi?action=show"))
-
- # List of all r+'d bugs.
- def fetch_bug_ids_from_pending_commit_list(self):
- needs_commit_query_url = "buglist.cgi?query_format=advanced&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&field0-0-0=flagtypes.name&type0-0-0=equals&value0-0-0=review%2B"
- return self._fetch_bug_ids_advanced_query(needs_commit_query_url)
-
- def fetch_patches_from_pending_commit_list(self):
- return sum([self._fetch_bug(bug_id).reviewed_patches()
- for bug_id in self.fetch_bug_ids_from_pending_commit_list()], [])
-
- def fetch_bug_ids_from_commit_queue(self):
- commit_queue_url = "buglist.cgi?query_format=advanced&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&field0-0-0=flagtypes.name&type0-0-0=equals&value0-0-0=commit-queue%2B&order=Last+Changed"
- return self._fetch_bug_ids_advanced_query(commit_queue_url)
-
- def fetch_patches_from_commit_queue(self):
- # This function will only return patches which have valid committers
- # set. It won't reject patches with invalid committers/reviewers.
- return sum([self._fetch_bug(bug_id).commit_queued_patches()
- for bug_id in self.fetch_bug_ids_from_commit_queue()], [])
-
- def fetch_bug_ids_from_review_queue(self):
- review_queue_url = "buglist.cgi?query_format=advanced&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&field0-0-0=flagtypes.name&type0-0-0=equals&value0-0-0=review?"
- return self._fetch_bug_ids_advanced_query(review_queue_url)
-
- # This method will make several requests to bugzilla.
- def fetch_patches_from_review_queue(self, limit=None):
- # [:None] returns the whole array.
- return sum([self._fetch_bug(bug_id).unreviewed_patches()
- for bug_id in self.fetch_bug_ids_from_review_queue()[:limit]], [])
-
- # NOTE: This is the only client of _fetch_attachment_ids_request_query
- # This method only makes one request to bugzilla.
- def fetch_attachment_ids_from_review_queue(self):
- review_queue_url = "request.cgi?action=queue&type=review&group=type"
- return self._fetch_attachment_ids_request_query(review_queue_url)
-
-
-class Bugzilla(object):
-
- def __init__(self, dryrun=False, committers=committers.CommitterList()):
- self.dryrun = dryrun
- self.authenticated = False
- self.queries = BugzillaQueries(self)
- self.committers = committers
- self.cached_quips = []
-
- # FIXME: We should use some sort of Browser mock object when in dryrun
- # mode (to prevent any mistakes).
- self.browser = Browser()
- # Ignore bugs.webkit.org/robots.txt until we fix it to allow this
- # script.
- self.browser.set_handle_robots(False)
-
- # FIXME: Much of this should go into some sort of config module:
- bug_server_host = "bugs.webkit.org"
- bug_server_regex = "https?://%s/" % re.sub('\.', '\\.', bug_server_host)
- bug_server_url = "https://%s/" % bug_server_host
-
- def quips(self):
- # We only fetch and parse the list of quips once per instantiation
- # so that we do not burden bugs.webkit.org.
- if not self.cached_quips and not self.dryrun:
- self.cached_quips = self.queries.fetch_quips()
- return self.cached_quips
-
- def bug_url_for_bug_id(self, bug_id, xml=False):
- if not bug_id:
- return None
- content_type = "&ctype=xml" if xml else ""
- return "%sshow_bug.cgi?id=%s%s" % (self.bug_server_url,
- bug_id,
- content_type)
-
- def short_bug_url_for_bug_id(self, bug_id):
- if not bug_id:
- return None
- return "http://webkit.org/b/%s" % bug_id
-
- def attachment_url_for_id(self, attachment_id, action="view"):
- if not attachment_id:
- return None
- action_param = ""
- if action and action != "view":
- action_param = "&action=%s" % action
- return "%sattachment.cgi?id=%s%s" % (self.bug_server_url,
- attachment_id,
- action_param)
-
- def _parse_attachment_flag(self,
- element,
- flag_name,
- attachment,
- result_key):
- flag = element.find('flag', attrs={'name': flag_name})
- if flag:
- attachment[flag_name] = flag['status']
- if flag['status'] == '+':
- attachment[result_key] = flag['setter']
- # Sadly show_bug.cgi?ctype=xml does not expose the flag modification date.
-
- def _string_contents(self, soup):
- # WebKit's bugzilla instance uses UTF-8.
- # BeautifulSoup always returns Unicode strings, however
- # the .string method returns a (unicode) NavigableString.
- # NavigableString can confuse other parts of the code, so we
- # convert from NavigableString to a real unicode() object using unicode().
- return unicode(soup.string)
-
- # Example: 2010-01-20 14:31 PST
- # FIXME: Some bugzilla dates seem to have seconds in them?
- # Python does not support timezones out of the box.
- # Assume that bugzilla always uses PST (which is true for bugs.webkit.org)
- _bugzilla_date_format = "%Y-%m-%d %H:%M"
-
- @classmethod
- def _parse_date(cls, date_string):
- (date, time, time_zone) = date_string.split(" ")
- # Ignore the timezone because python doesn't understand timezones out of the box.
- date_string = "%s %s" % (date, time)
- return datetime.strptime(date_string, cls._bugzilla_date_format)
-
- def _date_contents(self, soup):
- return self._parse_date(self._string_contents(soup))
-
- def _parse_attachment_element(self, element, bug_id):
- attachment = {}
- attachment['bug_id'] = bug_id
- attachment['is_obsolete'] = (element.has_key('isobsolete') and element['isobsolete'] == "1")
- attachment['is_patch'] = (element.has_key('ispatch') and element['ispatch'] == "1")
- attachment['id'] = int(element.find('attachid').string)
- # FIXME: No need to parse out the url here.
- attachment['url'] = self.attachment_url_for_id(attachment['id'])
- attachment["attach_date"] = self._date_contents(element.find("date"))
- attachment['name'] = self._string_contents(element.find('desc'))
- attachment['attacher_email'] = self._string_contents(element.find('attacher'))
- attachment['type'] = self._string_contents(element.find('type'))
- self._parse_attachment_flag(
- element, 'review', attachment, 'reviewer_email')
- self._parse_attachment_flag(
- element, 'commit-queue', attachment, 'committer_email')
- return attachment
-
- def _parse_bug_page(self, page):
- soup = BeautifulSoup(page)
- bug = {}
- bug["id"] = int(soup.find("bug_id").string)
- bug["title"] = self._string_contents(soup.find("short_desc"))
- bug["bug_status"] = self._string_contents(soup.find("bug_status"))
- bug["reporter_email"] = self._string_contents(soup.find("reporter"))
- bug["assigned_to_email"] = self._string_contents(soup.find("assigned_to"))
- bug["cc_emails"] = [self._string_contents(element)
- for element in soup.findAll('cc')]
- bug["attachments"] = [self._parse_attachment_element(element, bug["id"]) for element in soup.findAll('attachment')]
- return bug
-
- # Makes testing fetch_*_from_bug() possible until we have a better
- # BugzillaNetwork abstration.
-
- def _fetch_bug_page(self, bug_id):
- bug_url = self.bug_url_for_bug_id(bug_id, xml=True)
- log("Fetching: %s" % bug_url)
- return self.browser.open(bug_url)
-
- def fetch_bug_dictionary(self, bug_id):
- try:
- return self._parse_bug_page(self._fetch_bug_page(bug_id))
- except KeyboardInterrupt:
- raise
- except:
- self.authenticate()
- return self._parse_bug_page(self._fetch_bug_page(bug_id))
-
- # FIXME: A BugzillaCache object should provide all these fetch_ methods.
-
- def fetch_bug(self, bug_id):
- return Bug(self.fetch_bug_dictionary(bug_id), self)
-
- def fetch_attachment_contents(self, attachment_id):
- attachment_url = self.attachment_url_for_id(attachment_id)
- # We need to authenticate to download patches from security bugs.
- self.authenticate()
- return self.browser.open(attachment_url).read()
-
- def _parse_bug_id_from_attachment_page(self, page):
- # The "Up" relation happens to point to the bug.
- up_link = BeautifulSoup(page).find('link', rel='Up')
- if not up_link:
- # This attachment does not exist (or you don't have permissions to
- # view it).
- return None
- match = re.search("show_bug.cgi\?id=(?P<bug_id>\d+)", up_link['href'])
- return int(match.group('bug_id'))
-
- def bug_id_for_attachment_id(self, attachment_id):
- self.authenticate()
-
- attachment_url = self.attachment_url_for_id(attachment_id, 'edit')
- log("Fetching: %s" % attachment_url)
- page = self.browser.open(attachment_url)
- return self._parse_bug_id_from_attachment_page(page)
-
- # FIXME: This should just return Attachment(id), which should be able to
- # lazily fetch needed data.
-
- def fetch_attachment(self, attachment_id):
- # We could grab all the attachment details off of the attachment edit
- # page but we already have working code to do so off of the bugs page,
- # so re-use that.
- bug_id = self.bug_id_for_attachment_id(attachment_id)
- if not bug_id:
- return None
- attachments = self.fetch_bug(bug_id).attachments(include_obsolete=True)
- for attachment in attachments:
- if attachment.id() == int(attachment_id):
- return attachment
- return None # This should never be hit.
-
- def authenticate(self):
- if self.authenticated:
- return
-
- if self.dryrun:
- log("Skipping log in for dry run...")
- self.authenticated = True
- return
-
- credentials = Credentials(self.bug_server_host, git_prefix="bugzilla")
-
- attempts = 0
- while not self.authenticated:
- attempts += 1
- username, password = credentials.read_credentials()
-
- log("Logging in as %s..." % username)
- self.browser.open(self.bug_server_url +
- "index.cgi?GoAheadAndLogIn=1")
- self.browser.select_form(name="login")
- self.browser['Bugzilla_login'] = username
- self.browser['Bugzilla_password'] = password
- response = self.browser.submit()
-
- match = re.search("<title>(.+?)</title>", response.read())
- # If the resulting page has a title, and it contains the word
- # "invalid" assume it's the login failure page.
- if match and re.search("Invalid", match.group(1), re.IGNORECASE):
- errorMessage = "Bugzilla login failed: %s" % match.group(1)
- # raise an exception only if this was the last attempt
- if attempts < 5:
- log(errorMessage)
- else:
- raise Exception(errorMessage)
- else:
- self.authenticated = True
- self.username = username
-
- def _fill_attachment_form(self,
- description,
- patch_file_object,
- comment_text=None,
- mark_for_review=False,
- mark_for_commit_queue=False,
- mark_for_landing=False,
- bug_id=None):
- self.browser['description'] = description
- self.browser['ispatch'] = ("1",)
- self.browser['flag_type-1'] = ('?',) if mark_for_review else ('X',)
-
- if mark_for_landing:
- self.browser['flag_type-3'] = ('+',)
- elif mark_for_commit_queue:
- self.browser['flag_type-3'] = ('?',)
- else:
- self.browser['flag_type-3'] = ('X',)
-
- if bug_id:
- patch_name = "bug-%s-%s.patch" % (bug_id, timestamp())
- else:
- patch_name ="%s.patch" % timestamp()
-
- self.browser.add_file(patch_file_object,
- "text/plain",
- patch_name,
- 'data')
-
- def add_patch_to_bug(self,
- bug_id,
- diff,
- description,
- comment_text=None,
- mark_for_review=False,
- mark_for_commit_queue=False,
- mark_for_landing=False):
- self.authenticate()
-
- log('Adding patch "%s" to %sshow_bug.cgi?id=%s' % (description,
- self.bug_server_url,
- bug_id))
-
- if self.dryrun:
- log(comment_text)
- return
-
- self.browser.open("%sattachment.cgi?action=enter&bugid=%s" % (
- self.bug_server_url, bug_id))
- self.browser.select_form(name="entryform")
-
- # _fill_attachment_form expects a file-like object
- # Patch files are already binary, so no encoding needed.
- assert(isinstance(diff, str))
- patch_file_object = StringIO.StringIO(diff)
- self._fill_attachment_form(description,
- patch_file_object,
- mark_for_review=mark_for_review,
- mark_for_commit_queue=mark_for_commit_queue,
- mark_for_landing=mark_for_landing,
- bug_id=bug_id)
- if comment_text:
- log(comment_text)
- self.browser['comment'] = comment_text
- self.browser.submit()
-
- def _check_create_bug_response(self, response_html):
- match = re.search("<title>Bug (?P<bug_id>\d+) Submitted</title>",
- response_html)
- if match:
- return match.group('bug_id')
-
- match = re.search(
- '<div id="bugzilla-body">(?P<error_message>.+)<div id="footer">',
- response_html,
- re.DOTALL)
- error_message = "FAIL"
- if match:
- text_lines = BeautifulSoup(
- match.group('error_message')).findAll(text=True)
- error_message = "\n" + '\n'.join(
- [" " + line.strip()
- for line in text_lines if line.strip()])
- raise Exception("Bug not created: %s" % error_message)
-
- def create_bug(self,
- bug_title,
- bug_description,
- component=None,
- diff=None,
- patch_description=None,
- cc=None,
- blocked=None,
- assignee=None,
- mark_for_review=False,
- mark_for_commit_queue=False):
- self.authenticate()
-
- log('Creating bug with title "%s"' % bug_title)
- if self.dryrun:
- log(bug_description)
- return
-
- self.browser.open(self.bug_server_url + "enter_bug.cgi?product=WebKit")
- self.browser.select_form(name="Create")
- component_items = self.browser.find_control('component').items
- component_names = map(lambda item: item.name, component_items)
- if not component:
- component = "New Bugs"
- if component not in component_names:
- component = User.prompt_with_list("Please pick a component:", component_names)
- self.browser["component"] = [component]
- if cc:
- self.browser["cc"] = cc
- if blocked:
- self.browser["blocked"] = unicode(blocked)
- if assignee == None:
- assignee = self.username
- if assignee and not self.browser.find_control("assigned_to").disabled:
- self.browser["assigned_to"] = assignee
- self.browser["short_desc"] = bug_title
- self.browser["comment"] = bug_description
-
- if diff:
- # _fill_attachment_form expects a file-like object
- # Patch files are already binary, so no encoding needed.
- assert(isinstance(diff, str))
- patch_file_object = StringIO.StringIO(diff)
- self._fill_attachment_form(
- patch_description,
- patch_file_object,
- mark_for_review=mark_for_review,
- mark_for_commit_queue=mark_for_commit_queue)
-
- response = self.browser.submit()
-
- bug_id = self._check_create_bug_response(response.read())
- log("Bug %s created." % bug_id)
- log("%sshow_bug.cgi?id=%s" % (self.bug_server_url, bug_id))
- return bug_id
-
- def _find_select_element_for_flag(self, flag_name):
- # FIXME: This will break if we ever re-order attachment flags
- if flag_name == "review":
- return self.browser.find_control(type='select', nr=0)
- elif flag_name == "commit-queue":
- return self.browser.find_control(type='select', nr=1)
- raise Exception("Don't know how to find flag named \"%s\"" % flag_name)
-
- def clear_attachment_flags(self,
- attachment_id,
- additional_comment_text=None):
- self.authenticate()
-
- comment_text = "Clearing flags on attachment: %s" % attachment_id
- if additional_comment_text:
- comment_text += "\n\n%s" % additional_comment_text
- log(comment_text)
-
- if self.dryrun:
- return
-
- self.browser.open(self.attachment_url_for_id(attachment_id, 'edit'))
- self.browser.select_form(nr=1)
- self.browser.set_value(comment_text, name='comment', nr=0)
- self._find_select_element_for_flag('review').value = ("X",)
- self._find_select_element_for_flag('commit-queue').value = ("X",)
- self.browser.submit()
-
- def set_flag_on_attachment(self,
- attachment_id,
- flag_name,
- flag_value,
- comment_text=None,
- additional_comment_text=None):
- # FIXME: We need a way to test this function on a live bugzilla
- # instance.
-
- self.authenticate()
-
- if additional_comment_text:
- comment_text += "\n\n%s" % additional_comment_text
- log(comment_text)
-
- if self.dryrun:
- return
-
- self.browser.open(self.attachment_url_for_id(attachment_id, 'edit'))
- self.browser.select_form(nr=1)
-
- if comment_text:
- self.browser.set_value(comment_text, name='comment', nr=0)
-
- self._find_select_element_for_flag(flag_name).value = (flag_value,)
- self.browser.submit()
-
- # FIXME: All of these bug editing methods have a ridiculous amount of
- # copy/paste code.
-
- def obsolete_attachment(self, attachment_id, comment_text=None):
- self.authenticate()
-
- log("Obsoleting attachment: %s" % attachment_id)
- if self.dryrun:
- log(comment_text)
- return
-
- self.browser.open(self.attachment_url_for_id(attachment_id, 'edit'))
- self.browser.select_form(nr=1)
- self.browser.find_control('isobsolete').items[0].selected = True
- # Also clear any review flag (to remove it from review/commit queues)
- self._find_select_element_for_flag('review').value = ("X",)
- self._find_select_element_for_flag('commit-queue').value = ("X",)
- if comment_text:
- log(comment_text)
- # Bugzilla has two textareas named 'comment', one is somehow
- # hidden. We want the first.
- self.browser.set_value(comment_text, name='comment', nr=0)
- self.browser.submit()
-
- def add_cc_to_bug(self, bug_id, email_address_list):
- self.authenticate()
-
- log("Adding %s to the CC list for bug %s" % (email_address_list,
- bug_id))
- if self.dryrun:
- return
-
- self.browser.open(self.bug_url_for_bug_id(bug_id))
- self.browser.select_form(name="changeform")
- self.browser["newcc"] = ", ".join(email_address_list)
- self.browser.submit()
-
- def post_comment_to_bug(self, bug_id, comment_text, cc=None):
- self.authenticate()
-
- log("Adding comment to bug %s" % bug_id)
- if self.dryrun:
- log(comment_text)
- return
-
- self.browser.open(self.bug_url_for_bug_id(bug_id))
- self.browser.select_form(name="changeform")
- self.browser["comment"] = comment_text
- if cc:
- self.browser["newcc"] = ", ".join(cc)
- self.browser.submit()
-
- def close_bug_as_fixed(self, bug_id, comment_text=None):
- self.authenticate()
-
- log("Closing bug %s as fixed" % bug_id)
- if self.dryrun:
- log(comment_text)
- return
-
- self.browser.open(self.bug_url_for_bug_id(bug_id))
- self.browser.select_form(name="changeform")
- if comment_text:
- self.browser['comment'] = comment_text
- self.browser['bug_status'] = ['RESOLVED']
- self.browser['resolution'] = ['FIXED']
- self.browser.submit()
-
- def reassign_bug(self, bug_id, assignee, comment_text=None):
- self.authenticate()
-
- log("Assigning bug %s to %s" % (bug_id, assignee))
- if self.dryrun:
- log(comment_text)
- return
-
- self.browser.open(self.bug_url_for_bug_id(bug_id))
- self.browser.select_form(name="changeform")
- if comment_text:
- log(comment_text)
- self.browser["comment"] = comment_text
- self.browser["assigned_to"] = assignee
- self.browser.submit()
-
- def reopen_bug(self, bug_id, comment_text):
- self.authenticate()
-
- log("Re-opening bug %s" % bug_id)
- # Bugzilla requires a comment when re-opening a bug, so we know it will
- # never be None.
- log(comment_text)
- if self.dryrun:
- return
-
- self.browser.open(self.bug_url_for_bug_id(bug_id))
- self.browser.select_form(name="changeform")
- bug_status = self.browser.find_control("bug_status", type="select")
- # This is a hack around the fact that ClientForm.ListControl seems to
- # have no simpler way to ask if a control has an item named "REOPENED"
- # without using exceptions for control flow.
- 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:
- # 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/bugzilla_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/bugzilla/bugzilla_unittest.py
deleted file mode 100644
index c476c81..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/bugzilla/bugzilla_unittest.py
+++ /dev/null
@@ -1,342 +0,0 @@
-# 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
-# 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 unittest
-import datetime
-
-from .bugzilla import Bugzilla, BugzillaQueries, parse_bug_id
-
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.tool.mocktool import MockBrowser
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup
-
-
-class BugzillaTest(unittest.TestCase):
- _example_attachment = '''
- <attachment
- isobsolete="1"
- ispatch="1"
- isprivate="0"
- >
- <attachid>33721</attachid>
- <date>2009-07-29 10:23 PDT</date>
- <desc>Fixed whitespace issue</desc>
- <filename>patch</filename>
- <type>text/plain</type>
- <size>9719</size>
- <attacher>christian.plesner.hansen@gmail.com</attacher>
- <flag name="review"
- id="17931"
- status="+"
- setter="one@test.com"
- />
- <flag name="commit-queue"
- id="17932"
- status="+"
- setter="two@test.com"
- />
- </attachment>
-'''
- _expected_example_attachment_parsing = {
- 'attach_date': datetime.datetime(2009, 07, 29, 10, 23),
- 'bug_id' : 100,
- 'is_obsolete' : True,
- 'is_patch' : True,
- 'id' : 33721,
- 'url' : "https://bugs.webkit.org/attachment.cgi?id=33721",
- 'name' : "Fixed whitespace issue",
- 'type' : "text/plain",
- 'review' : '+',
- 'reviewer_email' : 'one@test.com',
- 'commit-queue' : '+',
- 'committer_email' : 'two@test.com',
- 'attacher_email' : 'christian.plesner.hansen@gmail.com',
- }
-
- def test_url_creation(self):
- # FIXME: These would be all better as doctests
- bugs = Bugzilla()
- self.assertEquals(None, bugs.bug_url_for_bug_id(None))
- self.assertEquals(None, bugs.short_bug_url_for_bug_id(None))
- self.assertEquals(None, bugs.attachment_url_for_id(None))
-
- def test_parse_bug_id(self):
- # FIXME: These would be all better as doctests
- bugs = Bugzilla()
- self.assertEquals(12345, parse_bug_id("http://webkit.org/b/12345"))
- self.assertEquals(12345, parse_bug_id("http://bugs.webkit.org/show_bug.cgi?id=12345"))
- self.assertEquals(12345, parse_bug_id(bugs.short_bug_url_for_bug_id(12345)))
- self.assertEquals(12345, parse_bug_id(bugs.bug_url_for_bug_id(12345)))
- self.assertEquals(12345, parse_bug_id(bugs.bug_url_for_bug_id(12345, xml=True)))
-
- # Our bug parser is super-fragile, but at least we're testing it.
- self.assertEquals(None, parse_bug_id("http://www.webkit.org/b/12345"))
- self.assertEquals(None, parse_bug_id("http://bugs.webkit.org/show_bug.cgi?ctype=xml&id=12345"))
-
- _example_bug = """
-<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
-<!DOCTYPE bugzilla SYSTEM "https://bugs.webkit.org/bugzilla.dtd">
-<bugzilla version="3.2.3"
- urlbase="https://bugs.webkit.org/"
- maintainer="admin@webkit.org"
- exporter="eric@webkit.org"
->
- <bug>
- <bug_id>32585</bug_id>
- <creation_ts>2009-12-15 15:17 PST</creation_ts>
- <short_desc>bug to test webkit-patch and commit-queue failures</short_desc>
- <delta_ts>2009-12-27 21:04:50 PST</delta_ts>
- <reporter_accessible>1</reporter_accessible>
- <cclist_accessible>1</cclist_accessible>
- <classification_id>1</classification_id>
- <classification>Unclassified</classification>
- <product>WebKit</product>
- <component>Tools / Tests</component>
- <version>528+ (Nightly build)</version>
- <rep_platform>PC</rep_platform>
- <op_sys>Mac OS X 10.5</op_sys>
- <bug_status>NEW</bug_status>
- <priority>P2</priority>
- <bug_severity>Normal</bug_severity>
- <target_milestone>---</target_milestone>
- <everconfirmed>1</everconfirmed>
- <reporter name="Eric Seidel">eric@webkit.org</reporter>
- <assigned_to name="Nobody">webkit-unassigned@lists.webkit.org</assigned_to>
- <cc>foo@bar.com</cc>
- <cc>example@example.com</cc>
- <long_desc isprivate="0">
- <who name="Eric Seidel">eric@webkit.org</who>
- <bug_when>2009-12-15 15:17:28 PST</bug_when>
- <thetext>bug to test webkit-patch and commit-queue failures
-
-Ignore this bug. Just for testing failure modes of webkit-patch and the commit-queue.</thetext>
- </long_desc>
- <attachment
- isobsolete="0"
- ispatch="1"
- isprivate="0"
- >
- <attachid>45548</attachid>
- <date>2009-12-27 23:51 PST</date>
- <desc>Patch</desc>
- <filename>bug-32585-20091228005112.patch</filename>
- <type>text/plain</type>
- <size>10882</size>
- <attacher>mjs@apple.com</attacher>
-
- <token>1261988248-dc51409e9c421a4358f365fa8bec8357</token>
- <data encoding="base64">SW5kZXg6IFdlYktpdC9tYWMvQ2hhbmdlTG9nCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09
-removed-because-it-was-really-long
-ZEZpbmlzaExvYWRXaXRoUmVhc29uOnJlYXNvbl07Cit9CisKIEBlbmQKIAogI2VuZGlmCg==
-</data>
-
- <flag name="review"
- id="27602"
- status="?"
- setter="mjs@apple.com"
- />
- </attachment>
- </bug>
-</bugzilla>
-"""
- _expected_example_bug_parsing = {
- "id" : 32585,
- "title" : u"bug to test webkit-patch and commit-queue failures",
- "cc_emails" : ["foo@bar.com", "example@example.com"],
- "reporter_email" : "eric@webkit.org",
- "assigned_to_email" : "webkit-unassigned@lists.webkit.org",
- "bug_status": "NEW",
- "attachments" : [{
- "attach_date": datetime.datetime(2009, 12, 27, 23, 51),
- 'name': u'Patch',
- 'url' : "https://bugs.webkit.org/attachment.cgi?id=45548",
- 'is_obsolete': False,
- 'review': '?',
- 'is_patch': True,
- 'attacher_email': 'mjs@apple.com',
- 'bug_id': 32585,
- 'type': 'text/plain',
- 'id': 45548
- }],
- }
-
- # FIXME: This should move to a central location and be shared by more unit tests.
- def _assert_dictionaries_equal(self, actual, expected):
- # Make sure we aren't parsing more or less than we expect
- self.assertEquals(sorted(actual.keys()), sorted(expected.keys()))
-
- for key, expected_value in expected.items():
- self.assertEquals(actual[key], expected_value, ("Failure for key: %s: Actual='%s' Expected='%s'" % (key, actual[key], expected_value)))
-
- def test_bug_parsing(self):
- bug = Bugzilla()._parse_bug_page(self._example_bug)
- self._assert_dictionaries_equal(bug, self._expected_example_bug_parsing)
-
- # This could be combined into test_bug_parsing later if desired.
- def test_attachment_parsing(self):
- bugzilla = Bugzilla()
- soup = BeautifulSoup(self._example_attachment)
- attachment_element = soup.find("attachment")
- attachment = bugzilla._parse_attachment_element(attachment_element, self._expected_example_attachment_parsing['bug_id'])
- self.assertTrue(attachment)
- self._assert_dictionaries_equal(attachment, self._expected_example_attachment_parsing)
-
- _sample_attachment_detail_page = """
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
- "http://www.w3.org/TR/html4/loose.dtd">
-<html>
- <head>
- <title>
- Attachment 41073 Details for Bug 27314</title>
-<link rel="Top" href="https://bugs.webkit.org/">
- <link rel="Up" href="show_bug.cgi?id=27314">
-"""
-
- def test_attachment_detail_bug_parsing(self):
- bugzilla = Bugzilla()
- self.assertEquals(27314, bugzilla._parse_bug_id_from_attachment_page(self._sample_attachment_detail_page))
-
- def test_add_cc_to_bug(self):
- bugzilla = Bugzilla()
- bugzilla.browser = MockBrowser()
- bugzilla.authenticate = lambda: None
- 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 = """
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
- "http://www.w3.org/TR/html4/loose.dtd">
-<html>
- <head>
- <title>Request Queue</title>
- </head>
-<body>
-
-<h3>Flag: review</h3>
- <table class="requests" cellspacing="0" cellpadding="4" border="1">
- <tr>
- <th>Requester</th>
- <th>Requestee</th>
- <th>Bug</th>
- <th>Attachment</th>
- <th>Created</th>
- </tr>
- <tr>
- <td>Shinichiro Hamaji &lt;hamaji&#64;chromium.org&gt;</td>
- <td></td>
- <td><a href="show_bug.cgi?id=30015">30015: text-transform:capitalize is failing in CSS2.1 test suite</a></td>
- <td><a href="attachment.cgi?id=40511&amp;action=review">
-40511: Patch v0</a></td>
- <td>2009-10-02 04:58 PST</td>
- </tr>
- <tr>
- <td>Zan Dobersek &lt;zandobersek&#64;gmail.com&gt;</td>
- <td></td>
- <td><a href="show_bug.cgi?id=26304">26304: [GTK] Add controls for playing html5 video.</a></td>
- <td><a href="attachment.cgi?id=40722&amp;action=review">
-40722: Media controls, the simple approach</a></td>
- <td>2009-10-06 09:13 PST</td>
- </tr>
- <tr>
- <td>Zan Dobersek &lt;zandobersek&#64;gmail.com&gt;</td>
- <td></td>
- <td><a href="show_bug.cgi?id=26304">26304: [GTK] Add controls for playing html5 video.</a></td>
- <td><a href="attachment.cgi?id=40723&amp;action=review">
-40723: Adjust the media slider thumb size</a></td>
- <td>2009-10-06 09:15 PST</td>
- </tr>
- </table>
-</body>
-</html>
-"""
- _sample_quip_page = u"""
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
- "http://www.w3.org/TR/html4/loose.dtd">
-<html>
- <head>
- <title>Bugzilla Quip System</title>
- </head>
- <body>
- <h2>
-
- Existing quips:
- </h2>
- <ul>
- <li>Everything should be made as simple as possible, but not simpler. - Albert Einstein</li>
- <li>Good artists copy. Great artists steal. - Pablo Picasso</li>
- <li>\u00e7gua mole em pedra dura, tanto bate at\u008e que fura.</li>
-
- </ul>
- </body>
-</html>
-"""
-
- def test_request_page_parsing(self):
- queries = BugzillaQueries(None)
- self.assertEquals([40511, 40722, 40723], queries._parse_attachment_ids_request_query(self._sample_request_page))
-
- def test_quip_page_parsing(self):
- queries = BugzillaQueries(None)
- expected_quips = ["Everything should be made as simple as possible, but not simpler. - Albert Einstein", "Good artists copy. Great artists steal. - Pablo Picasso", u"\u00e7gua mole em pedra dura, tanto bate at\u008e que fura."]
- self.assertEquals(expected_quips, queries._parse_quips(self._sample_quip_page))
-
- def test_load_query(self):
- queries = BugzillaQueries(Mock())
- queries._load_query("request.cgi?action=queue&type=review&group=type")
diff --git a/WebKitTools/Scripts/webkitpy/common/net/buildbot.py b/WebKitTools/Scripts/webkitpy/common/net/buildbot.py
deleted file mode 100644
index 88cdd4e..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/buildbot.py
+++ /dev/null
@@ -1,452 +0,0 @@
-# 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
-# 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.
-#
-# WebKit's Python module for interacting with WebKit's buildbot
-
-import operator
-import re
-import urllib
-import urllib2
-import xmlrpclib
-
-from webkitpy.common.net.failuremap import FailureMap
-from webkitpy.common.net.layouttestresults import LayoutTestResults
-from webkitpy.common.net.regressionwindow import RegressionWindow
-from webkitpy.common.system.logutils import get_logger
-from webkitpy.thirdparty.autoinstalled.mechanize import Browser
-from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup
-
-_log = get_logger(__file__)
-
-
-class Builder(object):
- def __init__(self, name, buildbot):
- self._name = name
- self._buildbot = buildbot
- self._builds_cache = {}
- self._revision_to_build_number = None
- self._browser = Browser()
- self._browser.set_handle_robots(False) # The builder pages are excluded by robots.txt
-
- def name(self):
- return self._name
-
- def results_url(self):
- return "http://%s/results/%s" % (self._buildbot.buildbot_host, self.url_encoded_name())
-
- def url_encoded_name(self):
- return urllib.quote(self._name)
-
- def url(self):
- return "http://%s/builders/%s" % (self._buildbot.buildbot_host, self.url_encoded_name())
-
- # This provides a single place to mock
- def _fetch_build(self, build_number):
- build_dictionary = self._buildbot._fetch_xmlrpc_build_dictionary(self, build_number)
- if not build_dictionary:
- return None
- return Build(self,
- build_number=int(build_dictionary['number']),
- revision=int(build_dictionary['revision']),
- is_green=(build_dictionary['results'] == 0) # Undocumented, buildbot XMLRPC, 0 seems to mean "pass"
- )
-
- def build(self, build_number):
- if not build_number:
- return None
- cached_build = self._builds_cache.get(build_number)
- if cached_build:
- return cached_build
-
- build = self._fetch_build(build_number)
- self._builds_cache[build_number] = build
- return build
-
- def force_build(self, username="webkit-patch", comments=None):
- def predicate(form):
- try:
- return form.find_control("username")
- except Exception, e:
- return False
- self._browser.open(self.url())
- self._browser.select_form(predicate=predicate)
- self._browser["username"] = username
- if comments:
- self._browser["comments"] = comments
- return self._browser.submit()
-
- file_name_regexp = re.compile(r"r(?P<revision>\d+) \((?P<build_number>\d+)\)")
- def _revision_and_build_for_filename(self, filename):
- # Example: "r47483 (1)/" or "r47483 (1).zip"
- match = self.file_name_regexp.match(filename)
- return (int(match.group("revision")), int(match.group("build_number")))
-
- def _fetch_revision_to_build_map(self):
- # All _fetch requests go through _buildbot for easier mocking
- # FIXME: This should use NetworkTransaction's 404 handling instead.
- try:
- # FIXME: This method is horribly slow due to the huge network load.
- # FIXME: This is a poor way to do revision -> build mapping.
- # Better would be to ask buildbot through some sort of API.
- print "Loading revision/build list from %s." % self.results_url()
- print "This may take a while..."
- result_files = self._buildbot._fetch_twisted_directory_listing(self.results_url())
- except urllib2.HTTPError, error:
- if error.code != 404:
- raise
- result_files = []
-
- # This assumes there was only one build per revision, which is false but we don't care for now.
- return dict([self._revision_and_build_for_filename(file_info["filename"]) for file_info in result_files])
-
- def _revision_to_build_map(self):
- if not self._revision_to_build_number:
- self._revision_to_build_number = self._fetch_revision_to_build_map()
- return self._revision_to_build_number
-
- def revision_build_pairs_with_results(self):
- return self._revision_to_build_map().items()
-
- # This assumes there can be only one build per revision, which is false, but we don't care for now.
- def build_for_revision(self, revision, allow_failed_lookups=False):
- # NOTE: This lookup will fail if that exact revision was never built.
- build_number = self._revision_to_build_map().get(int(revision))
- if not build_number:
- return None
- build = self.build(build_number)
- if not build and allow_failed_lookups:
- # Builds for old revisions with fail to lookup via buildbot's xmlrpc api.
- build = Build(self,
- build_number=build_number,
- revision=revision,
- is_green=False,
- )
- return build
-
- def find_regression_window(self, red_build, look_back_limit=30):
- if not red_build or red_build.is_green():
- return RegressionWindow(None, None)
- common_failures = None
- current_build = red_build
- build_after_current_build = None
- look_back_count = 0
- while current_build:
- if current_build.is_green():
- # current_build can't possibly have any failures in common
- # with red_build because it's green.
- break
- results = current_build.layout_test_results()
- # We treat a lack of results as if all the test failed.
- # This occurs, for example, when we can't compile at all.
- if results:
- failures = set(results.failing_tests())
- if common_failures == None:
- common_failures = failures
- else:
- common_failures = common_failures.intersection(failures)
- if not common_failures:
- # current_build doesn't have any failures in common with
- # the red build we're worried about. We assume that any
- # failures in current_build were due to flakiness.
- break
- look_back_count += 1
- if look_back_count > look_back_limit:
- return RegressionWindow(None, current_build, failing_tests=common_failures)
- build_after_current_build = current_build
- current_build = current_build.previous_build()
- # We must iterate at least once because red_build is red.
- assert(build_after_current_build)
- # Current build must either be green or have no failures in common
- # with red build, so we've found our failure transition.
- return RegressionWindow(current_build, build_after_current_build, failing_tests=common_failures)
-
- def find_blameworthy_regression_window(self, red_build_number, look_back_limit=30, avoid_flakey_tests=True):
- red_build = self.build(red_build_number)
- regression_window = self.find_regression_window(red_build, look_back_limit)
- if not regression_window.build_before_failure():
- return None # We ran off the limit of our search
- # If avoid_flakey_tests, require at least 2 bad builds before we
- # suspect a real failure transition.
- if avoid_flakey_tests and regression_window.failing_build() == red_build:
- return None
- return regression_window
-
-
-class Build(object):
- def __init__(self, builder, build_number, revision, is_green):
- self._builder = builder
- self._number = build_number
- self._revision = revision
- self._is_green = is_green
- self._layout_test_results = None
-
- @staticmethod
- def build_url(builder, build_number):
- return "%s/builds/%s" % (builder.url(), build_number)
-
- def url(self):
- return self.build_url(self.builder(), self._number)
-
- def results_url(self):
- results_directory = "r%s (%s)" % (self.revision(), self._number)
- return "%s/%s" % (self._builder.results_url(), urllib.quote(results_directory))
-
- def _fetch_results_html(self):
- results_html = "%s/results.html" % (self.results_url())
- # FIXME: This should use NetworkTransaction's 404 handling instead.
- try:
- # It seems this can return None if the url redirects and then returns 404.
- return urllib2.urlopen(results_html)
- except urllib2.HTTPError, error:
- if error.code != 404:
- raise
-
- def layout_test_results(self):
- if not self._layout_test_results:
- # FIXME: This should cache that the result was a 404 and stop hitting the network.
- self._layout_test_results = LayoutTestResults.results_from_string(self._fetch_results_html())
- return self._layout_test_results
-
- def builder(self):
- return self._builder
-
- def revision(self):
- return self._revision
-
- def is_green(self):
- return self._is_green
-
- def previous_build(self):
- # previous_build() allows callers to avoid assuming build numbers are sequential.
- # They may not be sequential across all master changes, or when non-trunk builds are made.
- return self._builder.build(self._number - 1)
-
-
-class BuildBot(object):
- # FIXME: This should move into some sort of webkit_config.py
- default_host = "build.webkit.org"
-
- def __init__(self, host=default_host):
- self.buildbot_host = host
- self._builder_by_name = {}
-
- # If any core builder is red we should not be landing patches. Other
- # builders should be added to this list once they are known to be
- # reliable.
- # See https://bugs.webkit.org/show_bug.cgi?id=33296 and related bugs.
- self.core_builder_names_regexps = [
- "SnowLeopard.*Build",
- "SnowLeopard.*\(Test", # Exclude WebKit2 for now.
- "Leopard",
- "Tiger",
- "Windows.*Build",
- "GTK.*32",
- "GTK.*64.*Debug", # Disallow the 64-bit Release bot which is broken.
- "Qt",
- "Chromium.*Release$",
- ]
-
- def _parse_last_build_cell(self, builder, cell):
- status_link = cell.find('a')
- if status_link:
- # Will be either a revision number or a build number
- revision_string = status_link.string
- # If revision_string has non-digits assume it's not a revision number.
- builder['built_revision'] = int(revision_string) \
- if not re.match('\D', revision_string) \
- else None
-
- # FIXME: We treat slave lost as green even though it is not to
- # work around the Qts bot being on a broken internet connection.
- # The real fix is https://bugs.webkit.org/show_bug.cgi?id=37099
- builder['is_green'] = not re.search('fail', cell.renderContents()) or \
- not not re.search('lost', cell.renderContents())
-
- status_link_regexp = r"builders/(?P<builder_name>.*)/builds/(?P<build_number>\d+)"
- link_match = re.match(status_link_regexp, status_link['href'])
- builder['build_number'] = int(link_match.group("build_number"))
- else:
- # We failed to find a link in the first cell, just give up. This
- # can happen if a builder is just-added, the first cell will just
- # be "no build"
- # Other parts of the code depend on is_green being present.
- builder['is_green'] = False
- builder['built_revision'] = None
- builder['build_number'] = None
-
- def _parse_current_build_cell(self, builder, cell):
- activity_lines = cell.renderContents().split("<br />")
- builder["activity"] = activity_lines[0] # normally "building" or "idle"
- # The middle lines document how long left for any current builds.
- match = re.match("(?P<pending_builds>\d) pending", activity_lines[-1])
- builder["pending_builds"] = int(match.group("pending_builds")) if match else 0
-
- def _parse_builder_status_from_row(self, status_row):
- status_cells = status_row.findAll('td')
- builder = {}
-
- # First cell is the name
- name_link = status_cells[0].find('a')
- builder["name"] = unicode(name_link.string)
-
- self._parse_last_build_cell(builder, status_cells[1])
- self._parse_current_build_cell(builder, status_cells[2])
- return builder
-
- def _matches_regexps(self, builder_name, name_regexps):
- for name_regexp in name_regexps:
- if re.match(name_regexp, builder_name):
- return True
- return False
-
- # FIXME: Should move onto Builder
- def _is_core_builder(self, builder_name):
- return self._matches_regexps(builder_name, self.core_builder_names_regexps)
-
- # FIXME: This method needs to die, but is used by a unit test at the moment.
- def _builder_statuses_with_names_matching_regexps(self, builder_statuses, name_regexps):
- return [builder for builder in builder_statuses if self._matches_regexps(builder["name"], name_regexps)]
-
- def red_core_builders(self):
- return [builder for builder in self.core_builder_statuses() if not builder["is_green"]]
-
- def red_core_builders_names(self):
- return [builder["name"] for builder in self.red_core_builders()]
-
- def idle_red_core_builders(self):
- return [builder for builder in self.red_core_builders() if builder["activity"] == "idle"]
-
- def core_builders_are_green(self):
- return not self.red_core_builders()
-
- # FIXME: These _fetch methods should move to a networking class.
- def _fetch_xmlrpc_build_dictionary(self, builder, build_number):
- # The buildbot XMLRPC API is super-limited.
- # For one, you cannot fetch info on builds which are incomplete.
- proxy = xmlrpclib.ServerProxy("http://%s/xmlrpc" % self.buildbot_host, allow_none=True)
- try:
- return proxy.getBuild(builder.name(), int(build_number))
- except xmlrpclib.Fault, err:
- build_url = Build.build_url(builder, build_number)
- _log.error("Error fetching data for %s build %s (%s): %s" % (builder.name(), build_number, build_url, err))
- return None
-
- def _fetch_one_box_per_builder(self):
- build_status_url = "http://%s/one_box_per_builder" % self.buildbot_host
- return urllib2.urlopen(build_status_url)
-
- def _file_cell_text(self, file_cell):
- """Traverses down through firstChild elements until one containing a string is found, then returns that string"""
- element = file_cell
- while element.string is None and element.contents:
- element = element.contents[0]
- return element.string
-
- def _parse_twisted_file_row(self, file_row):
- string_or_empty = lambda string: unicode(string) if string else u""
- file_cells = file_row.findAll('td')
- return {
- "filename": string_or_empty(self._file_cell_text(file_cells[0])),
- "size": string_or_empty(self._file_cell_text(file_cells[1])),
- "type": string_or_empty(self._file_cell_text(file_cells[2])),
- "encoding": string_or_empty(self._file_cell_text(file_cells[3])),
- }
-
- def _parse_twisted_directory_listing(self, page):
- soup = BeautifulSoup(page)
- # HACK: Match only table rows with a class to ignore twisted header/footer rows.
- file_rows = soup.find('table').findAll('tr', {'class': re.compile(r'\b(?:directory|file)\b')})
- return [self._parse_twisted_file_row(file_row) for file_row in file_rows]
-
- # FIXME: There should be a better way to get this information directly from twisted.
- def _fetch_twisted_directory_listing(self, url):
- return self._parse_twisted_directory_listing(urllib2.urlopen(url))
-
- def builders(self):
- return [self.builder_with_name(status["name"]) for status in self.builder_statuses()]
-
- # This method pulls from /one_box_per_builder as an efficient way to get information about
- def builder_statuses(self):
- soup = BeautifulSoup(self._fetch_one_box_per_builder())
- return [self._parse_builder_status_from_row(status_row) for status_row in soup.find('table').findAll('tr')]
-
- def core_builder_statuses(self):
- return [builder for builder in self.builder_statuses() if self._is_core_builder(builder["name"])]
-
- def builder_with_name(self, name):
- builder = self._builder_by_name.get(name)
- if not builder:
- builder = Builder(name, self)
- self._builder_by_name[name] = builder
- return builder
-
- def failure_map(self, only_core_builders=True):
- builder_statuses = self.core_builder_statuses() if only_core_builders else self.builder_statuses()
- failure_map = FailureMap()
- revision_to_failing_bots = {}
- for builder_status in builder_statuses:
- if builder_status["is_green"]:
- continue
- builder = self.builder_with_name(builder_status["name"])
- regression_window = builder.find_blameworthy_regression_window(builder_status["build_number"])
- if regression_window:
- failure_map.add_regression_window(builder, regression_window)
- return failure_map
-
- # This makes fewer requests than calling Builder.latest_build would. It grabs all builder
- # statuses in one request using self.builder_statuses (fetching /one_box_per_builder instead of builder pages).
- def _latest_builds_from_builders(self, only_core_builders=True):
- builder_statuses = self.core_builder_statuses() if only_core_builders else self.builder_statuses()
- return [self.builder_with_name(status["name"]).build(status["build_number"]) for status in builder_statuses]
-
- def _build_at_or_before_revision(self, build, revision):
- while build:
- if build.revision() <= revision:
- return build
- build = build.previous_build()
-
- def last_green_revision(self, only_core_builders=True):
- builds = self._latest_builds_from_builders(only_core_builders)
- target_revision = builds[0].revision()
- # An alternate way to do this would be to start at one revision and walk backwards
- # checking builder.build_for_revision, however build_for_revision is very slow on first load.
- while True:
- # Make builds agree on revision
- builds = [self._build_at_or_before_revision(build, target_revision) for build in builds]
- if None in builds: # One of the builds failed to load from the server.
- return None
- min_revision = min(map(lambda build: build.revision(), builds))
- if min_revision != target_revision:
- target_revision = min_revision
- continue # Builds don't all agree on revision, keep searching
- # Check to make sure they're all green
- all_are_green = reduce(operator.and_, map(lambda build: build.is_green(), builds))
- if not all_are_green:
- target_revision -= 1
- continue
- return min_revision
diff --git a/WebKitTools/Scripts/webkitpy/common/net/buildbot_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/buildbot_unittest.py
deleted file mode 100644
index 4fdf24c..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/buildbot_unittest.py
+++ /dev/null
@@ -1,410 +0,0 @@
-# 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
-# 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 unittest
-
-from webkitpy.common.net.buildbot import BuildBot, Builder, Build, LayoutTestResults
-from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup
-
-
-class BuilderTest(unittest.TestCase):
- def _install_fetch_build(self, failure):
- def _mock_fetch_build(build_number):
- build = Build(
- builder=self.builder,
- build_number=build_number,
- revision=build_number + 1000,
- is_green=build_number < 4
- )
- parsed_results = {LayoutTestResults.fail_key: failure(build_number)}
- build._layout_test_results = LayoutTestResults(parsed_results)
- return build
- self.builder._fetch_build = _mock_fetch_build
-
- def setUp(self):
- self.buildbot = BuildBot()
- self.builder = Builder(u"Test Builder \u2661", self.buildbot)
- self._install_fetch_build(lambda build_number: ["test1", "test2"])
-
- def test_find_regression_window(self):
- regression_window = self.builder.find_regression_window(self.builder.build(10))
- self.assertEqual(regression_window.build_before_failure().revision(), 1003)
- self.assertEqual(regression_window.failing_build().revision(), 1004)
-
- regression_window = self.builder.find_regression_window(self.builder.build(10), look_back_limit=2)
- self.assertEqual(regression_window.build_before_failure(), None)
- self.assertEqual(regression_window.failing_build().revision(), 1008)
-
- def test_none_build(self):
- self.builder._fetch_build = lambda build_number: None
- regression_window = self.builder.find_regression_window(self.builder.build(10))
- self.assertEqual(regression_window.build_before_failure(), None)
- self.assertEqual(regression_window.failing_build(), None)
-
- def test_flaky_tests(self):
- self._install_fetch_build(lambda build_number: ["test1"] if build_number % 2 else ["test2"])
- regression_window = self.builder.find_regression_window(self.builder.build(10))
- self.assertEqual(regression_window.build_before_failure().revision(), 1009)
- self.assertEqual(regression_window.failing_build().revision(), 1010)
-
- def test_failure_and_flaky(self):
- self._install_fetch_build(lambda build_number: ["test1", "test2"] if build_number % 2 else ["test2"])
- regression_window = self.builder.find_regression_window(self.builder.build(10))
- self.assertEqual(regression_window.build_before_failure().revision(), 1003)
- self.assertEqual(regression_window.failing_build().revision(), 1004)
-
- def test_no_results(self):
- self._install_fetch_build(lambda build_number: ["test1", "test2"] if build_number % 2 else ["test2"])
- regression_window = self.builder.find_regression_window(self.builder.build(10))
- self.assertEqual(regression_window.build_before_failure().revision(), 1003)
- self.assertEqual(regression_window.failing_build().revision(), 1004)
-
- def test_failure_after_flaky(self):
- self._install_fetch_build(lambda build_number: ["test1", "test2"] if build_number > 6 else ["test3"])
- regression_window = self.builder.find_regression_window(self.builder.build(10))
- self.assertEqual(regression_window.build_before_failure().revision(), 1006)
- self.assertEqual(regression_window.failing_build().revision(), 1007)
-
- def test_find_blameworthy_regression_window(self):
- self.assertEqual(self.builder.find_blameworthy_regression_window(10).revisions(), [1004])
- self.assertEqual(self.builder.find_blameworthy_regression_window(10, look_back_limit=2), None)
- # Flakey test avoidance requires at least 2 red builds:
- self.assertEqual(self.builder.find_blameworthy_regression_window(4), None)
- self.assertEqual(self.builder.find_blameworthy_regression_window(4, avoid_flakey_tests=False).revisions(), [1004])
- # Green builder:
- self.assertEqual(self.builder.find_blameworthy_regression_window(3), None)
-
- def test_build_caching(self):
- self.assertEqual(self.builder.build(10), self.builder.build(10))
-
- def test_build_and_revision_for_filename(self):
- expectations = {
- "r47483 (1)/" : (47483, 1),
- "r47483 (1).zip" : (47483, 1),
- }
- for filename, revision_and_build in expectations.items():
- self.assertEqual(self.builder._revision_and_build_for_filename(filename), revision_and_build)
-
-
-class BuildTest(unittest.TestCase):
- def test_layout_test_results(self):
- build = Build(None, None, None, None)
- build._fetch_results_html = lambda: None
- # Test that layout_test_results() returns None if the fetch fails.
- self.assertEqual(build.layout_test_results(), None)
-
-
-class BuildBotTest(unittest.TestCase):
-
- _example_one_box_status = '''
- <table>
- <tr>
- <td class="box"><a href="builders/Windows%20Debug%20%28Tests%29">Windows Debug (Tests)</a></td>
- <td align="center" class="LastBuild box success"><a href="builders/Windows%20Debug%20%28Tests%29/builds/3693">47380</a><br />build<br />successful</td>
- <td align="center" class="Activity building">building<br />ETA in<br />~ 14 mins<br />at 13:40</td>
- <tr>
- <td class="box"><a href="builders/SnowLeopard%20Intel%20Release">SnowLeopard Intel Release</a></td>
- <td class="LastBuild box" >no build</td>
- <td align="center" class="Activity building">building<br />< 1 min</td>
- <tr>
- <td class="box"><a href="builders/Qt%20Linux%20Release">Qt Linux Release</a></td>
- <td align="center" class="LastBuild box failure"><a href="builders/Qt%20Linux%20Release/builds/654">47383</a><br />failed<br />compile-webkit</td>
- <td align="center" class="Activity idle">idle<br />3 pending</td>
- <tr>
- <td class="box"><a href="builders/Qt%20Windows%2032-bit%20Debug">Qt Windows 32-bit Debug</a></td>
- <td align="center" class="LastBuild box failure"><a href="builders/Qt%20Windows%2032-bit%20Debug/builds/2090">60563</a><br />failed<br />failed<br />slave<br />lost</td>
- <td align="center" class="Activity building">building<br />ETA in<br />~ 5 mins<br />at 08:25</td>
- </table>
-'''
- _expected_example_one_box_parsings = [
- {
- 'is_green': True,
- 'build_number' : 3693,
- 'name': u'Windows Debug (Tests)',
- 'built_revision': 47380,
- 'activity': 'building',
- 'pending_builds': 0,
- },
- {
- 'is_green': False,
- 'build_number' : None,
- 'name': u'SnowLeopard Intel Release',
- 'built_revision': None,
- 'activity': 'building',
- 'pending_builds': 0,
- },
- {
- 'is_green': False,
- 'build_number' : 654,
- 'name': u'Qt Linux Release',
- 'built_revision': 47383,
- 'activity': 'idle',
- 'pending_builds': 3,
- },
- {
- 'is_green': True,
- 'build_number' : 2090,
- 'name': u'Qt Windows 32-bit Debug',
- 'built_revision': 60563,
- 'activity': 'building',
- 'pending_builds': 0,
- },
- ]
-
- def test_status_parsing(self):
- buildbot = BuildBot()
-
- soup = BeautifulSoup(self._example_one_box_status)
- status_table = soup.find("table")
- input_rows = status_table.findAll('tr')
-
- for x in range(len(input_rows)):
- status_row = input_rows[x]
- expected_parsing = self._expected_example_one_box_parsings[x]
-
- builder = buildbot._parse_builder_status_from_row(status_row)
-
- # Make sure we aren't parsing more or less than we expect
- self.assertEquals(builder.keys(), expected_parsing.keys())
-
- for key, expected_value in expected_parsing.items():
- self.assertEquals(builder[key], expected_value, ("Builder %d parse failure for key: %s: Actual='%s' Expected='%s'" % (x, key, builder[key], expected_value)))
-
- def test_core_builder_methods(self):
- buildbot = BuildBot()
-
- # Override builder_statuses function to not touch the network.
- def example_builder_statuses(): # We could use instancemethod() to bind 'self' but we don't need to.
- return BuildBotTest._expected_example_one_box_parsings
- buildbot.builder_statuses = example_builder_statuses
-
- buildbot.core_builder_names_regexps = [ 'Leopard', "Windows.*Build" ]
- self.assertEquals(buildbot.red_core_builders_names(), [])
- self.assertTrue(buildbot.core_builders_are_green())
-
- buildbot.core_builder_names_regexps = [ 'SnowLeopard', 'Qt' ]
- self.assertEquals(buildbot.red_core_builders_names(), [ u'SnowLeopard Intel Release', u'Qt Linux Release' ])
- self.assertFalse(buildbot.core_builders_are_green())
-
- def test_builder_name_regexps(self):
- buildbot = BuildBot()
-
- # For complete testing, this list should match the list of builders at build.webkit.org:
- example_builders = [
- {'name': u'Tiger Intel Release', },
- {'name': u'Leopard Intel Release (Build)', },
- {'name': u'Leopard Intel Release (Tests)', },
- {'name': u'Leopard Intel Debug (Build)', },
- {'name': u'Leopard Intel Debug (Tests)', },
- {'name': u'SnowLeopard Intel Release (Build)', },
- {'name': u'SnowLeopard Intel Release (Tests)', },
- {'name': u'SnowLeopard Intel Release (WebKit2 Tests)', },
- {'name': u'SnowLeopard Intel Leaks', },
- {'name': u'Windows Release (Build)', },
- {'name': u'Windows Release (Tests)', },
- {'name': u'Windows Debug (Build)', },
- {'name': u'Windows Debug (Tests)', },
- {'name': u'GTK Linux 32-bit Release', },
- {'name': u'GTK Linux 32-bit Debug', },
- {'name': u'GTK Linux 64-bit Debug', },
- {'name': u'GTK Linux 64-bit Release', },
- {'name': u'Qt Linux Release', },
- {'name': u'Qt Linux Release minimal', },
- {'name': u'Qt Linux ARMv5 Release', },
- {'name': u'Qt Linux ARMv7 Release', },
- {'name': u'Qt Windows 32-bit Release', },
- {'name': u'Qt Windows 32-bit Debug', },
- {'name': u'Chromium Linux Release', },
- {'name': u'Chromium Mac Release', },
- {'name': u'Chromium Win Release', },
- {'name': u'Chromium Linux Release (Tests)', },
- {'name': u'Chromium Mac Release (Tests)', },
- {'name': u'Chromium Win Release (Tests)', },
- {'name': u'New run-webkit-tests', },
- ]
- name_regexps = [
- "SnowLeopard.*Build",
- "SnowLeopard.*\(Test",
- "Leopard",
- "Tiger",
- "Windows.*Build",
- "GTK.*32",
- "GTK.*64.*Debug", # Disallow the 64-bit Release bot which is broken.
- "Qt",
- "Chromium.*Release$",
- ]
- expected_builders = [
- {'name': u'Tiger Intel Release', },
- {'name': u'Leopard Intel Release (Build)', },
- {'name': u'Leopard Intel Release (Tests)', },
- {'name': u'Leopard Intel Debug (Build)', },
- {'name': u'Leopard Intel Debug (Tests)', },
- {'name': u'SnowLeopard Intel Release (Build)', },
- {'name': u'SnowLeopard Intel Release (Tests)', },
- {'name': u'Windows Release (Build)', },
- {'name': u'Windows Debug (Build)', },
- {'name': u'GTK Linux 32-bit Release', },
- {'name': u'GTK Linux 32-bit Debug', },
- {'name': u'GTK Linux 64-bit Debug', },
- {'name': u'Qt Linux Release', },
- {'name': u'Qt Linux Release minimal', },
- {'name': u'Qt Linux ARMv5 Release', },
- {'name': u'Qt Linux ARMv7 Release', },
- {'name': u'Qt Windows 32-bit Release', },
- {'name': u'Qt Windows 32-bit Debug', },
- {'name': u'Chromium Linux Release', },
- {'name': u'Chromium Mac Release', },
- {'name': u'Chromium Win Release', },
- ]
-
- # This test should probably be updated if the default regexp list changes
- self.assertEquals(buildbot.core_builder_names_regexps, name_regexps)
-
- builders = buildbot._builder_statuses_with_names_matching_regexps(example_builders, name_regexps)
- self.assertEquals(builders, expected_builders)
-
- def test_builder_with_name(self):
- buildbot = BuildBot()
-
- builder = buildbot.builder_with_name("Test Builder")
- self.assertEqual(builder.name(), "Test Builder")
- self.assertEqual(builder.url(), "http://build.webkit.org/builders/Test%20Builder")
- self.assertEqual(builder.url_encoded_name(), "Test%20Builder")
- self.assertEqual(builder.results_url(), "http://build.webkit.org/results/Test%20Builder")
-
- # Override _fetch_xmlrpc_build_dictionary function to not touch the network.
- def mock_fetch_xmlrpc_build_dictionary(self, build_number):
- build_dictionary = {
- "revision" : 2 * build_number,
- "number" : int(build_number),
- "results" : build_number % 2, # 0 means pass
- }
- return build_dictionary
- buildbot._fetch_xmlrpc_build_dictionary = mock_fetch_xmlrpc_build_dictionary
-
- build = builder.build(10)
- self.assertEqual(build.builder(), builder)
- self.assertEqual(build.url(), "http://build.webkit.org/builders/Test%20Builder/builds/10")
- self.assertEqual(build.results_url(), "http://build.webkit.org/results/Test%20Builder/r20%20%2810%29")
- self.assertEqual(build.revision(), 20)
- self.assertEqual(build.is_green(), True)
-
- build = build.previous_build()
- self.assertEqual(build.builder(), builder)
- self.assertEqual(build.url(), "http://build.webkit.org/builders/Test%20Builder/builds/9")
- self.assertEqual(build.results_url(), "http://build.webkit.org/results/Test%20Builder/r18%20%289%29")
- self.assertEqual(build.revision(), 18)
- self.assertEqual(build.is_green(), False)
-
- self.assertEqual(builder.build(None), None)
-
- _example_directory_listing = '''
-<h1>Directory listing for /results/SnowLeopard Intel Leaks/</h1>
-
-<table>
- <tr class="alt">
- <th>Filename</th>
- <th>Size</th>
- <th>Content type</th>
- <th>Content encoding</th>
- </tr>
-<tr class="directory ">
- <td><a href="r47483%20%281%29/"><b>r47483 (1)/</b></a></td>
- <td><b></b></td>
- <td><b>[Directory]</b></td>
- <td><b></b></td>
-</tr>
-<tr class="file alt">
- <td><a href="r47484%20%282%29.zip">r47484 (2).zip</a></td>
- <td>89K</td>
- <td>[application/zip]</td>
- <td></td>
-</tr>
-'''
- _expected_files = [
- {
- "filename" : "r47483 (1)/",
- "size" : "",
- "type" : "[Directory]",
- "encoding" : "",
- },
- {
- "filename" : "r47484 (2).zip",
- "size" : "89K",
- "type" : "[application/zip]",
- "encoding" : "",
- },
- ]
-
- def test_parse_build_to_revision_map(self):
- buildbot = BuildBot()
- files = buildbot._parse_twisted_directory_listing(self._example_directory_listing)
- self.assertEqual(self._expected_files, files)
-
- # Revision, is_green
- # Ordered from newest (highest number) to oldest.
- fake_builder1 = [
- [2, False],
- [1, True],
- ]
- fake_builder2 = [
- [2, False],
- [1, True],
- ]
- fake_builders = [
- fake_builder1,
- fake_builder2,
- ]
- def _build_from_fake(self, fake_builder, index):
- if index >= len(fake_builder):
- return None
- fake_build = fake_builder[index]
- build = Build(
- builder=fake_builder,
- build_number=index,
- revision=fake_build[0],
- is_green=fake_build[1],
- )
- def mock_previous_build():
- return self._build_from_fake(fake_builder, index + 1)
- build.previous_build = mock_previous_build
- return build
-
- def _fake_builds_at_index(self, index):
- return [self._build_from_fake(builder, index) for builder in self.fake_builders]
-
- def test_last_green_revision(self):
- buildbot = BuildBot()
- def mock_builds_from_builders(only_core_builders):
- return self._fake_builds_at_index(0)
- buildbot._latest_builds_from_builders = mock_builds_from_builders
- self.assertEqual(buildbot.last_green_revision(), 1)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/net/credentials.py b/WebKitTools/Scripts/webkitpy/common/net/credentials.py
deleted file mode 100644
index 30480b3..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/credentials.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple 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.
-#
-# Python module for reading stored web credentials from the OS.
-
-import getpass
-import os
-import platform
-import re
-
-from webkitpy.common.checkout.scm import Git
-from webkitpy.common.system.executive import Executive, ScriptError
-from webkitpy.common.system.user import User
-from webkitpy.common.system.deprecated_logging import log
-
-try:
- # Use keyring, a cross platform keyring interface, as a fallback:
- # http://pypi.python.org/pypi/keyring
- import keyring
-except ImportError:
- keyring = None
-
-
-class Credentials(object):
- _environ_prefix = "webkit_bugzilla_"
-
- def __init__(self, host, git_prefix=None, executive=None, cwd=os.getcwd(),
- keyring=keyring):
- self.host = host
- self.git_prefix = "%s." % git_prefix if git_prefix else ""
- self.executive = executive or Executive()
- self.cwd = cwd
- self._keyring = keyring
-
- def _credentials_from_git(self):
- try:
- if not Git.in_working_directory(self.cwd):
- return (None, None)
- return (Git.read_git_config(self.git_prefix + "username"),
- Git.read_git_config(self.git_prefix + "password"))
- except OSError, e:
- # Catch and ignore OSError exceptions such as "no such file
- # or directory" (OSError errno 2), which imply that the Git
- # command cannot be found/is not installed.
- pass
- return (None, None)
-
- def _keychain_value_with_label(self, label, source_text):
- match = re.search("%s\"(?P<value>.+)\"" % label,
- source_text,
- re.MULTILINE)
- if match:
- return match.group('value')
-
- def _is_mac_os_x(self):
- return platform.mac_ver()[0]
-
- def _parse_security_tool_output(self, security_output):
- username = self._keychain_value_with_label("^\s*\"acct\"<blob>=",
- security_output)
- password = self._keychain_value_with_label("^password: ",
- security_output)
- return [username, password]
-
- def _run_security_tool(self, username=None):
- security_command = [
- "/usr/bin/security",
- "find-internet-password",
- "-g",
- "-s",
- self.host,
- ]
- if username:
- security_command += ["-a", username]
-
- log("Reading Keychain for %s account and password. "
- "Click \"Allow\" to continue..." % self.host)
- try:
- return self.executive.run_command(security_command)
- except ScriptError:
- # Failed to either find a keychain entry or somekind of OS-related
- # error occured (for instance, couldn't find the /usr/sbin/security
- # command).
- log("Could not find a keychain entry for %s." % self.host)
- return None
-
- def _credentials_from_keychain(self, username=None):
- if not self._is_mac_os_x():
- return [username, None]
-
- security_output = self._run_security_tool(username)
- if security_output:
- return self._parse_security_tool_output(security_output)
- else:
- return [None, None]
-
- def _read_environ(self, key):
- environ_key = self._environ_prefix + key
- return os.environ.get(environ_key.upper())
-
- def _credentials_from_environment(self):
- return (self._read_environ("username"), self._read_environ("password"))
-
- def _offer_to_store_credentials_in_keyring(self, username, password):
- if not self._keyring:
- return
- if not User().confirm("Store password in system keyring?", User.DEFAULT_NO):
- return
- self._keyring.set_password(self.host, username, password)
-
- def read_credentials(self):
- username, password = self._credentials_from_environment()
- # FIXME: We don't currently support pulling the username from one
- # source and the password from a separate source.
- if not username or not password:
- username, password = self._credentials_from_git()
- if not username or not password:
- username, password = self._credentials_from_keychain(username)
-
- if username and not password and self._keyring:
- password = self._keyring.get_password(self.host, username)
-
- if not username:
- username = User.prompt("%s login: " % self.host)
- if not password:
- password = getpass.getpass("%s password for %s: " % (self.host, username))
- self._offer_to_store_credentials_in_keyring(username, password)
-
- return (username, password)
diff --git a/WebKitTools/Scripts/webkitpy/common/net/credentials_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/credentials_unittest.py
deleted file mode 100644
index 6f2d909..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/credentials_unittest.py
+++ /dev/null
@@ -1,176 +0,0 @@
-# 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
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import with_statement
-
-import os
-import tempfile
-import unittest
-from webkitpy.common.net.credentials import Credentials
-from webkitpy.common.system.executive import Executive
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.thirdparty.mock import Mock
-
-
-# FIXME: Other unit tests probably want this class.
-class _TemporaryDirectory(object):
- def __init__(self, **kwargs):
- self._kwargs = kwargs
- self._directory_path = None
-
- def __enter__(self):
- self._directory_path = tempfile.mkdtemp(**self._kwargs)
- return self._directory_path
-
- def __exit__(self, type, value, traceback):
- os.rmdir(self._directory_path)
-
-
-class CredentialsTest(unittest.TestCase):
- example_security_output = """keychain: "/Users/test/Library/Keychains/login.keychain"
-class: "inet"
-attributes:
- 0x00000007 <blob>="bugs.webkit.org (test@webkit.org)"
- 0x00000008 <blob>=<NULL>
- "acct"<blob>="test@webkit.org"
- "atyp"<blob>="form"
- "cdat"<timedate>=0x32303039303832353233353231365A00 "20090825235216Z\000"
- "crtr"<uint32>=<NULL>
- "cusi"<sint32>=<NULL>
- "desc"<blob>="Web form password"
- "icmt"<blob>="default"
- "invi"<sint32>=<NULL>
- "mdat"<timedate>=0x32303039303930393137323635315A00 "20090909172651Z\000"
- "nega"<sint32>=<NULL>
- "path"<blob>=<NULL>
- "port"<uint32>=0x00000000
- "prot"<blob>=<NULL>
- "ptcl"<uint32>="htps"
- "scrp"<sint32>=<NULL>
- "sdmn"<blob>=<NULL>
- "srvr"<blob>="bugs.webkit.org"
- "type"<uint32>=<NULL>
-password: "SECRETSAUCE"
-"""
-
- def test_keychain_lookup_on_non_mac(self):
- class FakeCredentials(Credentials):
- def _is_mac_os_x(self):
- return False
- credentials = FakeCredentials("bugs.webkit.org")
- self.assertEqual(credentials._is_mac_os_x(), False)
- self.assertEqual(credentials._credentials_from_keychain("foo"), ["foo", None])
-
- def test_security_output_parse(self):
- credentials = Credentials("bugs.webkit.org")
- self.assertEqual(credentials._parse_security_tool_output(self.example_security_output), ["test@webkit.org", "SECRETSAUCE"])
-
- def test_security_output_parse_entry_not_found(self):
- credentials = Credentials("foo.example.com")
- if not credentials._is_mac_os_x():
- return # This test does not run on a non-Mac.
-
- # Note, we ignore the captured output because it is already covered
- # by the test case CredentialsTest._assert_security_call (below).
- outputCapture = OutputCapture()
- outputCapture.capture_output()
- self.assertEqual(credentials._run_security_tool(), None)
- outputCapture.restore_output()
-
- def _assert_security_call(self, username=None):
- executive_mock = Mock()
- credentials = Credentials("example.com", executive=executive_mock)
-
- expected_stderr = "Reading Keychain for example.com account and password. Click \"Allow\" to continue...\n"
- OutputCapture().assert_outputs(self, credentials._run_security_tool, [username], expected_stderr=expected_stderr)
-
- security_args = ["/usr/bin/security", "find-internet-password", "-g", "-s", "example.com"]
- if username:
- security_args += ["-a", username]
- executive_mock.run_command.assert_called_with(security_args)
-
- def test_security_calls(self):
- self._assert_security_call()
- self._assert_security_call(username="foo")
-
- def test_credentials_from_environment(self):
- executive_mock = Mock()
- credentials = Credentials("example.com", executive=executive_mock)
-
- saved_environ = os.environ.copy()
- os.environ['WEBKIT_BUGZILLA_USERNAME'] = "foo"
- os.environ['WEBKIT_BUGZILLA_PASSWORD'] = "bar"
- username, password = credentials._credentials_from_environment()
- self.assertEquals(username, "foo")
- self.assertEquals(password, "bar")
- os.environ = saved_environ
-
- def test_read_credentials_without_git_repo(self):
- # FIXME: This should share more code with test_keyring_without_git_repo
- class FakeCredentials(Credentials):
- def _is_mac_os_x(self):
- return True
-
- def _credentials_from_keychain(self, username):
- return ("test@webkit.org", "SECRETSAUCE")
-
- def _credentials_from_environment(self):
- return (None, None)
-
- with _TemporaryDirectory(suffix="not_a_git_repo") as temp_dir_path:
- credentials = FakeCredentials("bugs.webkit.org", cwd=temp_dir_path)
- # FIXME: Using read_credentials here seems too broad as higher-priority
- # credential source could be affected by the user's environment.
- self.assertEqual(credentials.read_credentials(), ("test@webkit.org", "SECRETSAUCE"))
-
-
- def test_keyring_without_git_repo(self):
- # FIXME: This should share more code with test_read_credentials_without_git_repo
- class MockKeyring(object):
- def get_password(self, host, username):
- return "NOMNOMNOM"
-
- class FakeCredentials(Credentials):
- def _is_mac_os_x(self):
- return True
-
- def _credentials_from_keychain(self, username):
- return ("test@webkit.org", None)
-
- def _credentials_from_environment(self):
- return (None, None)
-
- with _TemporaryDirectory(suffix="not_a_git_repo") as temp_dir_path:
- credentials = FakeCredentials("fake.hostname", cwd=temp_dir_path, keyring=MockKeyring())
- # FIXME: Using read_credentials here seems too broad as higher-priority
- # credential source could be affected by the user's environment.
- self.assertEqual(credentials.read_credentials(), ("test@webkit.org", "NOMNOMNOM"))
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/net/failuremap.py b/WebKitTools/Scripts/webkitpy/common/net/failuremap.py
deleted file mode 100644
index e2d53ae..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/failuremap.py
+++ /dev/null
@@ -1,84 +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.
-
-
-class FailureMap(object):
- def __init__(self):
- self._failures = []
-
- def add_regression_window(self, builder, regression_window):
- self._failures.append({
- 'builder': builder,
- 'regression_window': regression_window,
- })
-
- def is_empty(self):
- return not self._failures
-
- def failing_revisions(self):
- failing_revisions = [failure_info['regression_window'].revisions()
- for failure_info in self._failures]
- return sorted(set(sum(failing_revisions, [])))
-
- def builders_failing_for(self, revision):
- return self._builders_failing_because_of([revision])
-
- def tests_failing_for(self, revision):
- tests = [failure_info['regression_window'].failing_tests()
- for failure_info in self._failures
- if revision in failure_info['regression_window'].revisions()
- and failure_info['regression_window'].failing_tests()]
- result = set()
- for test in tests:
- result = result.union(test)
- return sorted(result)
-
- def _old_failures(self, is_old_failure):
- return filter(lambda revision: is_old_failure(revision),
- self.failing_revisions())
-
- def _builders_failing_because_of(self, revisions):
- revision_set = set(revisions)
- return [failure_info['builder'] for failure_info in self._failures
- if revision_set.intersection(
- failure_info['regression_window'].revisions())]
-
- # FIXME: We should re-process old failures after some time delay.
- # https://bugs.webkit.org/show_bug.cgi?id=36581
- def filter_out_old_failures(self, is_old_failure):
- old_failures = self._old_failures(is_old_failure)
- old_failing_builder_names = set([builder.name()
- for builder in self._builders_failing_because_of(old_failures)])
-
- # We filter out all the failing builders that could have been caused
- # by old_failures. We could miss some new failures this way, but
- # emperically, this reduces the amount of spam we generate.
- failures = self._failures
- self._failures = [failure_info for failure_info in failures
- if failure_info['builder'].name() not in old_failing_builder_names]
- self._cache = {}
diff --git a/WebKitTools/Scripts/webkitpy/common/net/failuremap_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/failuremap_unittest.py
deleted file mode 100644
index 2f0b49d..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/failuremap_unittest.py
+++ /dev/null
@@ -1,76 +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.
-
-import unittest
-
-from webkitpy.common.net.buildbot import Build
-from webkitpy.common.net.failuremap import *
-from webkitpy.common.net.regressionwindow import RegressionWindow
-from webkitpy.tool.mocktool import MockBuilder
-
-
-class FailureMapTest(unittest.TestCase):
- builder1 = MockBuilder("Builder1")
- builder2 = MockBuilder("Builder2")
-
- build1a = Build(builder1, build_number=22, revision=1233, is_green=True)
- build1b = Build(builder1, build_number=23, revision=1234, is_green=False)
- build2a = Build(builder2, build_number=89, revision=1233, is_green=True)
- build2b = Build(builder2, build_number=90, revision=1235, is_green=False)
-
- regression_window1 = RegressionWindow(build1a, build1b, failing_tests=[u'test1', u'test1'])
- regression_window2 = RegressionWindow(build2a, build2b, failing_tests=[u'test1'])
-
- def _make_failure_map(self):
- failure_map = FailureMap()
- failure_map.add_regression_window(self.builder1, self.regression_window1)
- failure_map.add_regression_window(self.builder2, self.regression_window2)
- return failure_map
-
- def test_failing_revisions(self):
- failure_map = self._make_failure_map()
- self.assertEquals(failure_map.failing_revisions(), [1234, 1235])
-
- def test_new_failures(self):
- failure_map = self._make_failure_map()
- failure_map.filter_out_old_failures(lambda revision: False)
- self.assertEquals(failure_map.failing_revisions(), [1234, 1235])
-
- def test_new_failures_with_old_revisions(self):
- failure_map = self._make_failure_map()
- failure_map.filter_out_old_failures(lambda revision: revision == 1234)
- self.assertEquals(failure_map.failing_revisions(), [])
-
- def test_new_failures_with_more_old_revisions(self):
- failure_map = self._make_failure_map()
- failure_map.filter_out_old_failures(lambda revision: revision == 1235)
- self.assertEquals(failure_map.failing_revisions(), [1234])
-
- def test_tests_failing_for(self):
- failure_map = self._make_failure_map()
- self.assertEquals(failure_map.tests_failing_for(1234), [u'test1'])
diff --git a/WebKitTools/Scripts/webkitpy/common/net/irc/__init__.py b/WebKitTools/Scripts/webkitpy/common/net/irc/__init__.py
deleted file mode 100644
index ef65bee..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/irc/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/common/net/irc/ircbot.py b/WebKitTools/Scripts/webkitpy/common/net/irc/ircbot.py
deleted file mode 100644
index f742867..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/irc/ircbot.py
+++ /dev/null
@@ -1,91 +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.
-
-import webkitpy.common.config.irc as config_irc
-
-from webkitpy.common.thread.messagepump import MessagePump, MessagePumpDelegate
-from webkitpy.thirdparty.autoinstalled.irc import ircbot
-from webkitpy.thirdparty.autoinstalled.irc import irclib
-
-
-class IRCBotDelegate(object):
- def irc_message_received(self, nick, message):
- raise NotImplementedError, "subclasses must implement"
-
- def irc_nickname(self):
- raise NotImplementedError, "subclasses must implement"
-
- def irc_password(self):
- raise NotImplementedError, "subclasses must implement"
-
-
-class IRCBot(ircbot.SingleServerIRCBot, MessagePumpDelegate):
- # FIXME: We should get this information from a config file.
- def __init__(self,
- message_queue,
- delegate):
- self._message_queue = message_queue
- self._delegate = delegate
- ircbot.SingleServerIRCBot.__init__(
- self,
- [(
- config_irc.server,
- config_irc.port,
- self._delegate.irc_password()
- )],
- self._delegate.irc_nickname(),
- self._delegate.irc_nickname())
- self._channel = config_irc.channel
-
- # ircbot.SingleServerIRCBot methods
-
- def on_nicknameinuse(self, connection, event):
- connection.nick(connection.get_nickname() + "_")
-
- def on_welcome(self, connection, event):
- connection.join(self._channel)
- self._message_pump = MessagePump(self, self._message_queue)
-
- def on_pubmsg(self, connection, event):
- nick = irclib.nm_to_n(event.source())
- request = event.arguments()[0].split(":", 1)
- if len(request) > 1 and irclib.irc_lower(request[0]) == irclib.irc_lower(self.connection.get_nickname()):
- response = self._delegate.irc_message_received(nick, request[1])
- if response:
- connection.privmsg(self._channel, response)
-
- # MessagePumpDelegate methods
-
- def schedule(self, interval, callback):
- self.connection.execute_delayed(interval, callback)
-
- def message_available(self, message):
- self.connection.privmsg(self._channel, message)
-
- def final_message_delivered(self):
- self.die()
diff --git a/WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy.py b/WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy.py
deleted file mode 100644
index 13348b4..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy.py
+++ /dev/null
@@ -1,62 +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.
-
-import threading
-
-from webkitpy.common.net.irc.ircbot import IRCBot
-from webkitpy.common.thread.threadedmessagequeue import ThreadedMessageQueue
-from webkitpy.common.system.deprecated_logging import log
-
-
-class _IRCThread(threading.Thread):
- def __init__(self, message_queue, irc_delegate, irc_bot):
- threading.Thread.__init__(self)
- self.setDaemon(True)
- self._message_queue = message_queue
- self._irc_delegate = irc_delegate
- self._irc_bot = irc_bot
-
- def run(self):
- bot = self._irc_bot(self._message_queue, self._irc_delegate)
- bot.start()
-
-
-class IRCProxy(object):
- def __init__(self, irc_delegate, irc_bot=IRCBot):
- log("Connecting to IRC")
- self._message_queue = ThreadedMessageQueue()
- self._child_thread = _IRCThread(self._message_queue, irc_delegate, irc_bot)
- self._child_thread.start()
-
- def post(self, message):
- self._message_queue.post(message)
-
- def disconnect(self):
- log("Disconnecting from IRC...")
- self._message_queue.stop()
- self._child_thread.join()
diff --git a/WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy_unittest.py
deleted file mode 100644
index b44ce40..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/irc/ircproxy_unittest.py
+++ /dev/null
@@ -1,43 +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.
-
-import unittest
-
-from webkitpy.common.net.irc.ircproxy import IRCProxy
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.thirdparty.mock import Mock
-
-class IRCProxyTest(unittest.TestCase):
- def test_trivial(self):
- def fun():
- proxy = IRCProxy(Mock(), Mock())
- proxy.post("hello")
- proxy.disconnect()
-
- expected_stderr = "Connecting to IRC\nDisconnecting from IRC...\n"
- OutputCapture().assert_outputs(self, fun, expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/common/net/layouttestresults.py b/WebKitTools/Scripts/webkitpy/common/net/layouttestresults.py
deleted file mode 100644
index a7b3b0a..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/layouttestresults.py
+++ /dev/null
@@ -1,90 +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.
-#
-# A module for parsing results.html files generated by old-run-webkit-tests
-
-from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup, SoupStrainer
-
-
-# FIXME: This should be unified with all the layout test results code in the layout_tests package
-# This doesn't belong in common.net, but we don't have a better place for it yet.
-def path_for_layout_test(test_name):
- return "LayoutTests/%s" % test_name
-
-
-# FIXME: This should be unified with all the layout test results code in the layout_tests package
-# This doesn't belong in common.net, but we don't have a better place for it yet.
-class LayoutTestResults(object):
- """This class knows how to parse old-run-webkit-tests results.html files."""
-
- stderr_key = u'Tests that had stderr output:'
- fail_key = u'Tests where results did not match expected results:'
- timeout_key = u'Tests that timed out:'
- crash_key = u'Tests that caused the DumpRenderTree tool to crash:'
- missing_key = u'Tests that had no expected results (probably new):'
-
- expected_keys = [
- stderr_key,
- fail_key,
- crash_key,
- timeout_key,
- missing_key,
- ]
-
- @classmethod
- def _parse_results_html(cls, page):
- if not page:
- return None
- parsed_results = {}
- tables = BeautifulSoup(page).findAll("table")
- for table in tables:
- table_title = unicode(table.findPreviousSibling("p").string)
- if table_title not in cls.expected_keys:
- # This Exception should only ever be hit if run-webkit-tests changes its results.html format.
- raise Exception("Unhandled title: %s" % table_title)
- # We might want to translate table titles into identifiers before storing.
- parsed_results[table_title] = [unicode(row.find("a").string) for row in table.findAll("tr")]
-
- return parsed_results
-
- @classmethod
- def results_from_string(cls, string):
- parsed_results = cls._parse_results_html(string)
- if not parsed_results:
- return None
- return cls(parsed_results)
-
- def __init__(self, parsed_results):
- self._parsed_results = parsed_results
-
- def parsed_results(self):
- return self._parsed_results
-
- def failing_tests(self):
- failing_keys = [self.fail_key, self.crash_key, self.timeout_key]
- return sorted(sum([tests for key, tests in self._parsed_results.items() if key in failing_keys], []))
diff --git a/WebKitTools/Scripts/webkitpy/common/net/layouttestresults_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/layouttestresults_unittest.py
deleted file mode 100644
index 8490eae..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/layouttestresults_unittest.py
+++ /dev/null
@@ -1,77 +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.
-
-import unittest
-
-from webkitpy.common.net.layouttestresults import LayoutTestResults
-
-
-class LayoutTestResultsTest(unittest.TestCase):
- _example_results_html = """
-<html>
-<head>
-<title>Layout Test Results</title>
-</head>
-<body>
-<p>Tests that had stderr output:</p>
-<table>
-<tr>
-<td><a href="/var/lib/buildbot/build/gtk-linux-64-release/build/LayoutTests/accessibility/aria-activedescendant-crash.html">accessibility/aria-activedescendant-crash.html</a></td>
-<td><a href="accessibility/aria-activedescendant-crash-stderr.txt">stderr</a></td>
-</tr>
-<td><a href="/var/lib/buildbot/build/gtk-linux-64-release/build/LayoutTests/http/tests/security/canvas-remote-read-svg-image.html">http/tests/security/canvas-remote-read-svg-image.html</a></td>
-<td><a href="http/tests/security/canvas-remote-read-svg-image-stderr.txt">stderr</a></td>
-</tr>
-</table><p>Tests that had no expected results (probably new):</p>
-<table>
-<tr>
-<td><a href="/var/lib/buildbot/build/gtk-linux-64-release/build/LayoutTests/fast/repaint/no-caret-repaint-in-non-content-editable-element.html">fast/repaint/no-caret-repaint-in-non-content-editable-element.html</a></td>
-<td><a href="fast/repaint/no-caret-repaint-in-non-content-editable-element-actual.txt">result</a></td>
-</tr>
-</table></body>
-</html>
-"""
-
- _expected_layout_test_results = {
- 'Tests that had stderr output:': [
- 'accessibility/aria-activedescendant-crash.html',
- ],
- 'Tests that had no expected results (probably new):': [
- 'fast/repaint/no-caret-repaint-in-non-content-editable-element.html',
- ],
- }
-
- def test_parse_layout_test_results(self):
- results = LayoutTestResults._parse_results_html(self._example_results_html)
- self.assertEqual(self._expected_layout_test_results, results)
-
- def test_results_from_string(self):
- self.assertEqual(LayoutTestResults.results_from_string(None), None)
- self.assertEqual(LayoutTestResults.results_from_string(""), None)
- results = LayoutTestResults.results_from_string(self._example_results_html)
- self.assertEqual(len(results.failing_tests()), 0)
diff --git a/WebKitTools/Scripts/webkitpy/common/net/networktransaction.py b/WebKitTools/Scripts/webkitpy/common/net/networktransaction.py
deleted file mode 100644
index de19e94..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/networktransaction.py
+++ /dev/null
@@ -1,72 +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.
-
-import logging
-import time
-
-from webkitpy.thirdparty.autoinstalled import mechanize
-from webkitpy.common.system.deprecated_logging import log
-
-
-_log = logging.getLogger(__name__)
-
-
-class NetworkTimeout(Exception):
- pass
-
-
-class NetworkTransaction(object):
- def __init__(self, initial_backoff_seconds=10, grown_factor=1.5, timeout_seconds=(10 * 60), convert_404_to_None=False):
- self._initial_backoff_seconds = initial_backoff_seconds
- self._grown_factor = grown_factor
- self._timeout_seconds = timeout_seconds
- self._convert_404_to_None = convert_404_to_None
-
- def run(self, request):
- self._total_sleep = 0
- self._backoff_seconds = self._initial_backoff_seconds
- while True:
- try:
- return request()
- # FIXME: We should catch urllib2.HTTPError here too.
- except mechanize.HTTPError, e:
- if self._convert_404_to_None and e.code == 404:
- return None
- self._check_for_timeout()
- _log.warn("Received HTTP status %s from server. Retrying in "
- "%s seconds..." % (e.code, self._backoff_seconds))
- self._sleep()
-
- def _check_for_timeout(self):
- if self._total_sleep + self._backoff_seconds > self._timeout_seconds:
- raise NetworkTimeout()
-
- def _sleep(self):
- time.sleep(self._backoff_seconds)
- self._total_sleep += self._backoff_seconds
- self._backoff_seconds *= self._grown_factor
diff --git a/WebKitTools/Scripts/webkitpy/common/net/networktransaction_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/networktransaction_unittest.py
deleted file mode 100644
index 49aaeed..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/networktransaction_unittest.py
+++ /dev/null
@@ -1,93 +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.
-
-import unittest
-
-from webkitpy.common.net.networktransaction import NetworkTransaction, NetworkTimeout
-from webkitpy.common.system.logtesting import LoggingTestCase
-from webkitpy.thirdparty.autoinstalled.mechanize import HTTPError
-
-
-class NetworkTransactionTest(LoggingTestCase):
- exception = Exception("Test exception")
-
- def test_success(self):
- transaction = NetworkTransaction()
- self.assertEqual(transaction.run(lambda: 42), 42)
-
- def _raise_exception(self):
- raise self.exception
-
- def test_exception(self):
- transaction = NetworkTransaction()
- did_process_exception = False
- did_throw_exception = True
- try:
- transaction.run(lambda: self._raise_exception())
- did_throw_exception = False
- except Exception, e:
- did_process_exception = True
- self.assertEqual(e, self.exception)
- self.assertTrue(did_throw_exception)
- self.assertTrue(did_process_exception)
-
- def _raise_500_error(self):
- self._run_count += 1
- if self._run_count < 3:
- raise HTTPError("http://example.com/", 500, "internal server error", None, None)
- return 42
-
- def _raise_404_error(self):
- raise HTTPError("http://foo.com/", 404, "not found", None, None)
-
- def test_retry(self):
- self._run_count = 0
- transaction = NetworkTransaction(initial_backoff_seconds=0)
- self.assertEqual(transaction.run(lambda: self._raise_500_error()), 42)
- self.assertEqual(self._run_count, 3)
- self.assertLog(['WARNING: Received HTTP status 500 from server. '
- 'Retrying in 0 seconds...\n',
- 'WARNING: Received HTTP status 500 from server. '
- 'Retrying in 0.0 seconds...\n'])
-
- def test_convert_404_to_None(self):
- transaction = NetworkTransaction(convert_404_to_None=True)
- self.assertEqual(transaction.run(lambda: self._raise_404_error()), None)
-
- def test_timeout(self):
- self._run_count = 0
- transaction = NetworkTransaction(initial_backoff_seconds=60*60, timeout_seconds=60)
- did_process_exception = False
- did_throw_exception = True
- try:
- transaction.run(lambda: self._raise_500_error())
- did_throw_exception = False
- except NetworkTimeout, e:
- did_process_exception = True
- self.assertTrue(did_throw_exception)
- self.assertTrue(did_process_exception)
diff --git a/WebKitTools/Scripts/webkitpy/common/net/regressionwindow.py b/WebKitTools/Scripts/webkitpy/common/net/regressionwindow.py
deleted file mode 100644
index ad89815..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/regressionwindow.py
+++ /dev/null
@@ -1,51 +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.
-
-
-class RegressionWindow(object):
- def __init__(self, build_before_failure, failing_build, failing_tests=None):
- self._build_before_failure = build_before_failure
- self._failing_build = failing_build
- self._failing_tests = failing_tests
- self._revisions = None
-
- def build_before_failure(self):
- return self._build_before_failure
-
- def failing_build(self):
- return self._failing_build
-
- def failing_tests(self):
- return self._failing_tests
-
- def revisions(self):
- # Cache revisions to avoid excessive allocations.
- if not self._revisions:
- self._revisions = range(self._failing_build.revision(), self._build_before_failure.revision(), -1)
- self._revisions.reverse()
- return self._revisions
diff --git a/WebKitTools/Scripts/webkitpy/common/net/statusserver.py b/WebKitTools/Scripts/webkitpy/common/net/statusserver.py
deleted file mode 100644
index 64dd77b..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/statusserver.py
+++ /dev/null
@@ -1,160 +0,0 @@
-# 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
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from webkitpy.common.net.networktransaction import NetworkTransaction
-from webkitpy.common.system.deprecated_logging import log
-from webkitpy.thirdparty.autoinstalled.mechanize import Browser
-from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup
-
-import logging
-import urllib2
-
-
-_log = logging.getLogger("webkitpy.common.net.statusserver")
-
-
-class StatusServer:
- default_host = "queues.webkit.org"
-
- def __init__(self, host=default_host, browser=None, bot_id=None):
- self.set_host(host)
- self._browser = browser or Browser()
- self.set_bot_id(bot_id)
-
- def set_host(self, host):
- self.host = host
- self.url = "http://%s" % self.host
-
- def set_bot_id(self, bot_id):
- self.bot_id = bot_id
-
- def results_url_for_status(self, status_id):
- return "%s/results/%s" % (self.url, status_id)
-
- def _add_patch(self, patch):
- if not patch:
- return
- if patch.bug_id():
- self._browser["bug_id"] = unicode(patch.bug_id())
- if patch.id():
- self._browser["patch_id"] = unicode(patch.id())
-
- def _add_results_file(self, results_file):
- if not results_file:
- return
- self._browser.add_file(results_file, "text/plain", "results.txt", 'results_file')
-
- def _post_status_to_server(self, queue_name, status, patch, results_file):
- if results_file:
- # We might need to re-wind the file if we've already tried to post it.
- results_file.seek(0)
-
- update_status_url = "%s/update-status" % self.url
- self._browser.open(update_status_url)
- self._browser.select_form(name="update_status")
- self._browser["queue_name"] = queue_name
- if self.bot_id:
- self._browser["bot_id"] = self.bot_id
- self._add_patch(patch)
- self._browser["status"] = status
- self._add_results_file(results_file)
- return self._browser.submit().read() # This is the id of the newly created status object.
-
- def _post_svn_revision_to_server(self, svn_revision_number, broken_bot):
- update_svn_revision_url = "%s/update-svn-revision" % self.url
- self._browser.open(update_svn_revision_url)
- self._browser.select_form(name="update_svn_revision")
- self._browser["number"] = unicode(svn_revision_number)
- self._browser["broken_bot"] = broken_bot
- return self._browser.submit().read()
-
- def _post_work_items_to_server(self, queue_name, work_items):
- update_work_items_url = "%s/update-work-items" % self.url
- self._browser.open(update_work_items_url)
- self._browser.select_form(name="update_work_items")
- self._browser["queue_name"] = queue_name
- work_items = map(unicode, work_items) # .join expects strings
- self._browser["work_items"] = " ".join(work_items)
- return self._browser.submit().read()
-
- def _post_work_item_to_ews(self, attachment_id):
- submit_to_ews_url = "%s/submit-to-ews" % self.url
- self._browser.open(submit_to_ews_url)
- self._browser.select_form(name="submit_to_ews")
- self._browser["attachment_id"] = unicode(attachment_id)
- self._browser.submit()
-
- def submit_to_ews(self, attachment_id):
- _log.info("Submitting attachment %s to EWS queues" % attachment_id)
- return NetworkTransaction().run(lambda: self._post_work_item_to_ews(attachment_id))
-
- def next_work_item(self, queue_name):
- _log.debug("Fetching next work item for %s" % queue_name)
- patch_status_url = "%s/next-patch/%s" % (self.url, queue_name)
- return self._fetch_url(patch_status_url)
-
- def _post_release_work_item(self, queue_name, patch):
- release_patch_url = "%s/release-patch" % (self.url)
- self._browser.open(release_patch_url)
- self._browser.select_form(name="release_patch")
- self._browser["queue_name"] = queue_name
- self._browser["attachment_id"] = unicode(patch.id())
- self._browser.submit()
-
- def release_work_item(self, queue_name, patch):
- _log.info("Releasing work item %s from %s" % (patch.id(), queue_name))
- return NetworkTransaction(convert_404_to_None=True).run(lambda: self._post_release_work_item(queue_name, patch))
-
- def update_work_items(self, queue_name, work_items):
- _log.debug("Recording work items: %s for %s" % (work_items, queue_name))
- return NetworkTransaction().run(lambda: self._post_work_items_to_server(queue_name, work_items))
-
- def update_status(self, queue_name, status, patch=None, results_file=None):
- log(status)
- return NetworkTransaction().run(lambda: self._post_status_to_server(queue_name, status, patch, results_file))
-
- def update_svn_revision(self, svn_revision_number, broken_bot):
- log("SVN revision: %s broke %s" % (svn_revision_number, broken_bot))
- return NetworkTransaction().run(lambda: self._post_svn_revision_to_server(svn_revision_number, broken_bot))
-
- def _fetch_url(self, url):
- # FIXME: This should use NetworkTransaction's 404 handling instead.
- try:
- return urllib2.urlopen(url).read()
- except urllib2.HTTPError, e:
- if e.code == 404:
- return None
- raise e
-
- def patch_status(self, queue_name, patch_id):
- patch_status_url = "%s/patch-status/%s/%s" % (self.url, queue_name, patch_id)
- return self._fetch_url(patch_status_url)
-
- def svn_revision(self, svn_revision_number):
- svn_revision_url = "%s/svn-revision/%s" % (self.url, svn_revision_number)
- return self._fetch_url(svn_revision_url)
diff --git a/WebKitTools/Scripts/webkitpy/common/net/statusserver_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/statusserver_unittest.py
deleted file mode 100644
index 1169ba0..0000000
--- a/WebKitTools/Scripts/webkitpy/common/net/statusserver_unittest.py
+++ /dev/null
@@ -1,43 +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.
-
-import unittest
-
-from webkitpy.common.net.statusserver import StatusServer
-from webkitpy.common.system.outputcapture import OutputCaptureTestCaseBase
-from webkitpy.tool.mocktool import MockBrowser
-
-
-class StatusServerTest(OutputCaptureTestCaseBase):
- def test_url_for_issue(self):
- mock_browser = MockBrowser()
- status_server = StatusServer(browser=mock_browser, bot_id='123')
- status_server.update_status('queue name', 'the status')
- self.assertEqual('queue name', mock_browser.params['queue_name'])
- self.assertEqual('the status', mock_browser.params['status'])
- self.assertEqual('123', mock_browser.params['bot_id'])
diff --git a/WebKitTools/Scripts/webkitpy/common/newstringio.py b/WebKitTools/Scripts/webkitpy/common/newstringio.py
deleted file mode 100644
index f6d08ec..0000000
--- a/WebKitTools/Scripts/webkitpy/common/newstringio.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env 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.
-
-"""'with'-compliant StringIO implementation."""
-
-import StringIO
-
-
-class StringIO(StringIO.StringIO):
- def __enter__(self):
- return self
-
- def __exit__(self, type, value, traceback):
- pass
diff --git a/WebKitTools/Scripts/webkitpy/common/newstringio_unittest.py b/WebKitTools/Scripts/webkitpy/common/newstringio_unittest.py
deleted file mode 100644
index 5755c98..0000000
--- a/WebKitTools/Scripts/webkitpy/common/newstringio_unittest.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env 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 newstringio module."""
-
-from __future__ import with_statement
-
-import unittest
-
-import newstringio
-
-
-class NewStringIOTest(unittest.TestCase):
- def test_with(self):
- with newstringio.StringIO("foo") as f:
- contents = f.read()
- self.assertEqual(contents, "foo")
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/prettypatch.py b/WebKitTools/Scripts/webkitpy/common/prettypatch.py
deleted file mode 100644
index 4e92a53..0000000
--- a/WebKitTools/Scripts/webkitpy/common/prettypatch.py
+++ /dev/null
@@ -1,66 +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.
-
-import os
-import tempfile
-
-
-class PrettyPatch(object):
- # FIXME: PrettyPatch should not require checkout_root.
- def __init__(self, executive, checkout_root):
- self._executive = executive
- self._checkout_root = checkout_root
-
- def pretty_diff_file(self, diff):
- # Diffs can contain multiple text files of different encodings
- # so we always deal with them as byte arrays, not unicode strings.
- assert(isinstance(diff, str))
- pretty_diff = self.pretty_diff(diff)
- diff_file = tempfile.NamedTemporaryFile(suffix=".html")
- diff_file.write(pretty_diff)
- diff_file.flush()
- return diff_file
-
- def pretty_diff(self, diff):
- # pretify.rb will hang forever if given no input.
- # Avoid the hang by returning an empty string.
- if not diff:
- return ""
-
- pretty_patch_path = os.path.join(self._checkout_root,
- "BugsSite", "PrettyPatch")
- prettify_path = os.path.join(pretty_patch_path, "prettify.rb")
- args = [
- "ruby",
- "-I",
- pretty_patch_path,
- prettify_path,
- ]
- # PrettyPatch does not modify the encoding of the diff output
- # so we can't expect it to be utf-8.
- return self._executive.run_command(args, input=diff, decode_output=False)
diff --git a/WebKitTools/Scripts/webkitpy/common/prettypatch_unittest.py b/WebKitTools/Scripts/webkitpy/common/prettypatch_unittest.py
deleted file mode 100644
index 1307856..0000000
--- a/WebKitTools/Scripts/webkitpy/common/prettypatch_unittest.py
+++ /dev/null
@@ -1,70 +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.
-
-import os.path
-import unittest
-
-from webkitpy.common.system.executive import Executive
-from webkitpy.common.prettypatch import PrettyPatch
-
-
-class PrettyPatchTest(unittest.TestCase):
-
- _diff_with_multiple_encodings = """
-Index: utf8_test
-===================================================================
---- utf8_test\t(revision 0)
-+++ utf8_test\t(revision 0)
-@@ -0,0 +1 @@
-+utf-8 test: \xc2\xa0
-Index: latin1_test
-===================================================================
---- latin1_test\t(revision 0)
-+++ latin1_test\t(revision 0)
-@@ -0,0 +1 @@
-+latin1 test: \xa0
-"""
-
- def _webkit_root(self):
- webkitpy_common = os.path.dirname(__file__)
- webkitpy = os.path.dirname(webkitpy_common)
- scripts = os.path.dirname(webkitpy)
- webkit_tools = os.path.dirname(scripts)
- webkit_root = os.path.dirname(webkit_tools)
- return webkit_root
-
- def test_pretty_diff_encodings(self):
- pretty_patch = PrettyPatch(Executive(), self._webkit_root())
- pretty = pretty_patch.pretty_diff(self._diff_with_multiple_encodings)
- self.assertTrue(pretty) # We got some output
- self.assertTrue(isinstance(pretty, str)) # It's a byte array, not unicode
-
- def test_pretty_print_empty_string(self):
- # Make sure that an empty diff does not hang the process.
- pretty_patch = PrettyPatch(Executive(), self._webkit_root())
- self.assertEqual(pretty_patch.pretty_diff(""), "")
diff --git a/WebKitTools/Scripts/webkitpy/common/system/__init__.py b/WebKitTools/Scripts/webkitpy/common/system/__init__.py
deleted file mode 100644
index ef65bee..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/common/system/autoinstall.py b/WebKitTools/Scripts/webkitpy/common/system/autoinstall.py
deleted file mode 100755
index 9adab29..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/autoinstall.py
+++ /dev/null
@@ -1,517 +0,0 @@
-# Copyright (c) 2009, Daniel Krech All rights reserved.
-# 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:
-#
-# * 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 the Daniel Krech 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
-# HOLDER 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.
-
-"""Support for automatically downloading Python packages from an URL."""
-
-
-from __future__ import with_statement
-
-import codecs
-import logging
-import new
-import os
-import shutil
-import sys
-import tarfile
-import tempfile
-import urllib
-import urlparse
-import zipfile
-import zipimport
-
-_log = logging.getLogger(__name__)
-
-
-class AutoInstaller(object):
-
- """Supports automatically installing Python packages from an URL.
-
- Supports uncompressed files, .tar.gz, and .zip formats.
-
- Basic usage:
-
- installer = AutoInstaller()
-
- installer.install(url="http://pypi.python.org/packages/source/p/pep8/pep8-0.5.0.tar.gz#md5=512a818af9979290cd619cce8e9c2e2b",
- url_subpath="pep8-0.5.0/pep8.py")
- installer.install(url="http://pypi.python.org/packages/source/m/mechanize/mechanize-0.1.11.zip",
- url_subpath="mechanize")
-
- """
-
- def __init__(self, append_to_search_path=False, make_package=True,
- target_dir=None, temp_dir=None):
- """Create an AutoInstaller instance, and set up the target directory.
-
- Args:
- append_to_search_path: A boolean value of whether to append the
- target directory to the sys.path search path.
- make_package: A boolean value of whether to make the target
- directory a package. This adds an __init__.py file
- to the target directory -- allowing packages and
- modules within the target directory to be imported
- explicitly using dotted module names.
- target_dir: The directory path to which packages should be installed.
- Defaults to a subdirectory of the folder containing
- this module called "autoinstalled".
- temp_dir: The directory path to use for any temporary files
- generated while downloading, unzipping, and extracting
- packages to install. Defaults to a standard temporary
- location generated by the tempfile module. This
- parameter should normally be used only for development
- testing.
-
- """
- if target_dir is None:
- this_dir = os.path.dirname(__file__)
- target_dir = os.path.join(this_dir, "autoinstalled")
-
- # Ensure that the target directory exists.
- self._set_up_target_dir(target_dir, append_to_search_path, make_package)
-
- self._target_dir = target_dir
- self._temp_dir = temp_dir
-
- def _log_transfer(self, message, source, target, log_method=None):
- """Log a debug message that involves a source and target."""
- if log_method is None:
- log_method = _log.debug
-
- log_method("%s" % message)
- log_method(' From: "%s"' % source)
- log_method(' To: "%s"' % target)
-
- def _create_directory(self, path, name=None):
- """Create a directory."""
- log = _log.debug
-
- name = name + " " if name is not None else ""
- log('Creating %sdirectory...' % name)
- log(' "%s"' % path)
-
- os.makedirs(path)
-
- def _write_file(self, path, text, encoding):
- """Create a file at the given path with given text.
-
- This method overwrites any existing file.
-
- """
- _log.debug("Creating file...")
- _log.debug(' "%s"' % path)
- with codecs.open(path, "w", encoding) as file:
- file.write(text)
-
- def _set_up_target_dir(self, target_dir, append_to_search_path,
- make_package):
- """Set up a target directory.
-
- Args:
- target_dir: The path to the target directory to set up.
- append_to_search_path: A boolean value of whether to append the
- target directory to the sys.path search path.
- make_package: A boolean value of whether to make the target
- directory a package. This adds an __init__.py file
- to the target directory -- allowing packages and
- modules within the target directory to be imported
- explicitly using dotted module names.
-
- """
- if not os.path.exists(target_dir):
- self._create_directory(target_dir, "autoinstall target")
-
- if append_to_search_path:
- sys.path.append(target_dir)
-
- if make_package:
- init_path = os.path.join(target_dir, "__init__.py")
- if not os.path.exists(init_path):
- text = ("# This file is required for Python to search this "
- "directory for modules.\n")
- self._write_file(init_path, text, "ascii")
-
- def _create_scratch_directory_inner(self, prefix):
- """Create a scratch directory without exception handling.
-
- Creates a scratch directory inside the AutoInstaller temp
- directory self._temp_dir, or inside a platform-dependent temp
- directory if self._temp_dir is None. Returns the path to the
- created scratch directory.
-
- Raises:
- OSError: [Errno 2] if the containing temp directory self._temp_dir
- is not None and does not exist.
-
- """
- # The tempfile.mkdtemp() method function requires that the
- # directory corresponding to the "dir" parameter already exist
- # if it is not None.
- scratch_dir = tempfile.mkdtemp(prefix=prefix, dir=self._temp_dir)
- return scratch_dir
-
- def _create_scratch_directory(self, target_name):
- """Create a temporary scratch directory, and return its path.
-
- The scratch directory is generated inside the temp directory
- of this AutoInstaller instance. This method also creates the
- temp directory if it does not already exist.
-
- """
- prefix = target_name + "_"
- try:
- scratch_dir = self._create_scratch_directory_inner(prefix)
- except OSError:
- # Handle case of containing temp directory not existing--
- # OSError: [Errno 2] No such file or directory:...
- temp_dir = self._temp_dir
- if temp_dir is None or os.path.exists(temp_dir):
- raise
- # Else try again after creating the temp directory.
- self._create_directory(temp_dir, "autoinstall temp")
- scratch_dir = self._create_scratch_directory_inner(prefix)
-
- return scratch_dir
-
- def _url_downloaded_path(self, target_name):
- """Return the path to the file containing the URL downloaded."""
- filename = ".%s.url" % target_name
- path = os.path.join(self._target_dir, filename)
- return path
-
- def _is_downloaded(self, target_name, url):
- """Return whether a package version has been downloaded."""
- version_path = self._url_downloaded_path(target_name)
-
- _log.debug('Checking %s URL downloaded...' % target_name)
- _log.debug(' "%s"' % version_path)
-
- if not os.path.exists(version_path):
- # Then no package version has been downloaded.
- _log.debug("No URL file found.")
- return False
-
- with codecs.open(version_path, "r", "utf-8") as file:
- version = file.read()
-
- return version.strip() == url.strip()
-
- def _record_url_downloaded(self, target_name, url):
- """Record the URL downloaded to a file."""
- version_path = self._url_downloaded_path(target_name)
- _log.debug("Recording URL downloaded...")
- _log.debug(' URL: "%s"' % url)
- _log.debug(' To: "%s"' % version_path)
-
- self._write_file(version_path, url, "utf-8")
-
- def _extract_targz(self, path, scratch_dir):
- # tarfile.extractall() extracts to a path without the
- # trailing ".tar.gz".
- target_basename = os.path.basename(path[:-len(".tar.gz")])
- target_path = os.path.join(scratch_dir, target_basename)
-
- self._log_transfer("Starting gunzip/extract...", path, target_path)
-
- try:
- tar_file = tarfile.open(path)
- except tarfile.ReadError, err:
- # Append existing Error message to new Error.
- message = ("Could not open tar file: %s\n"
- " The file probably does not have the correct format.\n"
- " --> Inner message: %s"
- % (path, err))
- raise Exception(message)
-
- try:
- # This is helpful for debugging purposes.
- _log.debug("Listing tar file contents...")
- for name in tar_file.getnames():
- _log.debug(' * "%s"' % name)
- _log.debug("Extracting gzipped tar file...")
- tar_file.extractall(target_path)
- finally:
- tar_file.close()
-
- return target_path
-
- # This is a replacement for ZipFile.extractall(), which is
- # available in Python 2.6 but not in earlier versions.
- def _extract_all(self, zip_file, target_dir):
- self._log_transfer("Extracting zip file...", zip_file, target_dir)
-
- # This is helpful for debugging purposes.
- _log.debug("Listing zip file contents...")
- for name in zip_file.namelist():
- _log.debug(' * "%s"' % name)
-
- for name in zip_file.namelist():
- path = os.path.join(target_dir, name)
- self._log_transfer("Extracting...", name, path)
-
- if not os.path.basename(path):
- # Then the path ends in a slash, so it is a directory.
- self._create_directory(path)
- continue
- # Otherwise, it is a file.
-
- try:
- # We open this file w/o encoding, as we're reading/writing
- # the raw byte-stream from the zip file.
- outfile = open(path, 'wb')
- except IOError, err:
- # Not all zip files seem to list the directories explicitly,
- # so try again after creating the containing directory.
- _log.debug("Got IOError: retrying after creating directory...")
- dir = os.path.dirname(path)
- self._create_directory(dir)
- outfile = open(path, 'wb')
-
- try:
- outfile.write(zip_file.read(name))
- finally:
- outfile.close()
-
- def _unzip(self, path, scratch_dir):
- # zipfile.extractall() extracts to a path without the
- # trailing ".zip".
- target_basename = os.path.basename(path[:-len(".zip")])
- target_path = os.path.join(scratch_dir, target_basename)
-
- self._log_transfer("Starting unzip...", path, target_path)
-
- try:
- zip_file = zipfile.ZipFile(path, "r")
- except zipfile.BadZipfile, err:
- message = ("Could not open zip file: %s\n"
- " --> Inner message: %s"
- % (path, err))
- raise Exception(message)
-
- try:
- self._extract_all(zip_file, scratch_dir)
- finally:
- zip_file.close()
-
- return target_path
-
- def _prepare_package(self, path, scratch_dir):
- """Prepare a package for use, if necessary, and return the new path.
-
- For example, this method unzips zipped files and extracts
- tar files.
-
- Args:
- path: The path to the downloaded URL contents.
- scratch_dir: The scratch directory. Note that the scratch
- directory contains the file designated by the
- path parameter.
-
- """
- # FIXME: Add other natural extensions.
- if path.endswith(".zip"):
- new_path = self._unzip(path, scratch_dir)
- elif path.endswith(".tar.gz"):
- new_path = self._extract_targz(path, scratch_dir)
- else:
- # No preparation is needed.
- new_path = path
-
- return new_path
-
- def _download_to_stream(self, url, stream):
- """Download an URL to a stream, and return the number of bytes."""
- try:
- netstream = urllib.urlopen(url)
- except IOError, err:
- # Append existing Error message to new Error.
- message = ('Could not download Python modules from URL "%s".\n'
- " Make sure you are connected to the internet.\n"
- " You must be connected to the internet when "
- "downloading needed modules for the first time.\n"
- " --> Inner message: %s"
- % (url, err))
- raise IOError(message)
- code = 200
- if hasattr(netstream, "getcode"):
- code = netstream.getcode()
- if not 200 <= code < 300:
- raise ValueError("HTTP Error code %s" % code)
-
- BUFSIZE = 2**13 # 8KB
- bytes = 0
- while True:
- data = netstream.read(BUFSIZE)
- if not data:
- break
- stream.write(data)
- bytes += len(data)
- netstream.close()
- return bytes
-
- def _download(self, url, scratch_dir):
- """Download URL contents, and return the download path."""
- url_path = urlparse.urlsplit(url)[2]
- url_path = os.path.normpath(url_path) # Removes trailing slash.
- target_filename = os.path.basename(url_path)
- target_path = os.path.join(scratch_dir, target_filename)
-
- self._log_transfer("Starting download...", url, target_path)
-
- with open(target_path, "wb") as stream:
- bytes = self._download_to_stream(url, stream)
-
- _log.debug("Downloaded %s bytes." % bytes)
-
- return target_path
-
- def _install(self, scratch_dir, package_name, target_path, url,
- url_subpath):
- """Install a python package from an URL.
-
- This internal method overwrites the target path if the target
- path already exists.
-
- """
- path = self._download(url=url, scratch_dir=scratch_dir)
- path = self._prepare_package(path, scratch_dir)
-
- if url_subpath is None:
- source_path = path
- else:
- source_path = os.path.join(path, url_subpath)
-
- if os.path.exists(target_path):
- _log.debug('Refreshing install: deleting "%s".' % target_path)
- if os.path.isdir(target_path):
- shutil.rmtree(target_path)
- else:
- os.remove(target_path)
-
- self._log_transfer("Moving files into place...", source_path, target_path)
-
- # The shutil.move() command creates intermediate directories if they
- # do not exist, but we do not rely on this behavior since we
- # need to create the __init__.py file anyway.
- shutil.move(source_path, target_path)
-
- self._record_url_downloaded(package_name, url)
-
- def install(self, url, should_refresh=False, target_name=None,
- url_subpath=None):
- """Install a python package from an URL.
-
- Args:
- url: The URL from which to download the package.
-
- Optional Args:
- should_refresh: A boolean value of whether the package should be
- downloaded again if the package is already present.
- target_name: The name of the folder or file in the autoinstaller
- target directory at which the package should be
- installed. Defaults to the base name of the
- URL sub-path. This parameter must be provided if
- the URL sub-path is not specified.
- url_subpath: The relative path of the URL directory that should
- be installed. Defaults to the full directory, or
- the entire URL contents.
-
- """
- if target_name is None:
- if not url_subpath:
- raise ValueError('The "target_name" parameter must be '
- 'provided if the "url_subpath" parameter '
- "is not provided.")
- # Remove any trailing slashes.
- url_subpath = os.path.normpath(url_subpath)
- target_name = os.path.basename(url_subpath)
-
- target_path = os.path.join(self._target_dir, target_name)
- if not should_refresh and self._is_downloaded(target_name, url):
- _log.debug('URL for %s already downloaded. Skipping...'
- % target_name)
- _log.debug(' "%s"' % url)
- return
-
- self._log_transfer("Auto-installing package: %s" % target_name,
- url, target_path, log_method=_log.info)
-
- # The scratch directory is where we will download and prepare
- # files specific to this install until they are ready to move
- # into place.
- scratch_dir = self._create_scratch_directory(target_name)
-
- try:
- self._install(package_name=target_name,
- target_path=target_path,
- scratch_dir=scratch_dir,
- url=url,
- url_subpath=url_subpath)
- except Exception, err:
- # Append existing Error message to new Error.
- message = ("Error auto-installing the %s package to:\n"
- ' "%s"\n'
- " --> Inner message: %s"
- % (target_name, target_path, err))
- raise Exception(message)
- finally:
- _log.debug('Cleaning up: deleting "%s".' % scratch_dir)
- shutil.rmtree(scratch_dir)
- _log.debug('Auto-installed %s to:' % target_name)
- _log.debug(' "%s"' % target_path)
-
-
-if __name__=="__main__":
-
- # Configure the autoinstall logger to log DEBUG messages for
- # development testing purposes.
- console = logging.StreamHandler()
-
- formatter = logging.Formatter('%(name)s: %(levelname)-8s %(message)s')
- console.setFormatter(formatter)
- _log.addHandler(console)
- _log.setLevel(logging.DEBUG)
-
- # Use a more visible temp directory for debug purposes.
- this_dir = os.path.dirname(__file__)
- target_dir = os.path.join(this_dir, "autoinstalled")
- temp_dir = os.path.join(target_dir, "Temp")
-
- installer = AutoInstaller(target_dir=target_dir,
- temp_dir=temp_dir)
-
- installer.install(should_refresh=False,
- target_name="pep8.py",
- url="http://pypi.python.org/packages/source/p/pep8/pep8-0.5.0.tar.gz#md5=512a818af9979290cd619cce8e9c2e2b",
- url_subpath="pep8-0.5.0/pep8.py")
- installer.install(should_refresh=False,
- target_name="mechanize",
- url="http://pypi.python.org/packages/source/m/mechanize/mechanize-0.1.11.zip",
- url_subpath="mechanize")
-
diff --git a/WebKitTools/Scripts/webkitpy/common/system/deprecated_logging.py b/WebKitTools/Scripts/webkitpy/common/system/deprecated_logging.py
deleted file mode 100644
index 9e6b529..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/deprecated_logging.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# Copyright (c) 2009, Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple 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.
-#
-# WebKit's Python module for logging
-# This module is now deprecated in favor of python's built-in logging.py.
-
-import codecs
-import os
-import sys
-
-
-def log(string):
- print >> sys.stderr, string
-
-
-def error(string):
- log("ERROR: %s" % string)
- exit(1)
-
-
-# Simple class to split output between multiple destinations
-class tee:
- def __init__(self, *files):
- self.files = files
-
- # Callers should pass an already encoded string for writing.
- def write(self, bytes):
- for file in self.files:
- file.write(bytes)
-
-
-class OutputTee:
- def __init__(self):
- self._original_stdout = None
- self._original_stderr = None
- self._files_for_output = []
-
- def add_log(self, path):
- log_file = self._open_log_file(path)
- self._files_for_output.append(log_file)
- self._tee_outputs_to_files(self._files_for_output)
- return log_file
-
- def remove_log(self, log_file):
- self._files_for_output.remove(log_file)
- self._tee_outputs_to_files(self._files_for_output)
- log_file.close()
-
- @staticmethod
- def _open_log_file(log_path):
- (log_directory, log_name) = os.path.split(log_path)
- if log_directory and not os.path.exists(log_directory):
- os.makedirs(log_directory)
- return codecs.open(log_path, "a+", "utf-8")
-
- def _tee_outputs_to_files(self, files):
- if not self._original_stdout:
- self._original_stdout = sys.stdout
- self._original_stderr = sys.stderr
- if files and len(files):
- sys.stdout = tee(self._original_stdout, *files)
- sys.stderr = tee(self._original_stderr, *files)
- else:
- sys.stdout = self._original_stdout
- sys.stderr = self._original_stderr
diff --git a/WebKitTools/Scripts/webkitpy/common/system/deprecated_logging_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/deprecated_logging_unittest.py
deleted file mode 100644
index 3778162..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/deprecated_logging_unittest.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# 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
-# 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 os
-import StringIO
-import tempfile
-import unittest
-
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.common.system.deprecated_logging import *
-
-class LoggingTest(unittest.TestCase):
-
- def assert_log_equals(self, log_input, expected_output):
- original_stderr = sys.stderr
- test_stderr = StringIO.StringIO()
- sys.stderr = test_stderr
-
- try:
- log(log_input)
- actual_output = test_stderr.getvalue()
- finally:
- sys.stderr = original_stderr
-
- self.assertEquals(actual_output, expected_output, "log(\"%s\") expected: %s actual: %s" % (log_input, expected_output, actual_output))
-
- def test_log(self):
- self.assert_log_equals("test", "test\n")
-
- # Test that log() does not throw an exception when passed an object instead of a string.
- self.assert_log_equals(ScriptError(message="ScriptError"), "ScriptError\n")
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/system/executive.py b/WebKitTools/Scripts/webkitpy/common/system/executive.py
deleted file mode 100644
index 85a683a..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/executive.py
+++ /dev/null
@@ -1,399 +0,0 @@
-# Copyright (c) 2009, Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple 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.
-
-try:
- # This API exists only in Python 2.6 and higher. :(
- import multiprocessing
-except ImportError:
- multiprocessing = None
-
-import ctypes
-import errno
-import logging
-import os
-import platform
-import StringIO
-import signal
-import subprocess
-import sys
-import time
-
-from webkitpy.common.system.deprecated_logging import tee
-from webkitpy.python24 import versioning
-
-
-_log = logging.getLogger("webkitpy.common.system")
-
-
-class ScriptError(Exception):
-
- def __init__(self,
- message=None,
- script_args=None,
- exit_code=None,
- output=None,
- cwd=None):
- if not message:
- message = 'Failed to run "%s"' % script_args
- if exit_code:
- message += " exit_code: %d" % exit_code
- if cwd:
- message += " cwd: %s" % cwd
-
- Exception.__init__(self, message)
- self.script_args = script_args # 'args' is already used by Exception
- self.exit_code = exit_code
- self.output = output
- self.cwd = cwd
-
- def message_with_output(self, output_limit=500):
- if self.output:
- if output_limit and len(self.output) > output_limit:
- return u"%s\nLast %s characters of output:\n%s" % \
- (self, output_limit, self.output[-output_limit:])
- return u"%s\n%s" % (self, self.output)
- return unicode(self)
-
- def command_name(self):
- command_path = self.script_args
- if type(command_path) is list:
- command_path = command_path[0]
- return os.path.basename(command_path)
-
-
-def run_command(*args, **kwargs):
- # FIXME: This should not be a global static.
- # New code should use Executive.run_command directly instead
- return Executive().run_command(*args, **kwargs)
-
-
-class Executive(object):
-
- def _should_close_fds(self):
- # We need to pass close_fds=True to work around Python bug #2320
- # (otherwise we can hang when we kill DumpRenderTree when we are running
- # multiple threads). See http://bugs.python.org/issue2320 .
- # Note that close_fds isn't supported on Windows, but this bug only
- # shows up on Mac and Linux.
- return sys.platform not in ('win32', 'cygwin')
-
- def _run_command_with_teed_output(self, args, teed_output):
- args = map(unicode, args) # Popen will throw an exception if args are non-strings (like int())
- args = map(self._encode_argument_if_needed, args)
-
- child_process = subprocess.Popen(args,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- close_fds=self._should_close_fds())
-
- # Use our own custom wait loop because Popen ignores a tee'd
- # stderr/stdout.
- # FIXME: This could be improved not to flatten output to stdout.
- while True:
- output_line = child_process.stdout.readline()
- if output_line == "" and child_process.poll() != None:
- # poll() is not threadsafe and can throw OSError due to:
- # http://bugs.python.org/issue1731717
- return child_process.poll()
- # We assume that the child process wrote to us in utf-8,
- # so no re-encoding is necessary before writing here.
- teed_output.write(output_line)
-
- # FIXME: Remove this deprecated method and move callers to run_command.
- # FIXME: This method is a hack to allow running command which both
- # capture their output and print out to stdin. Useful for things
- # like "build-webkit" where we want to display to the user that we're building
- # but still have the output to stuff into a log file.
- def run_and_throw_if_fail(self, args, quiet=False, decode_output=True):
- # Cache the child's output locally so it can be used for error reports.
- child_out_file = StringIO.StringIO()
- tee_stdout = sys.stdout
- if quiet:
- dev_null = open(os.devnull, "w") # FIXME: Does this need an encoding?
- tee_stdout = dev_null
- child_stdout = tee(child_out_file, tee_stdout)
- exit_code = self._run_command_with_teed_output(args, child_stdout)
- if quiet:
- dev_null.close()
-
- child_output = child_out_file.getvalue()
- child_out_file.close()
-
- if decode_output:
- child_output = child_output.decode(self._child_process_encoding())
-
- if exit_code:
- raise ScriptError(script_args=args,
- exit_code=exit_code,
- output=child_output)
- return child_output
-
- def cpu_count(self):
- if multiprocessing:
- return multiprocessing.cpu_count()
- # Darn. We don't have the multiprocessing package.
- system_name = platform.system()
- if system_name == "Darwin":
- return int(self.run_command(["sysctl", "-n", "hw.ncpu"]))
- elif system_name == "Windows":
- return int(os.environ.get('NUMBER_OF_PROCESSORS', 1))
- elif system_name == "Linux":
- num_cores = os.sysconf("SC_NPROCESSORS_ONLN")
- if isinstance(num_cores, int) and num_cores > 0:
- return num_cores
- # This quantity is a lie but probably a reasonable guess for modern
- # machines.
- return 2
-
- def kill_process(self, pid):
- """Attempts to kill the given pid.
- Will fail silently if pid does not exist or insufficient permisssions."""
- 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
-
- # 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 = 10 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 _win32_check_running_pid(self, pid):
-
- class PROCESSENTRY32(ctypes.Structure):
- _fields_ = [("dwSize", ctypes.c_ulong),
- ("cntUsage", ctypes.c_ulong),
- ("th32ProcessID", ctypes.c_ulong),
- ("th32DefaultHeapID", ctypes.c_ulong),
- ("th32ModuleID", ctypes.c_ulong),
- ("cntThreads", ctypes.c_ulong),
- ("th32ParentProcessID", ctypes.c_ulong),
- ("pcPriClassBase", ctypes.c_ulong),
- ("dwFlags", ctypes.c_ulong),
- ("szExeFile", ctypes.c_char * 260)]
-
- CreateToolhelp32Snapshot = ctypes.windll.kernel32.CreateToolhelp32Snapshot
- Process32First = ctypes.windll.kernel32.Process32First
- Process32Next = ctypes.windll.kernel32.Process32Next
- CloseHandle = ctypes.windll.kernel32.CloseHandle
- TH32CS_SNAPPROCESS = 0x00000002 # win32 magic number
- hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
- pe32 = PROCESSENTRY32()
- pe32.dwSize = ctypes.sizeof(PROCESSENTRY32)
- result = False
- if not Process32First(hProcessSnap, ctypes.byref(pe32)):
- _log.debug("Failed getting first process.")
- CloseHandle(hProcessSnap)
- return result
- while True:
- if pe32.th32ProcessID == pid:
- result = True
- break
- if not Process32Next(hProcessSnap, ctypes.byref(pe32)):
- break
- CloseHandle(hProcessSnap)
- return result
-
- def check_running_pid(self, pid):
- """Return True if pid is alive, otherwise return False."""
- if sys.platform in ('darwin', 'linux2', 'cygwin'):
- try:
- os.kill(pid, 0)
- return True
- except OSError:
- return False
- elif sys.platform == 'win32':
- return self._win32_check_running_pid(pid)
-
- assert(False)
-
- 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 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
-
- # FIXME: This is inconsistent that kill_all uses TERM and kill_process
- # uses KILL. Windows is always using /f (which seems like -KILL).
- # 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
- # updated to use an Executive object.
-
- @staticmethod
- def default_error_handler(error):
- raise error
-
- @staticmethod
- def ignore_error(error):
- pass
-
- def _compute_stdin(self, input):
- """Returns (stdin, string_to_communicate)"""
- # FIXME: We should be returning /dev/null for stdin
- # or closing stdin after process creation to prevent
- # child processes from getting input from the user.
- if not input:
- return (None, None)
- if hasattr(input, "read"): # Check if the input is a file.
- return (input, None) # Assume the file is in the right encoding.
-
- # Popen in Python 2.5 and before does not automatically encode unicode objects.
- # http://bugs.python.org/issue5290
- # See https://bugs.webkit.org/show_bug.cgi?id=37528
- # for an example of a regresion caused by passing a unicode string directly.
- # FIXME: We may need to encode differently on different platforms.
- if isinstance(input, unicode):
- input = input.encode(self._child_process_encoding())
- return (subprocess.PIPE, input)
-
- def _command_for_printing(self, args):
- """Returns a print-ready string representing command args.
- The string should be copy/paste ready for execution in a shell."""
- escaped_args = []
- for arg in args:
- if isinstance(arg, unicode):
- # Escape any non-ascii characters for easy copy/paste
- arg = arg.encode("unicode_escape")
- # FIXME: Do we need to fix quotes here?
- escaped_args.append(arg)
- return " ".join(escaped_args)
-
- # FIXME: run_and_throw_if_fail should be merged into this method.
- def run_command(self,
- args,
- cwd=None,
- input=None,
- error_handler=None,
- return_exit_code=False,
- return_stderr=True,
- decode_output=True):
- """Popen wrapper for convenience and to work around python bugs."""
- assert(isinstance(args, list) or isinstance(args, tuple))
- start_time = time.time()
- args = map(unicode, args) # Popen will throw an exception if args are non-strings (like int())
- args = map(self._encode_argument_if_needed, args)
-
- stdin, string_to_communicate = self._compute_stdin(input)
- stderr = subprocess.STDOUT if return_stderr else None
-
- process = subprocess.Popen(args,
- stdin=stdin,
- stdout=subprocess.PIPE,
- stderr=stderr,
- cwd=cwd,
- close_fds=self._should_close_fds())
- output = process.communicate(string_to_communicate)[0]
-
- # run_command automatically decodes to unicode() unless explicitly told not to.
- if decode_output:
- output = output.decode(self._child_process_encoding())
-
- # wait() is not threadsafe and can throw OSError due to:
- # http://bugs.python.org/issue1731717
- exit_code = process.wait()
-
- _log.debug('"%s" took %.2fs' % (self._command_for_printing(args), time.time() - start_time))
-
- if return_exit_code:
- return exit_code
-
- if exit_code:
- script_error = ScriptError(script_args=args,
- exit_code=exit_code,
- output=output,
- cwd=cwd)
- (error_handler or self.default_error_handler)(script_error)
- return output
-
- def _child_process_encoding(self):
- # Win32 Python 2.x uses CreateProcessA rather than CreateProcessW
- # to launch subprocesses, so we have to encode arguments using the
- # current code page.
- if sys.platform == 'win32' and versioning.compare_version(sys, '3.0')[0] < 0:
- return 'mbcs'
- # All other platforms use UTF-8.
- # FIXME: Using UTF-8 on Cygwin will confuse Windows-native commands
- # which will expect arguments to be encoded using the current code
- # page.
- return 'utf-8'
-
- def _should_encode_child_process_arguments(self):
- # Cygwin's Python's os.execv doesn't support unicode command
- # arguments, and neither does Cygwin's execv itself.
- if sys.platform == 'cygwin':
- return True
-
- # Win32 Python 2.x uses CreateProcessA rather than CreateProcessW
- # to launch subprocesses, so we have to encode arguments using the
- # current code page.
- if sys.platform == 'win32' and versioning.compare_version(sys, '3.0')[0] < 0:
- return True
-
- return False
-
- def _encode_argument_if_needed(self, argument):
- if not self._should_encode_child_process_arguments():
- return argument
- return argument.encode(self._child_process_encoding())
diff --git a/WebKitTools/Scripts/webkitpy/common/system/executive_mock.py b/WebKitTools/Scripts/webkitpy/common/system/executive_mock.py
deleted file mode 100644
index c1cf999..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/executive_mock.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# 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
-# 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.
-
-# FIXME: Implement the rest of the interface as needed for testing :).
-
-# FIXME: Unify with tool/mocktool.MockExecutive.
-
-
-class MockExecutive2(object):
- def __init__(self, output='', exit_code=0, exception=None,
- run_command_fn=None):
- self._output = output
- self._exit_code = exit_code
- self._exception = exception
- self._run_command_fn = run_command_fn
-
- def cpu_count(self):
- return 2
-
- def kill_all(self, process_name):
- pass
-
- def kill_process(self, pid):
- pass
-
- def run_command(self, arg_list, return_exit_code=False,
- decode_output=False):
- if self._exception:
- raise self._exception
- if return_exit_code:
- return self._exit_code
- if self._run_command_fn:
- return self._run_command_fn(arg_list)
- return self._output
diff --git a/WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py
deleted file mode 100644
index b8fd82e..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py
+++ /dev/null
@@ -1,151 +0,0 @@
-# Copyright (C) 2010 Google Inc. All rights reserved.
-# Copyright (C) 2009 Daniel Bates (dbates@intudata.com). 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 os
-import signal
-import subprocess
-import sys
-import unittest
-
-from webkitpy.common.system.executive import Executive, run_command, ScriptError
-from webkitpy.test import cat, echo
-
-
-def never_ending_command():
- """Arguments for a command that will never end (useful for testing process
- killing). It should be a process that is unlikely to already be running
- because all instances will be killed."""
- if sys.platform == 'win32':
- return ['wmic']
- return ['yes']
-
-
-class ExecutiveTest(unittest.TestCase):
-
- def test_run_command_with_bad_command(self):
- def run_bad_command():
- run_command(["foo_bar_command_blah"], error_handler=Executive.ignore_error, return_exit_code=True)
- self.failUnlessRaises(OSError, run_bad_command)
-
- def test_run_command_args_type(self):
- executive = Executive()
- self.assertRaises(AssertionError, executive.run_command, "echo")
- self.assertRaises(AssertionError, executive.run_command, u"echo")
- executive.run_command(echo.command_arguments('foo'))
- executive.run_command(tuple(echo.command_arguments('foo')))
-
- def test_run_command_with_unicode(self):
- """Validate that it is safe to pass unicode() objects
- to Executive.run* methods, and they will return unicode()
- objects by default unless decode_output=False"""
- unicode_tor_input = u"WebKit \u2661 Tor Arne Vestb\u00F8!"
- if sys.platform == 'win32':
- encoding = 'mbcs'
- else:
- encoding = 'utf-8'
- encoded_tor = unicode_tor_input.encode(encoding)
- # On Windows, we expect the unicode->mbcs->unicode roundtrip to be
- # lossy. On other platforms, we expect a lossless roundtrip.
- if sys.platform == 'win32':
- unicode_tor_output = encoded_tor.decode(encoding)
- else:
- unicode_tor_output = unicode_tor_input
-
- executive = Executive()
-
- output = executive.run_command(cat.command_arguments(), input=unicode_tor_input)
- self.assertEquals(output, unicode_tor_output)
-
- output = executive.run_command(echo.command_arguments("-n", unicode_tor_input))
- self.assertEquals(output, unicode_tor_output)
-
- output = executive.run_command(echo.command_arguments("-n", unicode_tor_input), decode_output=False)
- self.assertEquals(output, encoded_tor)
-
- # Make sure that str() input also works.
- output = executive.run_command(cat.command_arguments(), input=encoded_tor, decode_output=False)
- self.assertEquals(output, encoded_tor)
-
- # FIXME: We should only have one run* method to test
- output = executive.run_and_throw_if_fail(echo.command_arguments("-n", unicode_tor_input), quiet=True)
- self.assertEquals(output, unicode_tor_output)
-
- output = executive.run_and_throw_if_fail(echo.command_arguments("-n", unicode_tor_input), quiet=True, decode_output=False)
- self.assertEquals(output, encoded_tor)
-
- def test_kill_process(self):
- executive = Executive()
- process = subprocess.Popen(never_ending_command(), stdout=subprocess.PIPE)
- self.assertEqual(process.poll(), None) # Process is running
- executive.kill_process(process.pid)
- # Note: Can't use a ternary since signal.SIGKILL is undefined for sys.platform == "win32"
- if sys.platform == "win32":
- expected_exit_code = 1
- 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()
- # We use "yes" because it loops forever.
- process = subprocess.Popen(never_ending_command(), stdout=subprocess.PIPE)
- self.assertEqual(process.poll(), None) # Process is running
- executive.kill_all(never_ending_command()[0])
- # Note: Can't use a ternary since signal.SIGTERM is undefined for sys.platform == "win32"
- if sys.platform == "cygwin":
- expected_exit_code = 0 # os.kill results in exit(0) for this process.
- elif sys.platform == "win32":
- expected_exit_code = 1
- else:
- expected_exit_code = -signal.SIGTERM
- self.assertEqual(process.wait(), expected_exit_code)
- # Killing again should fail silently.
- executive.kill_all(never_ending_command()[0])
-
- def test_check_running_pid(self):
- executive = Executive()
- self.assertTrue(executive.check_running_pid(os.getpid()))
- # Maximum pid number on Linux is 32768 by default
- self.assertFalse(executive.check_running_pid(100000))
diff --git a/WebKitTools/Scripts/webkitpy/common/system/file_lock.py b/WebKitTools/Scripts/webkitpy/common/system/file_lock.py
deleted file mode 100644
index 7296958..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/file_lock.py
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Szeged
-#
-# 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.
-#
-# THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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.
-
-"""This class helps to lock files exclusively across processes."""
-
-import logging
-import os
-import sys
-import time
-
-
-_log = logging.getLogger("webkitpy.common.system.file_lock")
-
-
-class FileLock(object):
-
- def __init__(self, lock_file_path, max_wait_time_sec=20):
- self._lock_file_path = lock_file_path
- self._lock_file_descriptor = None
- self._max_wait_time_sec = max_wait_time_sec
-
- def _create_lock(self):
- if sys.platform in ('darwin', 'linux2', 'cygwin'):
- import fcntl
- fcntl.flock(self._lock_file_descriptor, fcntl.LOCK_EX | fcntl.LOCK_NB)
- elif sys.platform == 'win32':
- import msvcrt
- msvcrt.locking(self._lock_file_descriptor, msvcrt.LK_NBLCK, 32)
-
- def _remove_lock(self):
- if sys.platform in ('darwin', 'linux2', 'cygwin'):
- import fcntl
- fcntl.flock(self._lock_file_descriptor, fcntl.LOCK_UN)
- elif sys.platform == 'win32':
- import msvcrt
- msvcrt.locking(self._lock_file_descriptor, msvcrt.LK_UNLCK, 32)
-
- def acquire_lock(self):
- self._lock_file_descriptor = os.open(self._lock_file_path, os.O_TRUNC | os.O_CREAT)
- start_time = time.time()
- while True:
- try:
- self._create_lock()
- return True
- except IOError:
- if time.time() - start_time > self._max_wait_time_sec:
- _log.debug("File locking failed: %s" % str(sys.exc_info()))
- os.close(self._lock_file_descriptor)
- self._lock_file_descriptor = None
- return False
-
- def release_lock(self):
- try:
- if self._lock_file_descriptor:
- self._remove_lock()
- os.close(self._lock_file_descriptor)
- self._lock_file_descriptor = None
- os.unlink(self._lock_file_path)
- except (IOError, OSError):
- _log.debug("Warning in release lock: %s" % str(sys.exc_info()))
diff --git a/WebKitTools/Scripts/webkitpy/common/system/file_lock_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/file_lock_unittest.py
deleted file mode 100644
index c5c1db3..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/file_lock_unittest.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Szeged
-#
-# 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.
-#
-# THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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 os
-import tempfile
-import unittest
-
-from webkitpy.common.system.file_lock import FileLock
-
-
-class FileLockTest(unittest.TestCase):
-
- def setUp(self):
- self._lock_name = "TestWebKit" + str(os.getpid()) + ".lock"
- self._lock_path = os.path.join(tempfile.gettempdir(), self._lock_name)
- self._file_lock1 = FileLock(self._lock_path, 1)
- self._file_lock2 = FileLock(self._lock_path, 1)
-
- def tearDown(self):
- self._file_lock1.release_lock()
- self._file_lock2.release_lock()
-
- def test_lock_lifecycle(self):
- # Create the lock.
- self._file_lock1.acquire_lock()
- self.assertTrue(os.path.exists(self._lock_path))
-
- # Try to lock again.
- self.assertFalse(self._file_lock2.acquire_lock())
-
- # Release the lock.
- self._file_lock1.release_lock()
- self.assertFalse(os.path.exists(self._lock_path))
-
- def test_stuck_lock(self):
- open(self._lock_path, 'w').close()
- self._file_lock1.acquire_lock()
- self._file_lock1.release_lock()
diff --git a/WebKitTools/Scripts/webkitpy/common/system/filesystem.py b/WebKitTools/Scripts/webkitpy/common/system/filesystem.py
deleted file mode 100644
index c7efde3..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/filesystem.py
+++ /dev/null
@@ -1,117 +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.
-
-"""Wrapper object for the file system / source tree."""
-
-from __future__ import with_statement
-
-import codecs
-import errno
-import os
-import tempfile
-
-
-class FileSystem(object):
- """FileSystem interface for webkitpy.
-
- Unless otherwise noted, all paths are allowed to be either absolute
- or relative."""
-
- def exists(self, path):
- """Return whether the path exists in the filesystem."""
- return os.path.exists(path)
-
- def isdir(self, path):
- """Return whether the path refers to a directory."""
- return os.path.isdir(path)
-
- def join(self, *comps):
- """Return the path formed by joining the components."""
- return os.path.join(*comps)
-
- def listdir(self, path):
- """Return the contents of the directory pointed to by path."""
- return os.listdir(path)
-
- def mkdtemp(self, **kwargs):
- """Create and return a uniquely named directory.
-
- This is like tempfile.mkdtemp, but if used in a with statement
- the directory will self-delete at the end of the block (if the
- directory is empty; non-empty directories raise errors). The
- directory can be safely deleted inside the block as well, if so
- desired."""
- class TemporaryDirectory(object):
- def __init__(self, **kwargs):
- self._kwargs = kwargs
- self._directory_path = None
-
- def __enter__(self):
- self._directory_path = tempfile.mkdtemp(**self._kwargs)
- return self._directory_path
-
- def __exit__(self, type, value, traceback):
- # Only self-delete if necessary.
-
- # FIXME: Should we delete non-empty directories?
- if os.path.exists(self._directory_path):
- os.rmdir(self._directory_path)
-
- return TemporaryDirectory(**kwargs)
-
- def maybe_make_directory(self, *path):
- """Create the specified directory if it doesn't already exist."""
- try:
- os.makedirs(os.path.join(*path))
- except OSError, e:
- if e.errno != errno.EEXIST:
- raise
-
- def read_binary_file(self, path):
- """Return the contents of the file at the given path as a byte string."""
- with file(path, 'rb') as f:
- return f.read()
-
- def read_text_file(self, path):
- """Return the contents of the file at the given path as a Unicode string.
-
- The file is read assuming it is a UTF-8 encoded file with no BOM."""
- with codecs.open(path, 'r', 'utf8') as f:
- return f.read()
-
- def write_binary_file(self, path, contents):
- """Write the contents to the file at the given location."""
- with file(path, 'wb') as f:
- f.write(contents)
-
- def write_text_file(self, path, contents):
- """Write the contents to the file at the given location.
-
- The file is written encoded as UTF-8 with no BOM."""
- with codecs.open(path, 'w', 'utf8') as f:
- f.write(contents)
diff --git a/WebKitTools/Scripts/webkitpy/common/system/filesystem_mock.py b/WebKitTools/Scripts/webkitpy/common/system/filesystem_mock.py
deleted file mode 100644
index 4e6d3da..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/filesystem_mock.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# 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
-# 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 errno
-import os
-import path
-
-
-class MockFileSystem(object):
- def __init__(self, files={}):
- """Initializes a "mock" filesystem that can be used to completely
- stub out a filesystem.
-
- Args:
- files: a dict of filenames -> file contents. A file contents
- value of None is used to indicate that the file should
- not exist.
- """
- self.files = files
-
- def exists(self, path):
- if path in self.files:
- return self.files[path] is not None
- return False
-
- def join(self, *comps):
- # os.path.join ignores trailing slashes on components (i.e.
- # join('foo/', 'bar') and join('foo', 'bar') produce the same result),
- # we emulate that behavior.
- trimmed_comps = []
- for comp in comps:
- if len(comp) and comp[-1] == '/':
- trimmed_comps.append(comp[0:-1])
- else:
- trimmed_comps.append(comp)
- return '/'.join(trimmed_comps)
-
- def maybe_make_directory(self, *path):
- # FIXME: Implement such that subsequent calls to isdir() work?
- pass
-
- def read_text_file(self, path):
- return self.read_binary_file(path)
-
- def read_binary_file(self, path):
- if path in self.files:
- if self.files[path] is None:
- raise IOError(errno.ENOENT, path, os.strerror(errno.ENOENT))
- return self.files[path]
-
- def write_text_file(self, path, contents):
- return self.write_binary_file(path, contents)
-
- def write_binary_file(self, path, contents):
- self.files[path] = contents
diff --git a/WebKitTools/Scripts/webkitpy/common/system/filesystem_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/filesystem_unittest.py
deleted file mode 100644
index 95684b7..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/filesystem_unittest.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# vim: set fileencoding=utf-8 :
-# 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.
-
-# NOTE: The fileencoding comment on the first line of the file is
-# important; without it, Python will choke while trying to parse the file,
-# since it includes non-ASCII characters.
-
-from __future__ import with_statement
-
-import os
-import stat
-import sys
-import tempfile
-import unittest
-
-from filesystem import FileSystem
-
-
-class FileSystemTest(unittest.TestCase):
- def setUp(self):
- self._this_dir = os.path.dirname(os.path.abspath(__file__))
- self._missing_file = os.path.join(self._this_dir, 'missing_file.py')
- self._this_file = os.path.join(self._this_dir, 'filesystem_unittest.py')
-
- def test_exists__true(self):
- fs = FileSystem()
- self.assertTrue(fs.exists(self._this_file))
-
- def test_exists__false(self):
- fs = FileSystem()
- self.assertFalse(fs.exists(self._missing_file))
-
- def test_isdir__true(self):
- fs = FileSystem()
- self.assertTrue(fs.isdir(self._this_dir))
-
- def test_isdir__false(self):
- fs = FileSystem()
- self.assertFalse(fs.isdir(self._this_file))
-
- def test_join(self):
- fs = FileSystem()
- self.assertEqual(fs.join('foo', 'bar'),
- os.path.join('foo', 'bar'))
-
- def test_listdir(self):
- fs = FileSystem()
- with fs.mkdtemp(prefix='filesystem_unittest_') as d:
- self.assertEqual(fs.listdir(d), [])
- new_file = os.path.join(d, 'foo')
- fs.write_text_file(new_file, u'foo')
- self.assertEqual(fs.listdir(d), ['foo'])
- os.remove(new_file)
-
- def test_maybe_make_directory__success(self):
- fs = FileSystem()
-
- with fs.mkdtemp(prefix='filesystem_unittest_') as base_path:
- sub_path = os.path.join(base_path, "newdir")
- self.assertFalse(os.path.exists(sub_path))
- self.assertFalse(fs.isdir(sub_path))
-
- fs.maybe_make_directory(sub_path)
- self.assertTrue(os.path.exists(sub_path))
- self.assertTrue(fs.isdir(sub_path))
-
- # Make sure we can re-create it.
- fs.maybe_make_directory(sub_path)
- self.assertTrue(os.path.exists(sub_path))
- self.assertTrue(fs.isdir(sub_path))
-
- # Clean up.
- os.rmdir(sub_path)
-
- self.assertFalse(os.path.exists(base_path))
- self.assertFalse(fs.isdir(base_path))
-
- def test_maybe_make_directory__failure(self):
- # FIXME: os.chmod() doesn't work on Windows to set directories
- # as readonly, so we skip this test for now.
- if sys.platform in ('win32', 'cygwin'):
- return
-
- fs = FileSystem()
- with fs.mkdtemp(prefix='filesystem_unittest_') as d:
- # Remove write permissions on the parent directory.
- os.chmod(d, stat.S_IRUSR)
-
- # Now try to create a sub directory - should fail.
- sub_dir = fs.join(d, 'subdir')
- self.assertRaises(OSError, fs.maybe_make_directory, sub_dir)
-
- # Clean up in case the test failed and we did create the
- # directory.
- if os.path.exists(sub_dir):
- os.rmdir(sub_dir)
-
- def test_read_and_write_file(self):
- fs = FileSystem()
- text_path = None
- binary_path = None
-
- unicode_text_string = u'Ūnĭcōde̽'
- hex_equivalent = '\xC5\xAA\x6E\xC4\xAD\x63\xC5\x8D\x64\x65\xCC\xBD'
- try:
- text_path = tempfile.mktemp(prefix='tree_unittest_')
- binary_path = tempfile.mktemp(prefix='tree_unittest_')
- fs.write_text_file(text_path, unicode_text_string)
- contents = fs.read_binary_file(text_path)
- self.assertEqual(contents, hex_equivalent)
-
- fs.write_text_file(binary_path, hex_equivalent)
- text_contents = fs.read_text_file(binary_path)
- self.assertEqual(text_contents, unicode_text_string)
- except:
- if text_path:
- os.remove(text_path)
- if binary_path:
- os.remove(binary_path)
-
- def test_read_binary_file__missing(self):
- fs = FileSystem()
- self.assertRaises(IOError, fs.read_binary_file, self._missing_file)
-
- def test_read_text_file__missing(self):
- fs = FileSystem()
- self.assertRaises(IOError, fs.read_text_file, self._missing_file)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/system/fileutils.py b/WebKitTools/Scripts/webkitpy/common/system/fileutils.py
deleted file mode 100644
index 55821f8..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/fileutils.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2010 Apple 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.
-#
-# 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.
-
-import sys
-
-
-def make_stdout_binary():
- """Puts sys.stdout into binary mode (on platforms that have a distinction
- between text and binary mode)."""
- if sys.platform != 'win32' or not hasattr(sys.stdout, 'fileno'):
- return
- import msvcrt
- import os
- msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
diff --git a/WebKitTools/Scripts/webkitpy/common/system/logtesting.py b/WebKitTools/Scripts/webkitpy/common/system/logtesting.py
deleted file mode 100644
index e361cb5..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/logtesting.py
+++ /dev/null
@@ -1,258 +0,0 @@
-# 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.
-
-"""Supports the unit-testing of logging code.
-
-Provides support for unit-testing messages logged using the built-in
-logging module.
-
-Inherit from the LoggingTestCase class for basic testing needs. For
-more advanced needs (e.g. unit-testing methods that configure logging),
-see the TestLogStream class, and perhaps also the LogTesting class.
-
-"""
-
-import logging
-import unittest
-
-
-class TestLogStream(object):
-
- """Represents a file-like object for unit-testing logging.
-
- This is meant for passing to the logging.StreamHandler constructor.
- Log messages captured by instances of this object can be tested
- using self.assertMessages() below.
-
- """
-
- def __init__(self, test_case):
- """Create an instance.
-
- Args:
- test_case: A unittest.TestCase instance.
-
- """
- self._test_case = test_case
- self.messages = []
- """A list of log messages written to the stream."""
-
- # Python documentation says that any object passed to the StreamHandler
- # constructor should support write() and flush():
- #
- # http://docs.python.org/library/logging.html#module-logging.handlers
- def write(self, message):
- self.messages.append(message)
-
- def flush(self):
- pass
-
- def assertMessages(self, messages):
- """Assert that the given messages match the logged messages.
-
- messages: A list of log message strings.
-
- """
- self._test_case.assertEquals(messages, self.messages)
-
-
-class LogTesting(object):
-
- """Supports end-to-end unit-testing of log messages.
-
- Sample usage:
-
- class SampleTest(unittest.TestCase):
-
- def setUp(self):
- self._log = LogTesting.setUp(self) # Turn logging on.
-
- def tearDown(self):
- self._log.tearDown() # Turn off and reset logging.
-
- def test_logging_in_some_method(self):
- call_some_method() # Contains calls to _log.info(), etc.
-
- # Check the resulting log messages.
- self._log.assertMessages(["INFO: expected message #1",
- "WARNING: expected message #2"])
-
- """
-
- def __init__(self, test_stream, handler):
- """Create an instance.
-
- This method should never be called directly. Instances should
- instead be created using the static setUp() method.
-
- Args:
- test_stream: A TestLogStream instance.
- handler: The handler added to the logger.
-
- """
- self._test_stream = test_stream
- self._handler = handler
-
- @staticmethod
- def _getLogger():
- """Return the logger being tested."""
- # It is possible we might want to return something other than
- # the root logger in some special situation. For now, the
- # root logger seems to suffice.
- return logging.getLogger()
-
- @staticmethod
- def setUp(test_case, logging_level=logging.INFO):
- """Configure logging for unit testing.
-
- Configures the root logger to log to a testing log stream.
- Only messages logged at or above the given level are logged
- to the stream. Messages logged to the stream are formatted
- in the following way, for example--
-
- "INFO: This is a test log message."
-
- This method should normally be called in the setUp() method
- of a unittest.TestCase. See the docstring of this class
- for more details.
-
- Returns:
- A LogTesting instance.
-
- Args:
- test_case: A unittest.TestCase instance.
- logging_level: An integer logging level that is the minimum level
- of log messages you would like to test.
-
- """
- stream = TestLogStream(test_case)
- handler = logging.StreamHandler(stream)
- handler.setLevel(logging_level)
- formatter = logging.Formatter("%(levelname)s: %(message)s")
- handler.setFormatter(formatter)
-
- # Notice that we only change the root logger by adding a handler
- # to it. In particular, we do not reset its level using
- # logger.setLevel(). This ensures that we have not interfered
- # with how the code being tested may have configured the root
- # logger.
- logger = LogTesting._getLogger()
- logger.addHandler(handler)
-
- return LogTesting(stream, handler)
-
- def tearDown(self):
- """Assert there are no remaining log messages, and reset logging.
-
- This method asserts that there are no more messages in the array of
- log messages, and then restores logging to its original state.
- This method should normally be called in the tearDown() method of a
- unittest.TestCase. See the docstring of this class for more details.
-
- """
- self.assertMessages([])
- logger = LogTesting._getLogger()
- logger.removeHandler(self._handler)
-
- def messages(self):
- """Return the current list of log messages."""
- return self._test_stream.messages
-
- # FIXME: Add a clearMessages() method for cases where the caller
- # deliberately doesn't want to assert every message.
-
- # We clear the log messages after asserting since they are no longer
- # needed after asserting. This serves two purposes: (1) it simplifies
- # the calling code when we want to check multiple logging calls in a
- # single test method, and (2) it lets us check in the tearDown() method
- # that there are no remaining log messages to be asserted.
- #
- # The latter ensures that no extra log messages are getting logged that
- # the caller might not be aware of or may have forgotten to check for.
- # This gets us a bit more mileage out of our tests without writing any
- # additional code.
- def assertMessages(self, messages):
- """Assert the current array of log messages, and clear its contents.
-
- Args:
- messages: A list of log message strings.
-
- """
- try:
- self._test_stream.assertMessages(messages)
- finally:
- # We want to clear the array of messages even in the case of
- # an Exception (e.g. an AssertionError). Otherwise, another
- # AssertionError can occur in the tearDown() because the
- # array might not have gotten emptied.
- self._test_stream.messages = []
-
-
-# This class needs to inherit from unittest.TestCase. Otherwise, the
-# setUp() and tearDown() methods will not get fired for test case classes
-# that inherit from this class -- even if the class inherits from *both*
-# unittest.TestCase and LoggingTestCase.
-#
-# FIXME: Rename this class to LoggingTestCaseBase to be sure that
-# the unittest module does not interpret this class as a unittest
-# test case itself.
-class LoggingTestCase(unittest.TestCase):
-
- """Supports end-to-end unit-testing of log messages.
-
- Sample usage:
-
- class SampleTest(LoggingTestCase):
-
- def test_logging_in_some_method(self):
- call_some_method() # Contains calls to _log.info(), etc.
-
- # Check the resulting log messages.
- self.assertLog(["INFO: expected message #1",
- "WARNING: expected message #2"])
-
- """
-
- def setUp(self):
- self._log = LogTesting.setUp(self)
-
- def tearDown(self):
- self._log.tearDown()
-
- def logMessages(self):
- """Return the current list of log messages."""
- return self._log.messages()
-
- # FIXME: Add a clearMessages() method for cases where the caller
- # deliberately doesn't want to assert every message.
-
- # See the code comments preceding LogTesting.assertMessages() for
- # an explanation of why we clear the array of messages after
- # asserting its contents.
- def assertLog(self, messages):
- """Assert the current array of log messages, and clear its contents.
-
- Args:
- messages: A list of log message strings.
-
- """
- self._log.assertMessages(messages)
diff --git a/WebKitTools/Scripts/webkitpy/common/system/logutils.py b/WebKitTools/Scripts/webkitpy/common/system/logutils.py
deleted file mode 100644
index cd4e60f..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/logutils.py
+++ /dev/null
@@ -1,207 +0,0 @@
-# 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.
-
-"""Supports webkitpy logging."""
-
-# FIXME: Move this file to webkitpy/python24 since logging needs to
-# be configured prior to running version-checking code.
-
-import logging
-import os
-import sys
-
-import webkitpy
-
-
-_log = logging.getLogger(__name__)
-
-# We set these directory paths lazily in get_logger() below.
-_scripts_dir = ""
-"""The normalized, absolute path to the ...Scripts directory."""
-
-_webkitpy_dir = ""
-"""The normalized, absolute path to the ...Scripts/webkitpy directory."""
-
-
-def _normalize_path(path):
- """Return the given path normalized.
-
- Converts a path to an absolute path, removes any trailing slashes,
- removes any extension, and lower-cases it.
-
- """
- path = os.path.abspath(path)
- path = os.path.normpath(path)
- path = os.path.splitext(path)[0] # Remove the extension, if any.
- path = path.lower()
-
- return path
-
-
-# Observe that the implementation of this function does not require
-# the use of any hard-coded strings like "webkitpy", etc.
-#
-# The main benefit this function has over using--
-#
-# _log = logging.getLogger(__name__)
-#
-# is that get_logger() returns the same value even if __name__ is
-# "__main__" -- i.e. even if the module is the script being executed
-# from the command-line.
-def get_logger(path):
- """Return a logging.logger for the given path.
-
- Returns:
- A logger whose name is the name of the module corresponding to
- the given path. If the module is in webkitpy, the name is
- the fully-qualified dotted module name beginning with webkitpy....
- Otherwise, the name is the base name of the module (i.e. without
- any dotted module name prefix).
-
- Args:
- path: The path of the module. Normally, this parameter should be
- the __file__ variable of the module.
-
- Sample usage:
-
- import webkitpy.common.system.logutils as logutils
-
- _log = logutils.get_logger(__file__)
-
- """
- # Since we assign to _scripts_dir and _webkitpy_dir in this function,
- # we need to declare them global.
- global _scripts_dir
- global _webkitpy_dir
-
- path = _normalize_path(path)
-
- # Lazily evaluate _webkitpy_dir and _scripts_dir.
- if not _scripts_dir:
- # The normalized, absolute path to ...Scripts/webkitpy/__init__.
- webkitpy_path = _normalize_path(webkitpy.__file__)
-
- _webkitpy_dir = os.path.split(webkitpy_path)[0]
- _scripts_dir = os.path.split(_webkitpy_dir)[0]
-
- if path.startswith(_webkitpy_dir):
- # Remove the initial Scripts directory portion, so the path
- # starts with /webkitpy, for example "/webkitpy/init/logutils".
- path = path[len(_scripts_dir):]
-
- parts = []
- while True:
- (path, tail) = os.path.split(path)
- if not tail:
- break
- parts.insert(0, tail)
-
- logger_name = ".".join(parts) # For example, webkitpy.common.system.logutils.
- else:
- # The path is outside of webkitpy. Default to the basename
- # without the extension.
- basename = os.path.basename(path)
- logger_name = os.path.splitext(basename)[0]
-
- return logging.getLogger(logger_name)
-
-
-def _default_handlers(stream):
- """Return a list of the default logging handlers to use.
-
- Args:
- stream: See the configure_logging() docstring.
-
- """
- # Create the filter.
- def should_log(record):
- """Return whether a logging.LogRecord should be logged."""
- # FIXME: Enable the logging of autoinstall messages once
- # autoinstall is adjusted. Currently, autoinstall logs
- # INFO messages when importing already-downloaded packages,
- # which is too verbose.
- if record.name.startswith("webkitpy.thirdparty.autoinstall"):
- return False
- return True
-
- logging_filter = logging.Filter()
- logging_filter.filter = should_log
-
- # Create the handler.
- handler = logging.StreamHandler(stream)
- formatter = logging.Formatter("%(name)s: [%(levelname)s] %(message)s")
- handler.setFormatter(formatter)
- handler.addFilter(logging_filter)
-
- return [handler]
-
-
-def configure_logging(logging_level=None, logger=None, stream=None,
- handlers=None):
- """Configure logging for standard purposes.
-
- Returns:
- A list of references to the logging handlers added to the root
- logger. This allows the caller to later remove the handlers
- using logger.removeHandler. This is useful primarily during unit
- testing where the caller may want to configure logging temporarily
- and then undo the configuring.
-
- Args:
- logging_level: The minimum logging level to log. Defaults to
- logging.INFO.
- logger: A logging.logger instance to configure. This parameter
- should be used only in unit tests. Defaults to the
- root logger.
- stream: A file-like object to which to log used in creating the default
- handlers. The stream must define an "encoding" data attribute,
- or else logging raises an error. Defaults to sys.stderr.
- handlers: A list of logging.Handler instances to add to the logger
- being configured. If this parameter is provided, then the
- stream parameter is not used.
-
- """
- # If the stream does not define an "encoding" data attribute, the
- # logging module can throw an error like the following:
- #
- # Traceback (most recent call last):
- # File "/System/Library/Frameworks/Python.framework/Versions/2.6/...
- # lib/python2.6/logging/__init__.py", line 761, in emit
- # self.stream.write(fs % msg.encode(self.stream.encoding))
- # LookupError: unknown encoding: unknown
- if logging_level is None:
- logging_level = logging.INFO
- if logger is None:
- logger = logging.getLogger()
- if stream is None:
- stream = sys.stderr
- if handlers is None:
- handlers = _default_handlers(stream)
-
- logger.setLevel(logging_level)
-
- for handler in handlers:
- logger.addHandler(handler)
-
- _log.debug("Debug logging enabled.")
-
- return handlers
diff --git a/WebKitTools/Scripts/webkitpy/common/system/logutils_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/logutils_unittest.py
deleted file mode 100644
index a4a6496..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/logutils_unittest.py
+++ /dev/null
@@ -1,142 +0,0 @@
-# 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 for logutils.py."""
-
-import logging
-import os
-import unittest
-
-from webkitpy.common.system.logtesting import LogTesting
-from webkitpy.common.system.logtesting import TestLogStream
-import webkitpy.common.system.logutils as logutils
-
-
-class GetLoggerTest(unittest.TestCase):
-
- """Tests get_logger()."""
-
- def test_get_logger_in_webkitpy(self):
- logger = logutils.get_logger(__file__)
- self.assertEquals(logger.name, "webkitpy.common.system.logutils_unittest")
-
- def test_get_logger_not_in_webkitpy(self):
- # Temporarily change the working directory so that we
- # can test get_logger() for a path outside of webkitpy.
- working_directory = os.getcwd()
- root_dir = "/"
- os.chdir(root_dir)
-
- logger = logutils.get_logger("/WebKitTools/Scripts/test-webkitpy")
- self.assertEquals(logger.name, "test-webkitpy")
-
- logger = logutils.get_logger("/WebKitTools/Scripts/test-webkitpy.py")
- self.assertEquals(logger.name, "test-webkitpy")
-
- os.chdir(working_directory)
-
-
-class ConfigureLoggingTestBase(unittest.TestCase):
-
- """Base class for configure_logging() unit tests."""
-
- def _logging_level(self):
- raise Exception("Not implemented.")
-
- def setUp(self):
- log_stream = TestLogStream(self)
-
- # Use a logger other than the root logger or one prefixed with
- # "webkitpy." so as not to conflict with test-webkitpy logging.
- logger = logging.getLogger("unittest")
-
- # Configure the test logger not to pass messages along to the
- # root logger. This prevents test messages from being
- # propagated to loggers used by test-webkitpy logging (e.g.
- # the root logger).
- logger.propagate = False
-
- logging_level = self._logging_level()
- self._handlers = logutils.configure_logging(logging_level=logging_level,
- logger=logger,
- stream=log_stream)
- self._log = logger
- self._log_stream = log_stream
-
- def tearDown(self):
- """Reset logging to its original state.
-
- This method ensures that the logging configuration set up
- for a unit test does not affect logging in other unit tests.
-
- """
- logger = self._log
- for handler in self._handlers:
- logger.removeHandler(handler)
-
- def _assert_log_messages(self, messages):
- """Assert that the logged messages equal the given messages."""
- self._log_stream.assertMessages(messages)
-
-
-class ConfigureLoggingTest(ConfigureLoggingTestBase):
-
- """Tests configure_logging() with the default logging level."""
-
- def _logging_level(self):
- return None
-
- def test_info_message(self):
- self._log.info("test message")
- self._assert_log_messages(["unittest: [INFO] test message\n"])
-
- def test_below_threshold_message(self):
- # We test the boundary case of a logging level equal to 19.
- # In practice, we will probably only be calling log.debug(),
- # which corresponds to a logging level of 10.
- level = logging.INFO - 1 # Equals 19.
- self._log.log(level, "test message")
- self._assert_log_messages([])
-
- def test_two_messages(self):
- self._log.info("message1")
- self._log.info("message2")
- self._assert_log_messages(["unittest: [INFO] message1\n",
- "unittest: [INFO] message2\n"])
-
-
-class ConfigureLoggingCustomLevelTest(ConfigureLoggingTestBase):
-
- """Tests configure_logging() with a custom logging level."""
-
- _level = 36
-
- def _logging_level(self):
- return self._level
-
- def test_logged_message(self):
- self._log.log(self._level, "test message")
- self._assert_log_messages(["unittest: [Level 36] test message\n"])
-
- def test_below_threshold_message(self):
- self._log.log(self._level - 1, "test message")
- self._assert_log_messages([])
diff --git a/WebKitTools/Scripts/webkitpy/common/system/ospath.py b/WebKitTools/Scripts/webkitpy/common/system/ospath.py
deleted file mode 100644
index aed7a3d..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/ospath.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# 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.
-
-"""Contains a substitute for Python 2.6's os.path.relpath()."""
-
-import os
-
-
-# This function is a replacement for os.path.relpath(), which is only
-# available in Python 2.6:
-#
-# http://docs.python.org/library/os.path.html#os.path.relpath
-#
-# It should behave essentially the same as os.path.relpath(), except for
-# returning None on paths not contained in abs_start_path.
-def relpath(path, start_path, os_path_abspath=None):
- """Return a path relative to the given start path, or None.
-
- Returns None if the path is not contained in the directory start_path.
-
- Args:
- path: An absolute or relative path to convert to a relative path.
- start_path: The path relative to which the given path should be
- converted.
- os_path_abspath: A replacement function for unit testing. This
- function should strip trailing slashes just like
- os.path.abspath(). Defaults to os.path.abspath.
-
- """
- if os_path_abspath is None:
- os_path_abspath = os.path.abspath
-
- # Since os_path_abspath() calls os.path.normpath()--
- #
- # (see http://docs.python.org/library/os.path.html#os.path.abspath )
- #
- # it also removes trailing slashes and converts forward and backward
- # slashes to the preferred slash os.sep.
- start_path = os_path_abspath(start_path)
- path = os_path_abspath(path)
-
- if not path.lower().startswith(start_path.lower()):
- # Then path is outside the directory given by start_path.
- return None
-
- rel_path = path[len(start_path):]
-
- if not rel_path:
- # Then the paths are the same.
- pass
- elif rel_path[0] == os.sep:
- # It is probably sufficient to remove just the first character
- # since os.path.normpath() collapses separators, but we use
- # lstrip() just to be sure.
- rel_path = rel_path.lstrip(os.sep)
- else:
- # We are in the case typified by the following example:
- #
- # start_path = "/tmp/foo"
- # path = "/tmp/foobar"
- # rel_path = "bar"
- return None
-
- return rel_path
diff --git a/WebKitTools/Scripts/webkitpy/common/system/ospath_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/ospath_unittest.py
deleted file mode 100644
index 0493c68..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/ospath_unittest.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# 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 for ospath.py."""
-
-import os
-import unittest
-
-from webkitpy.common.system.ospath import relpath
-
-
-# Make sure the tests in this class are platform independent.
-class RelPathTest(unittest.TestCase):
-
- """Tests relpath()."""
-
- os_path_abspath = lambda self, path: path
-
- def _rel_path(self, path, abs_start_path):
- return relpath(path, abs_start_path, self.os_path_abspath)
-
- def test_same_path(self):
- rel_path = self._rel_path("WebKit", "WebKit")
- self.assertEquals(rel_path, "")
-
- def test_long_rel_path(self):
- start_path = "WebKit"
- expected_rel_path = os.path.join("test", "Foo.txt")
- path = os.path.join(start_path, expected_rel_path)
-
- rel_path = self._rel_path(path, start_path)
- self.assertEquals(expected_rel_path, rel_path)
-
- def test_none_rel_path(self):
- """Test _rel_path() with None return value."""
- start_path = "WebKit"
- path = os.path.join("other_dir", "foo.txt")
-
- rel_path = self._rel_path(path, start_path)
- self.assertTrue(rel_path is None)
-
- rel_path = self._rel_path("WebKitTools", "WebKit")
- self.assertTrue(rel_path is None)
diff --git a/WebKitTools/Scripts/webkitpy/common/system/outputcapture.py b/WebKitTools/Scripts/webkitpy/common/system/outputcapture.py
deleted file mode 100644
index 45e0e3f..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/outputcapture.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# 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
-# 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.
-#
-# Class for unittest support. Used for capturing stderr/stdout.
-
-import sys
-import unittest
-from StringIO import StringIO
-
-class OutputCapture(object):
- def __init__(self):
- self.saved_outputs = dict()
-
- def _capture_output_with_name(self, output_name):
- self.saved_outputs[output_name] = getattr(sys, output_name)
- captured_output = StringIO()
- setattr(sys, output_name, captured_output)
- return captured_output
-
- def _restore_output_with_name(self, output_name):
- captured_output = getattr(sys, output_name).getvalue()
- setattr(sys, output_name, self.saved_outputs[output_name])
- del self.saved_outputs[output_name]
- return captured_output
-
- def capture_output(self):
- return (self._capture_output_with_name("stdout"), self._capture_output_with_name("stderr"))
-
- def restore_output(self):
- return (self._restore_output_with_name("stdout"), self._restore_output_with_name("stderr"))
-
- def assert_outputs(self, testcase, function, args=[], kwargs={}, expected_stdout="", expected_stderr="", expected_exception=None):
- self.capture_output()
- if expected_exception:
- return_value = testcase.assertRaises(expected_exception, function, *args, **kwargs)
- else:
- return_value = function(*args, **kwargs)
- (stdout_string, stderr_string) = self.restore_output()
- testcase.assertEqual(stdout_string, expected_stdout)
- testcase.assertEqual(stderr_string, expected_stderr)
- # This is a little strange, but I don't know where else to return this information.
- return return_value
-
-
-class OutputCaptureTestCaseBase(unittest.TestCase):
- def setUp(self):
- unittest.TestCase.setUp(self)
- self.output_capture = OutputCapture()
- (self.__captured_stdout, self.__captured_stderr) = self.output_capture.capture_output()
-
- def tearDown(self):
- del self.__captured_stdout
- del self.__captured_stderr
- self.output_capture.restore_output()
- unittest.TestCase.tearDown(self)
-
- def assertStdout(self, expected_stdout):
- self.assertEquals(expected_stdout, self.__captured_stdout.getvalue())
-
- def assertStderr(self, expected_stderr):
- self.assertEquals(expected_stderr, self.__captured_stderr.getvalue())
diff --git a/WebKitTools/Scripts/webkitpy/common/system/path.py b/WebKitTools/Scripts/webkitpy/common/system/path.py
deleted file mode 100644
index 09787d7..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/path.py
+++ /dev/null
@@ -1,138 +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.
-
-"""generic routines to convert platform-specific paths to URIs."""
-from __future__ import with_statement
-
-import atexit
-import subprocess
-import sys
-import threading
-import urllib
-
-
-def abspath_to_uri(path, platform=None):
- """Converts a platform-specific absolute path to a file: URL."""
- if platform is None:
- platform = sys.platform
- return "file:" + _escape(_convert_path(path, platform))
-
-
-def cygpath(path):
- """Converts an absolute cygwin path to an absolute Windows path."""
- return _CygPath.convert_using_singleton(path)
-
-
-# Note that this object is not threadsafe and must only be called
-# from multiple threads under protection of a lock (as is done in cygpath())
-class _CygPath(object):
- """Manages a long-running 'cygpath' process for file conversion."""
- _lock = None
- _singleton = None
-
- @staticmethod
- def stop_cygpath_subprocess():
- if not _CygPath._lock:
- return
-
- with _CygPath._lock:
- if _CygPath._singleton:
- _CygPath._singleton.stop()
-
- @staticmethod
- def convert_using_singleton(path):
- if not _CygPath._lock:
- _CygPath._lock = threading.Lock()
-
- with _CygPath._lock:
- if not _CygPath._singleton:
- _CygPath._singleton = _CygPath()
- # Make sure the cygpath subprocess always gets shutdown cleanly.
- atexit.register(_CygPath.stop_cygpath_subprocess)
-
- return _CygPath._singleton.convert(path)
-
- def __init__(self):
- self._child_process = None
-
- def start(self):
- assert(self._child_process is None)
- args = ['cygpath', '-f', '-', '-wa']
- self._child_process = subprocess.Popen(args,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE)
-
- def is_running(self):
- if not self._child_process:
- return False
- return self._child_process.returncode is None
-
- def stop(self):
- if self._child_process:
- self._child_process.stdin.close()
- self._child_process.wait()
- self._child_process = None
-
- def convert(self, path):
- if not self.is_running():
- self.start()
- self._child_process.stdin.write("%s\r\n" % path)
- self._child_process.stdin.flush()
- windows_path = self._child_process.stdout.readline().rstrip()
- # Some versions of cygpath use lowercase drive letters while others
- # use uppercase. We always convert to uppercase for consistency.
- windows_path = '%s%s' % (windows_path[0].upper(), windows_path[1:])
- return windows_path
-
-
-def _escape(path):
- """Handle any characters in the path that should be escaped."""
- # FIXME: web browsers don't appear to blindly quote every character
- # when converting filenames to files. Instead of using urllib's default
- # rules, we allow a small list of other characters through un-escaped.
- # It's unclear if this is the best possible solution.
- return urllib.quote(path, safe='/+:')
-
-
-def _convert_path(path, platform):
- """Handles any os-specific path separators, mappings, etc."""
- if platform == 'win32':
- return _winpath_to_uri(path)
- if platform == 'cygwin':
- return _winpath_to_uri(cygpath(path))
- return _unixypath_to_uri(path)
-
-
-def _winpath_to_uri(path):
- """Converts a window absolute path to a file: URL."""
- return "///" + path.replace("\\", "/")
-
-
-def _unixypath_to_uri(path):
- """Converts a unix-style path to a file: URL."""
- return "//" + path
diff --git a/WebKitTools/Scripts/webkitpy/common/system/path_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/path_unittest.py
deleted file mode 100644
index 4dbd38a..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/path_unittest.py
+++ /dev/null
@@ -1,105 +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.
-
-import unittest
-import sys
-
-import path
-
-class AbspathTest(unittest.TestCase):
- def assertMatch(self, test_path, expected_uri,
- platform=None):
- if platform == 'cygwin' and sys.platform != 'cygwin':
- return
- self.assertEqual(path.abspath_to_uri(test_path, platform=platform),
- expected_uri)
-
- def test_abspath_to_uri_cygwin(self):
- if sys.platform != 'cygwin':
- return
-
- self.assertMatch('/cygdrive/c/foo/bar.html',
- 'file:///C:/foo/bar.html',
- platform='cygwin')
- self.assertEqual(path.abspath_to_uri('/cygdrive/c/foo/bar.html',
- platform='cygwin'),
- 'file:///C:/foo/bar.html')
-
- def test_abspath_to_uri_darwin(self):
- self.assertMatch('/foo/bar.html',
- 'file:///foo/bar.html',
- platform='darwin')
- self.assertEqual(path.abspath_to_uri("/foo/bar.html",
- platform='darwin'),
- "file:///foo/bar.html")
-
- def test_abspath_to_uri_linux2(self):
- self.assertMatch('/foo/bar.html',
- 'file:///foo/bar.html',
- platform='darwin')
- self.assertEqual(path.abspath_to_uri("/foo/bar.html",
- platform='linux2'),
- "file:///foo/bar.html")
-
- def test_abspath_to_uri_win(self):
- self.assertMatch('c:\\foo\\bar.html',
- 'file:///c:/foo/bar.html',
- platform='win32')
- self.assertEqual(path.abspath_to_uri("c:\\foo\\bar.html",
- platform='win32'),
- "file:///c:/foo/bar.html")
-
- def test_abspath_to_uri_escaping(self):
- self.assertMatch('/foo/bar + baz%?.html',
- 'file:///foo/bar%20+%20baz%25%3F.html',
- platform='darwin')
- self.assertMatch('/foo/bar + baz%?.html',
- 'file:///foo/bar%20+%20baz%25%3F.html',
- platform='linux2')
-
- # Note that you can't have '?' in a filename on windows.
- self.assertMatch('/cygdrive/c/foo/bar + baz%.html',
- 'file:///C:/foo/bar%20+%20baz%25.html',
- platform='cygwin')
-
- def test_stop_cygpath_subprocess(self):
- if sys.platform != 'cygwin':
- return
-
- # Call cygpath to ensure the subprocess is running.
- path.cygpath("/cygdrive/c/foo.txt")
- self.assertTrue(path._CygPath._singleton.is_running())
-
- # Stop it.
- path._CygPath.stop_cygpath_subprocess()
-
- # Ensure that it is stopped.
- self.assertFalse(path._CygPath._singleton.is_running())
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/system/user.py b/WebKitTools/Scripts/webkitpy/common/system/user.py
deleted file mode 100644
index 8917137..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/user.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# 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
-# 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 logging
-import os
-import re
-import shlex
-import subprocess
-import sys
-import webbrowser
-
-
-_log = logging.getLogger("webkitpy.common.system.user")
-
-
-try:
- import readline
-except ImportError:
- 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):
- DEFAULT_NO = 'n'
- DEFAULT_YES = 'y'
-
- # FIXME: These are @classmethods because bugzilla.py doesn't have a Tool object (thus no User instance).
- @classmethod
- def prompt(cls, message, repeat=1, raw_input=raw_input):
- response = None
- while (repeat and not response):
- repeat -= 1
- response = raw_input(message)
- return response
-
- @classmethod
- def prompt_with_list(cls, list_title, list_items, can_choose_multiple=False, raw_input=raw_input):
- print list_title
- i = 0
- for item in list_items:
- i += 1
- print "%2d. %s" % (i, item)
-
- # Loop until we get valid input
- while True:
- if can_choose_multiple:
- response = cls.prompt("Enter one or more numbers (comma-separated), or \"all\": ", raw_input=raw_input)
- if not response.strip() or response == "all":
- return list_items
- try:
- indices = [int(r) - 1 for r in re.split("\s*,\s*", response)]
- except ValueError, err:
- continue
- return [list_items[i] for i in indices]
- else:
- try:
- result = int(cls.prompt("Enter a number: ", raw_input=raw_input)) - 1
- except ValueError, err:
- continue
- return list_items[result]
-
- def edit(self, files):
- editor = os.environ.get("EDITOR") or "vi"
- args = shlex.split(editor)
- # Note: Not thread safe: http://bugs.python.org/issue2320
- subprocess.call(args + files)
-
- def _warn_if_application_is_xcode(self, edit_application):
- if "Xcode" in edit_application:
- print "Instead of using Xcode.app, consider using EDITOR=\"xed --wait\"."
-
- def edit_changelog(self, files):
- edit_application = os.environ.get("CHANGE_LOG_EDIT_APPLICATION")
- if edit_application and sys.platform == "darwin":
- # On Mac we support editing ChangeLogs using an application.
- args = shlex.split(edit_application)
- print "Using editor in the CHANGE_LOG_EDIT_APPLICATION environment variable."
- print "Please quit the editor application when done editing."
- self._warn_if_application_is_xcode(edit_application)
- subprocess.call(["open", "-W", "-n", "-a"] + args + files)
- return
- self.edit(files)
-
- def page(self, message):
- pager = os.environ.get("PAGER") or "less"
- try:
- # Note: Not thread safe: http://bugs.python.org/issue2320
- child_process = subprocess.Popen([pager], stdin=subprocess.PIPE)
- child_process.communicate(input=message)
- except IOError, e:
- pass
-
- def confirm(self, message=None, default=DEFAULT_YES, raw_input=raw_input):
- if not message:
- message = "Continue?"
- choice = {'y': 'Y/n', 'n': 'y/N'}[default]
- response = raw_input("%s [%s]: " % (message, choice))
- if not response:
- response = default
- return response.lower() == 'y'
-
- def can_open_url(self):
- try:
- webbrowser.get()
- return True
- except webbrowser.Error, e:
- return False
-
- def open_url(self, url):
- if not self.can_open_url():
- _log.warn("Failed to open %s" % url)
- webbrowser.open(url)
diff --git a/WebKitTools/Scripts/webkitpy/common/system/user_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/user_unittest.py
deleted file mode 100644
index 7ec9b34..0000000
--- a/WebKitTools/Scripts/webkitpy/common/system/user_unittest.py
+++ /dev/null
@@ -1,109 +0,0 @@
-# Copyright (C) 2010 Research in Motion Ltd. 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 Research in Motion Ltd. 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 unittest
-
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.common.system.user import User
-
-class UserTest(unittest.TestCase):
-
- example_user_response = "example user response"
-
- def test_prompt_repeat(self):
- self.repeatsRemaining = 2
- def mock_raw_input(message):
- self.repeatsRemaining -= 1
- if not self.repeatsRemaining:
- return UserTest.example_user_response
- return None
- self.assertEqual(User.prompt("input", repeat=self.repeatsRemaining, raw_input=mock_raw_input), UserTest.example_user_response)
-
- def test_prompt_when_exceeded_repeats(self):
- self.repeatsRemaining = 2
- def mock_raw_input(message):
- self.repeatsRemaining -= 1
- return None
- self.assertEqual(User.prompt("input", repeat=self.repeatsRemaining, raw_input=mock_raw_input), None)
-
- def test_prompt_with_list(self):
- def run_prompt_test(inputs, expected_result, can_choose_multiple=False):
- def mock_raw_input(message):
- return inputs.pop(0)
- output_capture = OutputCapture()
- actual_result = output_capture.assert_outputs(
- self,
- User.prompt_with_list,
- args=["title", ["foo", "bar"]],
- kwargs={"can_choose_multiple": can_choose_multiple, "raw_input": mock_raw_input},
- expected_stdout="title\n 1. foo\n 2. bar\n")
- self.assertEqual(actual_result, expected_result)
- self.assertEqual(len(inputs), 0)
-
- run_prompt_test(["1"], "foo")
- run_prompt_test(["badinput", "2"], "bar")
-
- run_prompt_test(["1,2"], ["foo", "bar"], can_choose_multiple=True)
- run_prompt_test([" 1, 2 "], ["foo", "bar"], can_choose_multiple=True)
- run_prompt_test(["all"], ["foo", "bar"], can_choose_multiple=True)
- run_prompt_test([""], ["foo", "bar"], can_choose_multiple=True)
- run_prompt_test([" "], ["foo", "bar"], can_choose_multiple=True)
- run_prompt_test(["badinput", "all"], ["foo", "bar"], can_choose_multiple=True)
-
- def test_confirm(self):
- test_cases = (
- (("Continue? [Y/n]: ", True), (User.DEFAULT_YES, 'y')),
- (("Continue? [Y/n]: ", False), (User.DEFAULT_YES, 'n')),
- (("Continue? [Y/n]: ", True), (User.DEFAULT_YES, '')),
- (("Continue? [Y/n]: ", False), (User.DEFAULT_YES, 'q')),
- (("Continue? [y/N]: ", True), (User.DEFAULT_NO, 'y')),
- (("Continue? [y/N]: ", False), (User.DEFAULT_NO, 'n')),
- (("Continue? [y/N]: ", False), (User.DEFAULT_NO, '')),
- (("Continue? [y/N]: ", False), (User.DEFAULT_NO, 'q')),
- )
- for test_case in test_cases:
- expected, inputs = test_case
-
- def mock_raw_input(message):
- self.assertEquals(expected[0], message)
- return inputs[1]
-
- result = User().confirm(default=inputs[0],
- raw_input=mock_raw_input)
- self.assertEquals(expected[1], result)
-
- def test_warn_if_application_is_xcode(self):
- output = OutputCapture()
- user = User()
- output.assert_outputs(self, user._warn_if_application_is_xcode, ["TextMate"])
- output.assert_outputs(self, user._warn_if_application_is_xcode, ["/Applications/TextMate.app"])
- output.assert_outputs(self, user._warn_if_application_is_xcode, ["XCode"]) # case sensitive matching
-
- xcode_warning = "Instead of using Xcode.app, consider using EDITOR=\"xed --wait\".\n"
- output.assert_outputs(self, user._warn_if_application_is_xcode, ["Xcode"], expected_stdout=xcode_warning)
- output.assert_outputs(self, user._warn_if_application_is_xcode, ["/Developer/Applications/Xcode.app"], expected_stdout=xcode_warning)
diff --git a/WebKitTools/Scripts/webkitpy/common/thread/__init__.py b/WebKitTools/Scripts/webkitpy/common/thread/__init__.py
deleted file mode 100644
index ef65bee..0000000
--- a/WebKitTools/Scripts/webkitpy/common/thread/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/common/thread/messagepump.py b/WebKitTools/Scripts/webkitpy/common/thread/messagepump.py
deleted file mode 100644
index 0e39285..0000000
--- a/WebKitTools/Scripts/webkitpy/common/thread/messagepump.py
+++ /dev/null
@@ -1,59 +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.
-
-
-class MessagePumpDelegate(object):
- def schedule(self, interval, callback):
- raise NotImplementedError, "subclasses must implement"
-
- def message_available(self, message):
- raise NotImplementedError, "subclasses must implement"
-
- def final_message_delivered(self):
- raise NotImplementedError, "subclasses must implement"
-
-
-class MessagePump(object):
- interval = 10 # seconds
-
- def __init__(self, delegate, message_queue):
- self._delegate = delegate
- self._message_queue = message_queue
- self._schedule()
-
- def _schedule(self):
- self._delegate.schedule(self.interval, self._callback)
-
- def _callback(self):
- (messages, is_running) = self._message_queue.take_all()
- for message in messages:
- self._delegate.message_available(message)
- if not is_running:
- self._delegate.final_message_delivered()
- return
- self._schedule()
diff --git a/WebKitTools/Scripts/webkitpy/common/thread/messagepump_unittest.py b/WebKitTools/Scripts/webkitpy/common/thread/messagepump_unittest.py
deleted file mode 100644
index f731db2..0000000
--- a/WebKitTools/Scripts/webkitpy/common/thread/messagepump_unittest.py
+++ /dev/null
@@ -1,83 +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.
-
-import unittest
-
-from webkitpy.common.thread.messagepump import MessagePump, MessagePumpDelegate
-from webkitpy.common.thread.threadedmessagequeue import ThreadedMessageQueue
-
-
-class TestDelegate(MessagePumpDelegate):
- def __init__(self):
- self.log = []
-
- def schedule(self, interval, callback):
- self.callback = callback
- self.log.append("schedule")
-
- def message_available(self, message):
- self.log.append("message_available: %s" % message)
-
- def final_message_delivered(self):
- self.log.append("final_message_delivered")
-
-
-class MessagePumpTest(unittest.TestCase):
-
- def test_basic(self):
- queue = ThreadedMessageQueue()
- delegate = TestDelegate()
- pump = MessagePump(delegate, queue)
- self.assertEqual(delegate.log, [
- 'schedule'
- ])
- delegate.callback()
- queue.post("Hello")
- queue.post("There")
- delegate.callback()
- self.assertEqual(delegate.log, [
- 'schedule',
- 'schedule',
- 'message_available: Hello',
- 'message_available: There',
- 'schedule'
- ])
- queue.post("More")
- queue.post("Messages")
- queue.stop()
- delegate.callback()
- self.assertEqual(delegate.log, [
- 'schedule',
- 'schedule',
- 'message_available: Hello',
- 'message_available: There',
- 'schedule',
- 'message_available: More',
- 'message_available: Messages',
- 'final_message_delivered'
- ])
diff --git a/WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue.py b/WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue.py
deleted file mode 100644
index 17b6277..0000000
--- a/WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue.py
+++ /dev/null
@@ -1,54 +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.
-
-from __future__ import with_statement
-
-import threading
-
-
-class ThreadedMessageQueue(object):
- def __init__(self):
- self._messages = []
- self._is_running = True
- self._lock = threading.Lock()
-
- def post(self, message):
- with self._lock:
- self._messages.append(message)
-
- def stop(self):
- with self._lock:
- self._is_running = False
-
- def take_all(self):
- with self._lock:
- messages = self._messages
- is_running = self._is_running
- self._messages = []
- return (messages, is_running)
-
diff --git a/WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue_unittest.py b/WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue_unittest.py
deleted file mode 100644
index cb67c1e..0000000
--- a/WebKitTools/Scripts/webkitpy/common/thread/threadedmessagequeue_unittest.py
+++ /dev/null
@@ -1,53 +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.
-
-import unittest
-
-from webkitpy.common.thread.threadedmessagequeue import ThreadedMessageQueue
-
-class ThreadedMessageQueueTest(unittest.TestCase):
-
- def test_basic(self):
- queue = ThreadedMessageQueue()
- queue.post("Hello")
- queue.post("There")
- (messages, is_running) = queue.take_all()
- self.assertEqual(messages, ["Hello", "There"])
- self.assertTrue(is_running)
- (messages, is_running) = queue.take_all()
- self.assertEqual(messages, [])
- self.assertTrue(is_running)
- queue.post("More")
- queue.stop()
- queue.post("Messages")
- (messages, is_running) = queue.take_all()
- self.assertEqual(messages, ["More", "Messages"])
- self.assertFalse(is_running)
- (messages, is_running) = queue.take_all()
- self.assertEqual(messages, [])
- self.assertFalse(is_running)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/__init__.py b/WebKitTools/Scripts/webkitpy/layout_tests/__init__.py
deleted file mode 100644
index ef65bee..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py
deleted file mode 100644
index 51dcac8..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py
+++ /dev/null
@@ -1,231 +0,0 @@
-#!/usr/bin/env 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:
-#
-# 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 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.
-
-"""deduplicate_tests -- lists duplicated between platforms.
-
-If platform/mac-leopard is missing an expected test output, we fall back on
-platform/mac. This means it's possible to grow redundant test outputs,
-where we have the same expected data in both a platform directory and another
-platform it falls back on.
-"""
-
-import collections
-import fnmatch
-import os
-import subprocess
-import sys
-import re
-import webkitpy.common.checkout.scm as scm
-import webkitpy.common.system.executive as executive
-import webkitpy.common.system.logutils as logutils
-import webkitpy.common.system.ospath as ospath
-import webkitpy.layout_tests.port.factory as port_factory
-
-_log = logutils.get_logger(__file__)
-
-_BASE_PLATFORM = 'base'
-
-
-def port_fallbacks():
- """Get the port fallback information.
- Returns:
- A dictionary mapping platform name to a list of other platforms to fall
- back on. All platforms fall back on 'base'.
- """
- fallbacks = {_BASE_PLATFORM: []}
- platform_dir = os.path.join(scm.find_checkout_root(), 'LayoutTests',
- 'platform')
- for port_name in os.listdir(platform_dir):
- try:
- platforms = port_factory.get(port_name).baseline_search_path()
- except NotImplementedError:
- _log.error("'%s' lacks baseline_search_path(), please fix."
- % port_name)
- fallbacks[port_name] = [_BASE_PLATFORM]
- continue
- fallbacks[port_name] = [os.path.basename(p) for p in platforms][1:]
- fallbacks[port_name].append(_BASE_PLATFORM)
- return fallbacks
-
-
-def parse_git_output(git_output, glob_pattern):
- """Parses the output of git ls-tree and filters based on glob_pattern.
- Args:
- git_output: result of git ls-tree -r HEAD LayoutTests.
- glob_pattern: a pattern to filter the files.
- Returns:
- A dictionary mapping (test name, hash of content) => [paths]
- """
- hashes = collections.defaultdict(set)
- for line in git_output.split('\n'):
- if not line:
- break
- attrs, path = line.strip().split('\t')
- if not fnmatch.fnmatch(path, glob_pattern):
- continue
- path = path[len('LayoutTests/'):]
- match = re.match(r'^(platform/.*?/)?(.*)', path)
- test = match.group(2)
- _, _, hash = attrs.split(' ')
- hashes[(test, hash)].add(path)
- return hashes
-
-
-def cluster_file_hashes(glob_pattern):
- """Get the hashes of all the test expectations in the tree.
- We cheat and use git's hashes.
- Args:
- glob_pattern: a pattern to filter the files.
- Returns:
- A dictionary mapping (test name, hash of content) => [paths]
- """
-
- # A map of file hash => set of all files with that hash.
- hashes = collections.defaultdict(set)
-
- # Fill in the map.
- cmd = ('git', 'ls-tree', '-r', 'HEAD', 'LayoutTests')
- try:
- git_output = executive.Executive().run_command(cmd,
- cwd=scm.find_checkout_root())
- except OSError, e:
- if e.errno == 2: # No such file or directory.
- _log.error("Error: 'No such file' when running git.")
- _log.error("This script requires git.")
- sys.exit(1)
- raise e
- return parse_git_output(git_output, glob_pattern)
-
-
-def extract_platforms(paths):
- """Extracts the platforms from a list of paths matching ^platform/(.*?)/.
- Args:
- paths: a list of paths.
- Returns:
- A dictionary containing all platforms from paths.
- """
- platforms = {}
- for path in paths:
- match = re.match(r'^platform/(.*?)/', path)
- if match:
- platform = match.group(1)
- else:
- platform = _BASE_PLATFORM
- platforms[platform] = path
- return platforms
-
-
-def has_intermediate_results(test, fallbacks, matching_platform,
- path_exists=os.path.exists):
- """Returns True if there is a test result that causes us to not delete
- this duplicate.
-
- For example, chromium-linux may be a duplicate of the checked in result,
- but chromium-win may have a different result checked in. In this case,
- we need to keep the duplicate results.
-
- Args:
- test: The test name.
- fallbacks: A list of platforms we fall back on.
- matching_platform: The platform that we found the duplicate test
- result. We can stop checking here.
- path_exists: Optional parameter that allows us to stub out
- os.path.exists for testing.
- """
- for platform in fallbacks:
- if platform == matching_platform:
- return False
- test_path = os.path.join('LayoutTests', 'platform', platform, test)
- if path_exists(test_path):
- return True
- return False
-
-
-def get_relative_test_path(filename, relative_to,
- checkout_root=scm.find_checkout_root()):
- """Constructs a relative path to |filename| from |relative_to|.
- Args:
- filename: The test file we're trying to get a relative path to.
- relative_to: The absolute path we're relative to.
- Returns:
- A relative path to filename or None if |filename| is not below
- |relative_to|.
- """
- layout_test_dir = os.path.join(checkout_root, 'LayoutTests')
- abs_path = os.path.join(layout_test_dir, filename)
- return ospath.relpath(abs_path, relative_to)
-
-
-def find_dups(hashes, port_fallbacks, relative_to):
- """Yields info about redundant test expectations.
- Args:
- hashes: a list of hashes as returned by cluster_file_hashes.
- port_fallbacks: a list of fallback information as returned by
- get_port_fallbacks.
- relative_to: the directory that we want the results relative to
- Returns:
- a tuple containing (test, platform, fallback, platforms)
- """
- for (test, hash), cluster in hashes.items():
- if len(cluster) < 2:
- continue # Common case: only one file with that hash.
-
- # Compute the list of platforms we have this particular hash for.
- platforms = extract_platforms(cluster)
- if len(platforms) == 1:
- continue
-
- # See if any of the platforms are redundant with each other.
- for platform in platforms.keys():
- for fallback in port_fallbacks[platform]:
- if fallback not in platforms.keys():
- continue
- # We have to verify that there isn't an intermediate result
- # that causes this duplicate hash to exist.
- if has_intermediate_results(test, port_fallbacks[platform],
- fallback):
- continue
- # We print the relative path so it's easy to pipe the results
- # to xargs rm.
- path = get_relative_test_path(platforms[platform], relative_to)
- if not path:
- continue
- yield {
- 'test': test,
- 'platform': platform,
- 'fallback': fallback,
- 'path': path,
- }
-
-
-def deduplicate(glob_pattern):
- """Traverses LayoutTests and returns information about duplicated files.
- Args:
- glob pattern to filter the files in LayoutTests.
- Returns:
- a dictionary containing test, path, platform and fallback.
- """
- fallbacks = port_fallbacks()
- hashes = cluster_file_hashes(glob_pattern)
- return list(find_dups(hashes, fallbacks, os.getcwd()))
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py
deleted file mode 100644
index 309bf8d..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py
+++ /dev/null
@@ -1,210 +0,0 @@
-#!/usr/bin/env 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:
-#
-# 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 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.
-
-"""Unit tests for deduplicate_tests.py."""
-
-import deduplicate_tests
-import os
-import unittest
-import webkitpy.common.checkout.scm as scm
-
-
-class MockExecutive(object):
- last_run_command = []
- response = ''
-
- class Executive(object):
- def run_command(self,
- args,
- cwd=None,
- input=None,
- error_handler=None,
- return_exit_code=False,
- return_stderr=True,
- decode_output=True):
- MockExecutive.last_run_command += [args]
- return MockExecutive.response
-
-
-class ListDuplicatesTest(unittest.TestCase):
- def setUp(self):
- MockExecutive.last_run_command = []
- MockExecutive.response = ''
- deduplicate_tests.executive = MockExecutive
- self._original_cwd = os.getcwd()
- checkout_root = scm.find_checkout_root()
- self.assertNotEqual(checkout_root, None)
- os.chdir(checkout_root)
-
- def tearDown(self):
- os.chdir(self._original_cwd)
-
- def test_parse_git_output(self):
- git_output = (
- '100644 blob 5053240b3353f6eb39f7cb00259785f16d121df2\tLayoutTests/mac/foo-expected.txt\n'
- '100644 blob a004548d107ecc4e1ea08019daf0a14e8634a1ff\tLayoutTests/platform/chromium/foo-expected.txt\n'
- '100644 blob d6bb0bc762e3aec5df03b5c04485b2cb3b65ffb1\tLayoutTests/platform/chromium-linux/foo-expected.txt\n'
- '100644 blob abcdebc762e3aec5df03b5c04485b2cb3b65ffb1\tLayoutTests/platform/chromium-linux/animage.png\n'
- '100644 blob d6bb0bc762e3aec5df03b5c04485b2cb3b65ffb1\tLayoutTests/platform/chromium-win/foo-expected.txt\n'
- '100644 blob abcdebc762e3aec5df03b5c04485b2cb3b65ffb1\tLayoutTests/platform/chromium-win/animage.png\n'
- '100644 blob 4303df5389ca87cae83dd3236b8dd84e16606517\tLayoutTests/platform/mac/foo-expected.txt\n')
- hashes = deduplicate_tests.parse_git_output(git_output, '*')
- expected = {('mac/foo-expected.txt', '5053240b3353f6eb39f7cb00259785f16d121df2'): set(['mac/foo-expected.txt']),
- ('animage.png', 'abcdebc762e3aec5df03b5c04485b2cb3b65ffb1'): set(['platform/chromium-linux/animage.png', 'platform/chromium-win/animage.png']),
- ('foo-expected.txt', '4303df5389ca87cae83dd3236b8dd84e16606517'): set(['platform/mac/foo-expected.txt']),
- ('foo-expected.txt', 'd6bb0bc762e3aec5df03b5c04485b2cb3b65ffb1'): set(['platform/chromium-linux/foo-expected.txt', 'platform/chromium-win/foo-expected.txt']),
- ('foo-expected.txt', 'a004548d107ecc4e1ea08019daf0a14e8634a1ff'): set(['platform/chromium/foo-expected.txt'])}
- self.assertEquals(expected, hashes)
-
- hashes = deduplicate_tests.parse_git_output(git_output, '*.png')
- expected = {('animage.png', 'abcdebc762e3aec5df03b5c04485b2cb3b65ffb1'): set(['platform/chromium-linux/animage.png', 'platform/chromium-win/animage.png'])}
- self.assertEquals(expected, hashes)
-
- def test_extract_platforms(self):
- self.assertEquals({'foo': 'platform/foo/bar',
- 'zoo': 'platform/zoo/com'},
- deduplicate_tests.extract_platforms(['platform/foo/bar', 'platform/zoo/com']))
- self.assertEquals({'foo': 'platform/foo/bar',
- deduplicate_tests._BASE_PLATFORM: 'what/'},
- deduplicate_tests.extract_platforms(['platform/foo/bar', 'what/']))
-
- def test_has_intermediate_results(self):
- test_cases = (
- # If we found a duplicate in our first fallback, we have no
- # intermediate results.
- (False, ('fast/foo-expected.txt',
- ['chromium-win', 'chromium', 'base'],
- 'chromium-win',
- lambda path: True)),
- # Since chromium-win has a result, we have an intermediate result.
- (True, ('fast/foo-expected.txt',
- ['chromium-win', 'chromium', 'base'],
- 'chromium',
- lambda path: True)),
- # There are no intermediate results.
- (False, ('fast/foo-expected.txt',
- ['chromium-win', 'chromium', 'base'],
- 'chromium',
- lambda path: False)),
- # There are no intermediate results since a result for chromium is
- # our duplicate file.
- (False, ('fast/foo-expected.txt',
- ['chromium-win', 'chromium', 'base'],
- 'chromium',
- lambda path: path == 'LayoutTests/platform/chromium/fast/foo-expected.txt')),
- # We have an intermediate result in 'chromium' even though our
- # duplicate is with the file in 'base'.
- (True, ('fast/foo-expected.txt',
- ['chromium-win', 'chromium', 'base'],
- 'base',
- lambda path: path == 'LayoutTests/platform/chromium/fast/foo-expected.txt')),
- # We have an intermediate result in 'chromium-win' even though our
- # duplicate is in 'base'.
- (True, ('fast/foo-expected.txt',
- ['chromium-win', 'chromium', 'base'],
- 'base',
- lambda path: path == 'LayoutTests/platform/chromium-win/fast/foo-expected.txt')),
- )
- for expected, inputs in test_cases:
- self.assertEquals(expected,
- deduplicate_tests.has_intermediate_results(*inputs))
-
- def test_unique(self):
- MockExecutive.response = (
- '100644 blob 5053240b3353f6eb39f7cb00259785f16d121df2\tLayoutTests/mac/foo-expected.txt\n'
- '100644 blob a004548d107ecc4e1ea08019daf0a14e8634a1ff\tLayoutTests/platform/chromium/foo-expected.txt\n'
- '100644 blob abcd0bc762e3aec5df03b5c04485b2cb3b65ffb1\tLayoutTests/platform/chromium-linux/foo-expected.txt\n'
- '100644 blob d6bb0bc762e3aec5df03b5c04485b2cb3b65ffb1\tLayoutTests/platform/chromium-win/foo-expected.txt\n'
- '100644 blob 4303df5389ca87cae83dd3236b8dd84e16606517\tLayoutTests/platform/mac/foo-expected.txt\n')
- result = deduplicate_tests.deduplicate('*')
- self.assertEquals(1, len(MockExecutive.last_run_command))
- self.assertEquals(('git', 'ls-tree', '-r', 'HEAD', 'LayoutTests'), MockExecutive.last_run_command[-1])
- self.assertEquals(0, len(result))
-
- def test_duplicates(self):
- MockExecutive.response = (
- '100644 blob 5053240b3353f6eb39f7cb00259785f16d121df2\tLayoutTests/mac/foo-expected.txt\n'
- '100644 blob a004548d107ecc4e1ea08019daf0a14e8634a1ff\tLayoutTests/platform/chromium/foo-expected.txt\n'
- '100644 blob d6bb0bc762e3aec5df03b5c04485b2cb3b65ffb1\tLayoutTests/platform/chromium-linux/foo-expected.txt\n'
- '100644 blob abcdebc762e3aec5df03b5c04485b2cb3b65ffb1\tLayoutTests/platform/chromium-linux/animage.png\n'
- '100644 blob d6bb0bc762e3aec5df03b5c04485b2cb3b65ffb1\tLayoutTests/platform/chromium-win/foo-expected.txt\n'
- '100644 blob abcdebc762e3aec5df03b5c04485b2cb3b65ffb1\tLayoutTests/platform/chromium-win/animage.png\n'
- '100644 blob 4303df5389ca87cae83dd3236b8dd84e16606517\tLayoutTests/platform/mac/foo-expected.txt\n')
-
- result = deduplicate_tests.deduplicate('*')
- self.assertEquals(1, len(MockExecutive.last_run_command))
- self.assertEquals(('git', 'ls-tree', '-r', 'HEAD', 'LayoutTests'), MockExecutive.last_run_command[-1])
- self.assertEquals(2, len(result))
- self.assertEquals({'test': 'animage.png',
- 'path': 'LayoutTests/platform/chromium-linux/animage.png',
- 'fallback': 'chromium-win',
- 'platform': 'chromium-linux'},
- result[0])
- self.assertEquals({'test': 'foo-expected.txt',
- 'path': 'LayoutTests/platform/chromium-linux/foo-expected.txt',
- 'fallback': 'chromium-win',
- 'platform': 'chromium-linux'},
- result[1])
-
- result = deduplicate_tests.deduplicate('*.txt')
- self.assertEquals(2, len(MockExecutive.last_run_command))
- self.assertEquals(('git', 'ls-tree', '-r', 'HEAD', 'LayoutTests'), MockExecutive.last_run_command[-1])
- self.assertEquals(1, len(result))
- self.assertEquals({'test': 'foo-expected.txt',
- 'path': 'LayoutTests/platform/chromium-linux/foo-expected.txt',
- 'fallback': 'chromium-win',
- 'platform': 'chromium-linux'},
- result[0])
-
- result = deduplicate_tests.deduplicate('*.png')
- self.assertEquals(3, len(MockExecutive.last_run_command))
- self.assertEquals(('git', 'ls-tree', '-r', 'HEAD', 'LayoutTests'), MockExecutive.last_run_command[-1])
- self.assertEquals(1, len(result))
- self.assertEquals({'test': 'animage.png',
- 'path': 'LayoutTests/platform/chromium-linux/animage.png',
- 'fallback': 'chromium-win',
- 'platform': 'chromium-linux'},
- result[0])
-
- def test_get_relative_test_path(self):
- checkout_root = scm.find_checkout_root()
- layout_test_dir = os.path.join(checkout_root, 'LayoutTests')
- test_cases = (
- ('platform/mac/test.html',
- ('platform/mac/test.html', layout_test_dir)),
- ('LayoutTests/platform/mac/test.html',
- ('platform/mac/test.html', checkout_root)),
- (None,
- ('platform/mac/test.html', os.path.join(checkout_root, 'WebCore'))),
- ('test.html',
- ('platform/mac/test.html', os.path.join(layout_test_dir, 'platform/mac'))),
- (None,
- ('platform/mac/test.html', os.path.join(layout_test_dir, 'platform/win'))),
- )
- for expected, inputs in test_cases:
- self.assertEquals(expected,
- deduplicate_tests.get_relative_test_path(*inputs))
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/__init__.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/__init__.py
+++ /dev/null
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py
deleted file mode 100644
index fdb8da6..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py
+++ /dev/null
@@ -1,569 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2010 Google Inc. All rights reserved.
-# Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Szeged
-#
-# 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.
-
-"""A Thread object for running DumpRenderTree and processing URLs from a
-shared queue.
-
-Each thread runs a separate instance of the DumpRenderTree binary and validates
-the output. When there are no more URLs to process in the shared queue, the
-thread exits.
-"""
-
-from __future__ import with_statement
-
-import codecs
-import copy
-import logging
-import os
-import Queue
-import signal
-import sys
-import thread
-import threading
-import time
-
-
-from webkitpy.layout_tests.test_types import image_diff
-from webkitpy.layout_tests.test_types import test_type_base
-from webkitpy.layout_tests.test_types import text_diff
-
-import test_failures
-import test_output
-import test_results
-
-_log = logging.getLogger("webkitpy.layout_tests.layout_package."
- "dump_render_tree_thread")
-
-
-def _expected_test_output(port, filename):
- """Returns an expected TestOutput object."""
- return test_output.TestOutput(port.expected_text(filename),
- port.expected_image(filename),
- port.expected_checksum(filename))
-
-def _process_output(port, options, test_input, test_types, test_args,
- test_output, worker_name):
- """Receives the output from a DumpRenderTree process, subjects it to a
- number of tests, and returns a list of failure types the test produced.
-
- Args:
- port: port-specific hooks
- options: command line options argument from optparse
- proc: an active DumpRenderTree process
- test_input: Object containing the test filename and timeout
- test_types: list of test types to subject the output to
- test_args: arguments to be passed to each test
- test_output: a TestOutput object containing the output of the test
- worker_name: worker name for logging
-
- Returns: a TestResult object
- """
- failures = []
-
- if test_output.crash:
- failures.append(test_failures.FailureCrash())
- if test_output.timeout:
- failures.append(test_failures.FailureTimeout())
-
- test_name = port.relative_test_filename(test_input.filename)
- if test_output.crash:
- _log.debug("%s Stacktrace for %s:\n%s" % (worker_name, test_name,
- test_output.error))
- filename = os.path.join(options.results_directory, test_name)
- filename = os.path.splitext(filename)[0] + "-stack.txt"
- port.maybe_make_directory(os.path.split(filename)[0])
- with codecs.open(filename, "wb", "utf-8") as file:
- file.write(test_output.error)
- elif test_output.error:
- _log.debug("%s %s output stderr lines:\n%s" % (worker_name, test_name,
- test_output.error))
-
- expected_test_output = _expected_test_output(port, test_input.filename)
-
- # Check the output and save the results.
- start_time = time.time()
- time_for_diffs = {}
- for test_type in test_types:
- start_diff_time = time.time()
- new_failures = test_type.compare_output(port, test_input.filename,
- test_args, test_output,
- expected_test_output)
- # Don't add any more failures if we already have a crash, so we don't
- # double-report those tests. We do double-report for timeouts since
- # we still want to see the text and image output.
- if not test_output.crash:
- failures.extend(new_failures)
- time_for_diffs[test_type.__class__.__name__] = (
- time.time() - start_diff_time)
-
- total_time_for_all_diffs = time.time() - start_diff_time
- return test_results.TestResult(test_input.filename, failures, test_output.test_time,
- total_time_for_all_diffs, time_for_diffs)
-
-
-def _pad_timeout(timeout):
- """Returns a safe multiple of the per-test timeout value to use
- to detect hung test threads.
-
- """
- # When we're running one test per DumpRenderTree process, we can
- # enforce a hard timeout. The DumpRenderTree watchdog uses 2.5x
- # the timeout; we want to be larger than that.
- return timeout * 3
-
-
-def _milliseconds_to_seconds(msecs):
- return float(msecs) / 1000.0
-
-
-def _should_fetch_expected_checksum(options):
- return options.pixel_tests and not (options.new_baseline or options.reset_results)
-
-
-def _run_single_test(port, options, test_input, test_types, test_args, driver, worker_name):
- # FIXME: Pull this into TestShellThread._run().
-
- # The image hash is used to avoid doing an image dump if the
- # checksums match, so it should be set to a blank value if we
- # are generating a new baseline. (Otherwise, an image from a
- # previous run will be copied into the baseline."""
- if _should_fetch_expected_checksum(options):
- test_input.image_hash = port.expected_checksum(test_input.filename)
- test_output = driver.run_test(test_input)
- return _process_output(port, options, test_input, test_types, test_args,
- test_output, worker_name)
-
-
-class SingleTestThread(threading.Thread):
- """Thread wrapper for running a single test file."""
-
- def __init__(self, port, options, worker_number, worker_name,
- test_input, test_types, test_args):
- """
- Args:
- port: object implementing port-specific hooks
- options: command line argument object from optparse
- worker_number: worker number for tests
- worker_name: for logging
- test_input: Object containing the test filename and timeout
- test_types: A list of TestType objects to run the test output
- against.
- test_args: A TestArguments object to pass to each TestType.
- """
-
- threading.Thread.__init__(self)
- self._port = port
- self._options = options
- self._test_input = test_input
- self._test_types = test_types
- self._test_args = test_args
- self._driver = None
- self._worker_number = worker_number
- self._name = worker_name
-
- def run(self):
- self._covered_run()
-
- def _covered_run(self):
- # FIXME: this is a separate routine to work around a bug
- # in coverage: see http://bitbucket.org/ned/coveragepy/issue/85.
- self._driver = self._port.create_driver(self._worker_number)
- self._driver.start()
- self._test_result = _run_single_test(self._port, self._options,
- self._test_input, self._test_types,
- self._test_args, self._driver,
- self._name)
- self._driver.stop()
-
- def get_test_result(self):
- return self._test_result
-
-
-class WatchableThread(threading.Thread):
- """This class abstracts an interface used by
- run_webkit_tests.TestRunner._wait_for_threads_to_finish for thread
- management."""
- def __init__(self):
- threading.Thread.__init__(self)
- self._canceled = False
- self._exception_info = None
- self._next_timeout = None
- self._thread_id = None
-
- def cancel(self):
- """Set a flag telling this thread to quit."""
- self._canceled = True
-
- def clear_next_timeout(self):
- """Mark a flag telling this thread to stop setting timeouts."""
- self._timeout = 0
-
- def exception_info(self):
- """If run() terminated on an uncaught exception, return it here
- ((type, value, traceback) tuple).
- Returns None if run() terminated normally. Meant to be called after
- joining this thread."""
- return self._exception_info
-
- def id(self):
- """Return a thread identifier."""
- return self._thread_id
-
- def next_timeout(self):
- """Return the time the test is supposed to finish by."""
- return self._next_timeout
-
-
-class TestShellThread(WatchableThread):
- def __init__(self, port, options, worker_number, worker_name,
- filename_list_queue, result_queue):
- """Initialize all the local state for this DumpRenderTree thread.
-
- Args:
- port: interface to port-specific hooks
- options: command line options argument from optparse
- worker_number: identifier for a particular worker thread.
- worker_name: for logging.
- filename_list_queue: A thread safe Queue class that contains lists
- of tuples of (filename, uri) pairs.
- result_queue: A thread safe Queue class that will contain
- serialized TestResult objects.
- """
- WatchableThread.__init__(self)
- self._port = port
- self._options = options
- self._worker_number = worker_number
- self._name = worker_name
- self._filename_list_queue = filename_list_queue
- self._result_queue = result_queue
- self._filename_list = []
- self._driver = None
- self._test_group_timing_stats = {}
- self._test_results = []
- self._num_tests = 0
- self._start_time = 0
- self._stop_time = 0
- self._have_http_lock = False
- self._http_lock_wait_begin = 0
- self._http_lock_wait_end = 0
-
- self._test_types = []
- for cls in self._get_test_type_classes():
- self._test_types.append(cls(self._port,
- self._options.results_directory))
- self._test_args = self._get_test_args(worker_number)
-
- # Current group of tests we're running.
- self._current_group = None
- # Number of tests in self._current_group.
- self._num_tests_in_current_group = None
- # Time at which we started running tests from self._current_group.
- self._current_group_start_time = None
-
- def _get_test_args(self, worker_number):
- """Returns the tuple of arguments for tests and for DumpRenderTree."""
- test_args = test_type_base.TestArguments()
- test_args.new_baseline = self._options.new_baseline
- test_args.reset_results = self._options.reset_results
-
- return test_args
-
- def _get_test_type_classes(self):
- classes = [text_diff.TestTextDiff]
- if self._options.pixel_tests:
- classes.append(image_diff.ImageDiff)
- return classes
-
- def get_test_group_timing_stats(self):
- """Returns a dictionary mapping test group to a tuple of
- (number of tests in that group, time to run the tests)"""
- return self._test_group_timing_stats
-
- def get_test_results(self):
- """Return the list of all tests run on this thread.
-
- This is used to calculate per-thread statistics.
-
- """
- return self._test_results
-
- def get_total_time(self):
- return max(self._stop_time - self._start_time -
- self._http_lock_wait_time(), 0.0)
-
- def get_num_tests(self):
- return self._num_tests
-
- def run(self):
- """Delegate main work to a helper method and watch for uncaught
- exceptions."""
- self._covered_run()
-
- def _covered_run(self):
- # FIXME: this is a separate routine to work around a bug
- # in coverage: see http://bitbucket.org/ned/coveragepy/issue/85.
- self._thread_id = thread.get_ident()
- self._start_time = time.time()
- self._num_tests = 0
- try:
- _log.debug('%s starting' % (self.getName()))
- self._run(test_runner=None, result_summary=None)
- _log.debug('%s done (%d tests)' % (self.getName(),
- self.get_num_tests()))
- except KeyboardInterrupt:
- self._exception_info = sys.exc_info()
- _log.debug("%s interrupted" % self.getName())
- except:
- # Save the exception for our caller to see.
- self._exception_info = sys.exc_info()
- self._stop_time = time.time()
- _log.error('%s dying, exception raised' % self.getName())
-
- self._stop_time = time.time()
-
- def run_in_main_thread(self, test_runner, result_summary):
- """This hook allows us to run the tests from the main thread if
- --num-test-shells==1, instead of having to always run two or more
- threads. This allows us to debug the test harness without having to
- do multi-threaded debugging."""
- self._run(test_runner, result_summary)
-
- def cancel(self):
- """Clean up http lock and set a flag telling this thread to quit."""
- self._stop_servers_with_lock()
- WatchableThread.cancel(self)
-
- def next_timeout(self):
- """Return the time the test is supposed to finish by."""
- if self._next_timeout:
- return self._next_timeout + self._http_lock_wait_time()
- return self._next_timeout
-
- def _http_lock_wait_time(self):
- """Return the time what http locking takes."""
- if self._http_lock_wait_begin == 0:
- return 0
- if self._http_lock_wait_end == 0:
- return time.time() - self._http_lock_wait_begin
- return self._http_lock_wait_end - self._http_lock_wait_begin
-
- def _run(self, test_runner, result_summary):
- """Main work entry point of the thread. Basically we pull urls from the
- filename queue and run the tests until we run out of urls.
-
- If test_runner is not None, then we call test_runner.UpdateSummary()
- with the results of each test."""
- batch_size = self._options.batch_size
- batch_count = 0
-
- # Append tests we're running to the existing tests_run.txt file.
- # This is created in run_webkit_tests.py:_PrepareListsAndPrintOutput.
- tests_run_filename = os.path.join(self._options.results_directory,
- "tests_run.txt")
- tests_run_file = codecs.open(tests_run_filename, "a", "utf-8")
-
- while True:
- if self._canceled:
- _log.debug('Testing cancelled')
- tests_run_file.close()
- return
-
- if len(self._filename_list) is 0:
- if self._current_group is not None:
- self._test_group_timing_stats[self._current_group] = \
- (self._num_tests_in_current_group,
- time.time() - self._current_group_start_time)
-
- try:
- self._current_group, self._filename_list = \
- self._filename_list_queue.get_nowait()
- except Queue.Empty:
- self._stop_servers_with_lock()
- self._kill_dump_render_tree()
- tests_run_file.close()
- return
-
- if self._current_group == "tests_to_http_lock":
- self._start_servers_with_lock()
- elif self._have_http_lock:
- self._stop_servers_with_lock()
-
- self._num_tests_in_current_group = len(self._filename_list)
- self._current_group_start_time = time.time()
-
- test_input = self._filename_list.pop()
-
- # We have a url, run tests.
- batch_count += 1
- self._num_tests += 1
- if self._options.run_singly:
- result = self._run_test_in_another_thread(test_input)
- else:
- result = self._run_test_in_this_thread(test_input)
-
- filename = test_input.filename
- tests_run_file.write(filename + "\n")
- if result.failures:
- # Check and kill DumpRenderTree if we need to.
- if len([1 for f in result.failures
- if f.should_kill_dump_render_tree()]):
- self._kill_dump_render_tree()
- # Reset the batch count since the shell just bounced.
- batch_count = 0
- # Print the error message(s).
- error_str = '\n'.join([' ' + f.message() for
- f in result.failures])
- _log.debug("%s %s failed:\n%s" % (self.getName(),
- self._port.relative_test_filename(filename),
- error_str))
- else:
- _log.debug("%s %s passed" % (self.getName(),
- self._port.relative_test_filename(filename)))
- self._result_queue.put(result.dumps())
-
- if batch_size > 0 and batch_count >= batch_size:
- # Bounce the shell and reset count.
- self._kill_dump_render_tree()
- batch_count = 0
-
- if test_runner:
- test_runner.update_summary(result_summary)
-
- def _run_test_in_another_thread(self, test_input):
- """Run a test in a separate thread, enforcing a hard time limit.
-
- Since we can only detect the termination of a thread, not any internal
- state or progress, we can only run per-test timeouts when running test
- files singly.
-
- Args:
- test_input: Object containing the test filename and timeout
-
- Returns:
- A TestResult
- """
- worker = SingleTestThread(self._port,
- self._options,
- self._worker_number,
- self._name,
- test_input,
- self._test_types,
- self._test_args)
-
- worker.start()
-
- thread_timeout = _milliseconds_to_seconds(
- _pad_timeout(int(test_input.timeout)))
- thread._next_timeout = time.time() + thread_timeout
- worker.join(thread_timeout)
- if worker.isAlive():
- # If join() returned with the thread still running, the
- # DumpRenderTree is completely hung and there's nothing
- # more we can do with it. We have to kill all the
- # DumpRenderTrees to free it up. If we're running more than
- # one DumpRenderTree thread, we'll end up killing the other
- # DumpRenderTrees too, introducing spurious crashes. We accept
- # that tradeoff in order to avoid losing the rest of this
- # thread's results.
- _log.error('Test thread hung: killing all DumpRenderTrees')
- if worker._driver:
- worker._driver.stop()
-
- try:
- result = worker.get_test_result()
- except AttributeError, e:
- # This gets raised if the worker thread has already exited.
- failures = []
- _log.error('Cannot get results of test: %s' %
- test_input.filename)
- result = test_results.TestResult(test_input.filename, failures=[],
- test_run_time=0, total_time_for_all_diffs=0, time_for_diffs={})
-
- return result
-
- def _run_test_in_this_thread(self, test_input):
- """Run a single test file using a shared DumpRenderTree process.
-
- Args:
- test_input: Object containing the test filename, uri and timeout
-
- Returns: a TestResult object.
- """
- self._ensure_dump_render_tree_is_running()
- thread_timeout = _milliseconds_to_seconds(
- _pad_timeout(int(test_input.timeout)))
- self._next_timeout = time.time() + thread_timeout
- test_result = _run_single_test(self._port, self._options, test_input,
- self._test_types, self._test_args,
- self._driver, self._name)
- self._test_results.append(test_result)
- return test_result
-
- def _ensure_dump_render_tree_is_running(self):
- """Start the shared DumpRenderTree, if it's not running.
-
- This is not for use when running tests singly, since those each start
- a separate DumpRenderTree in their own thread.
-
- """
- # poll() is not threadsafe and can throw OSError due to:
- # http://bugs.python.org/issue1731717
- if not self._driver or self._driver.poll() is not None:
- self._driver = self._port.create_driver(self._worker_number)
- self._driver.start()
-
- def _start_servers_with_lock(self):
- """Acquire http lock and start the servers."""
- self._http_lock_wait_begin = time.time()
- _log.debug('Acquire http lock ...')
- self._port.acquire_http_lock()
- _log.debug('Starting HTTP server ...')
- self._port.start_http_server()
- _log.debug('Starting WebSocket server ...')
- self._port.start_websocket_server()
- self._http_lock_wait_end = time.time()
- self._have_http_lock = True
-
- def _stop_servers_with_lock(self):
- """Stop the servers and release http lock."""
- if self._have_http_lock:
- _log.debug('Stopping HTTP server ...')
- self._port.stop_http_server()
- _log.debug('Stopping WebSocket server ...')
- self._port.stop_websocket_server()
- _log.debug('Release http lock ...')
- self._port.release_http_lock()
- self._have_http_lock = False
-
- def _kill_dump_render_tree(self):
- """Kill the DumpRenderTree process if it's running."""
- if self._driver:
- self._driver.stop()
- self._driver = None
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py
deleted file mode 100644
index b054c5b..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py
+++ /dev/null
@@ -1,212 +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.
-
-import logging
-import os
-
-from webkitpy.layout_tests.layout_package import json_results_generator
-from webkitpy.layout_tests.layout_package import test_expectations
-from webkitpy.layout_tests.layout_package import test_failures
-import webkitpy.thirdparty.simplejson as simplejson
-
-
-class JSONLayoutResultsGenerator(json_results_generator.JSONResultsGeneratorBase):
- """A JSON results generator for layout tests."""
-
- LAYOUT_TESTS_PATH = "LayoutTests"
-
- # Additional JSON fields.
- WONTFIX = "wontfixCounts"
-
- # Note that we omit test_expectations.FAIL from this list because
- # it should never show up (it's a legacy input expectation, never
- # an output expectation).
- FAILURE_TO_CHAR = {test_expectations.CRASH: "C",
- test_expectations.TIMEOUT: "T",
- test_expectations.IMAGE: "I",
- test_expectations.TEXT: "F",
- test_expectations.MISSING: "O",
- test_expectations.IMAGE_PLUS_TEXT: "Z"}
-
- def __init__(self, port, builder_name, build_name, build_number,
- results_file_base_path, builder_base_url,
- test_timings, expectations, result_summary, all_tests,
- generate_incremental_results=False, test_results_server=None,
- test_type="", master_name=""):
- """Modifies the results.json file. Grabs it off the archive directory
- if it is not found locally.
-
- Args:
- result_summary: ResultsSummary object storing the summary of the test
- results.
- """
- super(JSONLayoutResultsGenerator, self).__init__(
- builder_name, build_name, build_number, results_file_base_path,
- builder_base_url, {}, port.test_repository_paths(),
- generate_incremental_results, test_results_server,
- test_type, master_name)
-
- self._port = port
- self._expectations = expectations
-
- # We want relative paths to LayoutTest root for JSON output.
- path_to_name = self._get_path_relative_to_layout_test_root
- self._result_summary = result_summary
- self._failures = dict(
- (path_to_name(test), test_failures.determine_result_type(failures))
- for (test, failures) in result_summary.failures.iteritems())
- self._all_tests = [path_to_name(test) for test in all_tests]
- self._test_timings = dict(
- (path_to_name(test_tuple.filename), test_tuple.test_run_time)
- for test_tuple in test_timings)
-
- self.generate_json_output()
-
- def _get_path_relative_to_layout_test_root(self, test):
- """Returns the path of the test relative to the layout test root.
- For example, for:
- src/third_party/WebKit/LayoutTests/fast/forms/foo.html
- We would return
- fast/forms/foo.html
- """
- index = test.find(self.LAYOUT_TESTS_PATH)
- if index is not -1:
- index += len(self.LAYOUT_TESTS_PATH)
-
- if index is -1:
- # Already a relative path.
- relativePath = test
- else:
- relativePath = test[index + 1:]
-
- # Make sure all paths are unix-style.
- return relativePath.replace('\\', '/')
-
- # override
- def _get_test_timing(self, test_name):
- if test_name in self._test_timings:
- # Floor for now to get time in seconds.
- return int(self._test_timings[test_name])
- return 0
-
- # override
- def _get_failed_test_names(self):
- return set(self._failures.keys())
-
- # override
- def _get_modifier_char(self, test_name):
- if test_name not in self._all_tests:
- return self.NO_DATA_RESULT
-
- if test_name in self._failures:
- return self.FAILURE_TO_CHAR[self._failures[test_name]]
-
- return self.PASS_RESULT
-
- # override
- def _get_result_char(self, test_name):
- return self._get_modifier_char(test_name)
-
- # override
- def _convert_json_to_current_version(self, results_json):
- archive_version = None
- if self.VERSION_KEY in results_json:
- archive_version = results_json[self.VERSION_KEY]
-
- super(JSONLayoutResultsGenerator,
- self)._convert_json_to_current_version(results_json)
-
- # version 2->3
- if archive_version == 2:
- for results_for_builder in results_json.itervalues():
- try:
- test_results = results_for_builder[self.TESTS]
- except:
- continue
-
- for test in test_results:
- # Make sure all paths are relative
- test_path = self._get_path_relative_to_layout_test_root(test)
- if test_path != test:
- test_results[test_path] = test_results[test]
- del test_results[test]
-
- # override
- def _insert_failure_summaries(self, results_for_builder):
- summary = self._result_summary
-
- self._insert_item_into_raw_list(results_for_builder,
- len((set(summary.failures.keys()) |
- summary.tests_by_expectation[test_expectations.SKIP]) &
- summary.tests_by_timeline[test_expectations.NOW]),
- self.FIXABLE_COUNT)
- self._insert_item_into_raw_list(results_for_builder,
- self._get_failure_summary_entry(test_expectations.NOW),
- self.FIXABLE)
- self._insert_item_into_raw_list(results_for_builder,
- len(self._expectations.get_tests_with_timeline(
- test_expectations.NOW)), self.ALL_FIXABLE_COUNT)
- self._insert_item_into_raw_list(results_for_builder,
- self._get_failure_summary_entry(test_expectations.WONTFIX),
- self.WONTFIX)
-
- # override
- def _normalize_results_json(self, test, test_name, tests):
- super(JSONLayoutResultsGenerator, self)._normalize_results_json(
- test, test_name, tests)
-
- # Remove tests that don't exist anymore.
- full_path = os.path.join(self._port.layout_tests_dir(), test_name)
- full_path = os.path.normpath(full_path)
- if not os.path.exists(full_path):
- del tests[test_name]
-
- def _get_failure_summary_entry(self, timeline):
- """Creates a summary object to insert into the JSON.
-
- Args:
- summary ResultSummary object with test results
- timeline current test_expectations timeline to build entry for
- (e.g., test_expectations.NOW, etc.)
- """
- entry = {}
- summary = self._result_summary
- timeline_tests = summary.tests_by_timeline[timeline]
- entry[self.SKIP_RESULT] = len(
- summary.tests_by_expectation[test_expectations.SKIP] &
- timeline_tests)
- entry[self.PASS_RESULT] = len(
- summary.tests_by_expectation[test_expectations.PASS] &
- timeline_tests)
- for failure_type in summary.tests_by_expectation.keys():
- if failure_type not in self.FAILURE_TO_CHAR:
- continue
- count = len(summary.tests_by_expectation[failure_type] &
- timeline_tests)
- entry[self.FAILURE_TO_CHAR[failure_type]] = count
- return entry
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py
deleted file mode 100644
index 331e330..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py
+++ /dev/null
@@ -1,661 +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.
-
-from __future__ import with_statement
-
-import codecs
-import logging
-import os
-import subprocess
-import sys
-import time
-import urllib2
-import xml.dom.minidom
-
-from webkitpy.layout_tests.layout_package import test_results_uploader
-
-import webkitpy.thirdparty.simplejson as simplejson
-
-# A JSON results generator for generic tests.
-# FIXME: move this code out of the layout_package directory.
-
-_log = logging.getLogger("webkitpy.layout_tests.layout_package.json_results_generator")
-
-class TestResult(object):
- """A simple class that represents a single test result."""
-
- # Test modifier constants.
- (NONE, FAILS, FLAKY, DISABLED) = range(4)
-
- def __init__(self, name, failed=False, elapsed_time=0):
- self.name = name
- self.failed = failed
- self.time = elapsed_time
-
- test_name = name
- try:
- test_name = name.split('.')[1]
- except IndexError:
- _log.warn("Invalid test name: %s.", name)
- pass
-
- if test_name.startswith('FAILS_'):
- self.modifier = self.FAILS
- elif test_name.startswith('FLAKY_'):
- self.modifier = self.FLAKY
- elif test_name.startswith('DISABLED_'):
- self.modifier = self.DISABLED
- else:
- self.modifier = self.NONE
-
- def fixable(self):
- return self.failed or self.modifier == self.DISABLED
-
-
-class JSONResultsGeneratorBase(object):
- """A JSON results generator for generic tests."""
-
- MAX_NUMBER_OF_BUILD_RESULTS_TO_LOG = 750
- # Min time (seconds) that will be added to the JSON.
- MIN_TIME = 1
- JSON_PREFIX = "ADD_RESULTS("
- JSON_SUFFIX = ");"
-
- # Note that in non-chromium tests those chars are used to indicate
- # test modifiers (FAILS, FLAKY, etc) but not actual test results.
- PASS_RESULT = "P"
- SKIP_RESULT = "X"
- FAIL_RESULT = "F"
- FLAKY_RESULT = "L"
- NO_DATA_RESULT = "N"
-
- MODIFIER_TO_CHAR = {TestResult.NONE: PASS_RESULT,
- TestResult.DISABLED: SKIP_RESULT,
- TestResult.FAILS: FAIL_RESULT,
- TestResult.FLAKY: FLAKY_RESULT}
-
- VERSION = 3
- VERSION_KEY = "version"
- RESULTS = "results"
- TIMES = "times"
- BUILD_NUMBERS = "buildNumbers"
- TIME = "secondsSinceEpoch"
- TESTS = "tests"
-
- FIXABLE_COUNT = "fixableCount"
- FIXABLE = "fixableCounts"
- ALL_FIXABLE_COUNT = "allFixableCount"
-
- RESULTS_FILENAME = "results.json"
- INCREMENTAL_RESULTS_FILENAME = "incremental_results.json"
-
- URL_FOR_TEST_LIST_JSON = \
- "http://%s/testfile?builder=%s&name=%s&testlistjson=1&testtype=%s"
-
- def __init__(self, builder_name, build_name, build_number,
- results_file_base_path, builder_base_url,
- test_results_map, svn_repositories=None,
- generate_incremental_results=False,
- test_results_server=None,
- test_type="",
- master_name=""):
- """Modifies the results.json file. Grabs it off the archive directory
- if it is not found locally.
-
- Args
- builder_name: the builder name (e.g. Webkit).
- build_name: the build name (e.g. webkit-rel).
- build_number: the build number.
- results_file_base_path: Absolute path to the directory containing the
- results json file.
- builder_base_url: the URL where we have the archived test results.
- If this is None no archived results will be retrieved.
- test_results_map: A dictionary that maps test_name to TestResult.
- svn_repositories: A (json_field_name, svn_path) pair for SVN
- repositories that tests rely on. The SVN revision will be
- included in the JSON with the given json_field_name.
- generate_incremental_results: If true, generate incremental json file
- from current run results.
- test_results_server: server that hosts test results json.
- test_type: test type string (e.g. 'layout-tests').
- master_name: the name of the buildbot master.
- """
- self._builder_name = builder_name
- self._build_name = build_name
- self._build_number = build_number
- self._builder_base_url = builder_base_url
- self._results_directory = results_file_base_path
- self._results_file_path = os.path.join(results_file_base_path,
- self.RESULTS_FILENAME)
- self._incremental_results_file_path = os.path.join(
- results_file_base_path, self.INCREMENTAL_RESULTS_FILENAME)
-
- self._test_results_map = test_results_map
- self._test_results = test_results_map.values()
- self._generate_incremental_results = generate_incremental_results
-
- self._svn_repositories = svn_repositories
- if not self._svn_repositories:
- self._svn_repositories = {}
-
- self._test_results_server = test_results_server
- self._test_type = test_type
- self._master_name = master_name
-
- self._json = None
- self._archived_results = None
-
- def generate_json_output(self):
- """Generates the JSON output file."""
-
- # Generate the JSON output file that has full results.
- # FIXME: stop writing out the full results file once all bots use
- # incremental results.
- if not self._json:
- self._json = self.get_json()
- if self._json:
- self._generate_json_file(self._json, self._results_file_path)
-
- # Generate the JSON output file that only has incremental results.
- if self._generate_incremental_results:
- json = self.get_json(incremental=True)
- if json:
- self._generate_json_file(
- json, self._incremental_results_file_path)
-
- def get_json(self, incremental=False):
- """Gets the results for the results.json file."""
- results_json = {}
- if not incremental:
- if self._json:
- return self._json
-
- if self._archived_results:
- results_json = self._archived_results
-
- if not results_json:
- results_json, error = self._get_archived_json_results(incremental)
- if error:
- # If there was an error don't write a results.json
- # file at all as it would lose all the information on the
- # bot.
- _log.error("Archive directory is inaccessible. Not "
- "modifying or clobbering the results.json "
- "file: " + str(error))
- return None
-
- builder_name = self._builder_name
- if results_json and builder_name not in results_json:
- _log.debug("Builder name (%s) is not in the results.json file."
- % builder_name)
-
- self._convert_json_to_current_version(results_json)
-
- if builder_name not in results_json:
- results_json[builder_name] = (
- self._create_results_for_builder_json())
-
- results_for_builder = results_json[builder_name]
-
- self._insert_generic_metadata(results_for_builder)
-
- self._insert_failure_summaries(results_for_builder)
-
- # Update the all failing tests with result type and time.
- tests = results_for_builder[self.TESTS]
- all_failing_tests = self._get_failed_test_names()
- all_failing_tests.update(tests.iterkeys())
- for test in all_failing_tests:
- self._insert_test_time_and_result(test, tests, incremental)
-
- return results_json
-
- def set_archived_results(self, archived_results):
- self._archived_results = archived_results
-
- def upload_json_files(self, json_files):
- """Uploads the given json_files to the test_results_server (if the
- test_results_server is given)."""
- if not self._test_results_server:
- return
-
- if not self._master_name:
- _log.error("--test-results-server was set, but --master-name was not. Not uploading JSON files.")
- return
-
- _log.info("Uploading JSON files for builder: %s", self._builder_name)
- attrs = [("builder", self._builder_name),
- ("testtype", self._test_type),
- ("master", self._master_name)]
-
- files = [(file, os.path.join(self._results_directory, file))
- for file in json_files]
-
- uploader = test_results_uploader.TestResultsUploader(
- self._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 _generate_json_file(self, json, file_path):
- # Specify separators in order to get compact encoding.
- json_data = simplejson.dumps(json, separators=(',', ':'))
- json_string = self.JSON_PREFIX + json_data + self.JSON_SUFFIX
-
- results_file = codecs.open(file_path, "w", "utf-8")
- results_file.write(json_string)
- results_file.close()
-
- def _get_test_timing(self, test_name):
- """Returns test timing data (elapsed time) in second
- for the given test_name."""
- if test_name in self._test_results_map:
- # Floor for now to get time in seconds.
- return int(self._test_results_map[test_name].time)
- return 0
-
- def _get_failed_test_names(self):
- """Returns a set of failed test names."""
- return set([r.name for r in self._test_results if r.failed])
-
- def _get_modifier_char(self, test_name):
- """Returns a single char (e.g. SKIP_RESULT, FAIL_RESULT,
- PASS_RESULT, NO_DATA_RESULT, etc) that indicates the test modifier
- for the given test_name.
- """
- if test_name not in self._test_results_map:
- return JSONResultsGenerator.NO_DATA_RESULT
-
- test_result = self._test_results_map[test_name]
- if test_result.modifier in self.MODIFIER_TO_CHAR.keys():
- return self.MODIFIER_TO_CHAR[test_result.modifier]
-
- return JSONResultsGenerator.PASS_RESULT
-
- def _get_result_char(self, test_name):
- """Returns a single char (e.g. SKIP_RESULT, FAIL_RESULT,
- PASS_RESULT, NO_DATA_RESULT, etc) that indicates the test result
- for the given test_name.
- """
- if test_name not in self._test_results_map:
- return JSONResultsGenerator.NO_DATA_RESULT
-
- test_result = self._test_results_map[test_name]
- if test_result.modifier == TestResult.DISABLED:
- return JSONResultsGenerator.SKIP_RESULT
-
- if test_result.failed:
- return JSONResultsGenerator.FAIL_RESULT
-
- return JSONResultsGenerator.PASS_RESULT
-
- # FIXME: Callers should use scm.py instead.
- # FIXME: Identify and fix the run-time errors that were observed on Windows
- # chromium buildbot when we had updated this code to use scm.py once before.
- def _get_svn_revision(self, in_directory):
- """Returns the svn revision for the given directory.
-
- Args:
- in_directory: The directory where svn is to be run.
- """
- if os.path.exists(os.path.join(in_directory, '.svn')):
- # Note: Not thread safe: http://bugs.python.org/issue2320
- output = subprocess.Popen(["svn", "info", "--xml"],
- cwd=in_directory,
- shell=(sys.platform == 'win32'),
- stdout=subprocess.PIPE).communicate()[0]
- try:
- dom = xml.dom.minidom.parseString(output)
- return dom.getElementsByTagName('entry')[0].getAttribute(
- 'revision')
- except xml.parsers.expat.ExpatError:
- return ""
- return ""
-
- def _get_archived_json_results(self, for_incremental=False):
- """Reads old results JSON file if it exists.
- Returns (archived_results, error) tuple where error is None if results
- were successfully read.
-
- if for_incremental is True, download JSON file that only contains test
- name list from test-results server. This is for generating incremental
- JSON so the file generated has info for tests that failed before but
- pass or are skipped from current run.
- """
- results_json = {}
- old_results = None
- error = None
-
- if os.path.exists(self._results_file_path) and not for_incremental:
- with codecs.open(self._results_file_path, "r", "utf-8") as file:
- old_results = file.read()
- elif self._builder_base_url or for_incremental:
- if for_incremental:
- if not self._test_results_server:
- # starting from fresh if no test results server specified.
- return {}, None
-
- results_file_url = (self.URL_FOR_TEST_LIST_JSON %
- (urllib2.quote(self._test_results_server),
- urllib2.quote(self._builder_name),
- self.RESULTS_FILENAME,
- urllib2.quote(self._test_type)))
- else:
- # Check if we have the archived JSON file on the buildbot
- # server.
- results_file_url = (self._builder_base_url +
- self._build_name + "/" + self.RESULTS_FILENAME)
- _log.error("Local results.json file does not exist. Grabbing "
- "it off the archive at " + results_file_url)
-
- try:
- results_file = urllib2.urlopen(results_file_url)
- info = results_file.info()
- old_results = results_file.read()
- except urllib2.HTTPError, http_error:
- # A non-4xx status code means the bot is hosed for some reason
- # and we can't grab the results.json file off of it.
- if (http_error.code < 400 and http_error.code >= 500):
- error = http_error
- except urllib2.URLError, url_error:
- error = url_error
-
- if old_results:
- # Strip the prefix and suffix so we can get the actual JSON object.
- old_results = old_results[len(self.JSON_PREFIX):
- len(old_results) - len(self.JSON_SUFFIX)]
-
- try:
- results_json = simplejson.loads(old_results)
- except:
- _log.debug("results.json was not valid JSON. Clobbering.")
- # The JSON file is not valid JSON. Just clobber the results.
- results_json = {}
- else:
- _log.debug('Old JSON results do not exist. Starting fresh.')
- results_json = {}
-
- return results_json, error
-
- def _insert_failure_summaries(self, results_for_builder):
- """Inserts aggregate pass/failure statistics into the JSON.
- This method reads self._test_results and generates
- FIXABLE, FIXABLE_COUNT and ALL_FIXABLE_COUNT entries.
-
- Args:
- results_for_builder: Dictionary containing the test results for a
- single builder.
- """
- # Insert the number of tests that failed or skipped.
- fixable_count = len([r for r in self._test_results if r.fixable()])
- self._insert_item_into_raw_list(results_for_builder,
- fixable_count, self.FIXABLE_COUNT)
-
- # Create a test modifiers (FAILS, FLAKY etc) summary dictionary.
- entry = {}
- for test_name in self._test_results_map.iterkeys():
- result_char = self._get_modifier_char(test_name)
- entry[result_char] = entry.get(result_char, 0) + 1
-
- # Insert the pass/skip/failure summary dictionary.
- self._insert_item_into_raw_list(results_for_builder, entry,
- self.FIXABLE)
-
- # Insert the number of all the tests that are supposed to pass.
- all_test_count = len(self._test_results)
- self._insert_item_into_raw_list(results_for_builder,
- all_test_count, self.ALL_FIXABLE_COUNT)
-
- def _insert_item_into_raw_list(self, results_for_builder, item, key):
- """Inserts the item into the list with the given key in the results for
- this builder. Creates the list if no such list exists.
-
- Args:
- results_for_builder: Dictionary containing the test results for a
- single builder.
- item: Number or string to insert into the list.
- key: Key in results_for_builder for the list to insert into.
- """
- if key in results_for_builder:
- raw_list = results_for_builder[key]
- else:
- raw_list = []
-
- raw_list.insert(0, item)
- raw_list = raw_list[:self.MAX_NUMBER_OF_BUILD_RESULTS_TO_LOG]
- results_for_builder[key] = raw_list
-
- def _insert_item_run_length_encoded(self, item, encoded_results):
- """Inserts the item into the run-length encoded results.
-
- Args:
- item: String or number to insert.
- encoded_results: run-length encoded results. An array of arrays, e.g.
- [[3,'A'],[1,'Q']] encodes AAAQ.
- """
- if len(encoded_results) and item == encoded_results[0][1]:
- num_results = encoded_results[0][0]
- if num_results <= self.MAX_NUMBER_OF_BUILD_RESULTS_TO_LOG:
- encoded_results[0][0] = num_results + 1
- else:
- # Use a list instead of a class for the run-length encoding since
- # we want the serialized form to be concise.
- encoded_results.insert(0, [1, item])
-
- def _insert_generic_metadata(self, results_for_builder):
- """ Inserts generic metadata (such as version number, current time etc)
- into the JSON.
-
- Args:
- results_for_builder: Dictionary containing the test results for
- a single builder.
- """
- self._insert_item_into_raw_list(results_for_builder,
- self._build_number, self.BUILD_NUMBERS)
-
- # Include SVN revisions for the given repositories.
- for (name, path) in self._svn_repositories:
- self._insert_item_into_raw_list(results_for_builder,
- self._get_svn_revision(path),
- name + 'Revision')
-
- self._insert_item_into_raw_list(results_for_builder,
- int(time.time()),
- self.TIME)
-
- def _insert_test_time_and_result(self, test_name, tests, incremental=False):
- """ Insert a test item with its results to the given tests dictionary.
-
- Args:
- tests: Dictionary containing test result entries.
- """
-
- result = self._get_result_char(test_name)
- time = self._get_test_timing(test_name)
-
- if test_name not in tests:
- tests[test_name] = self._create_results_and_times_json()
-
- thisTest = tests[test_name]
- if self.RESULTS in thisTest:
- self._insert_item_run_length_encoded(result, thisTest[self.RESULTS])
- else:
- thisTest[self.RESULTS] = [[1, result]]
-
- if self.TIMES in thisTest:
- self._insert_item_run_length_encoded(time, thisTest[self.TIMES])
- else:
- thisTest[self.TIMES] = [[1, time]]
-
- # Don't normalize the incremental results json because we need results
- # for tests that pass or have no data from current run.
- if not incremental:
- self._normalize_results_json(thisTest, test_name, tests)
-
- def _convert_json_to_current_version(self, results_json):
- """If the JSON does not match the current version, converts it to the
- current version and adds in the new version number.
- """
- if (self.VERSION_KEY in results_json and
- results_json[self.VERSION_KEY] == self.VERSION):
- return
-
- results_json[self.VERSION_KEY] = self.VERSION
-
- def _create_results_and_times_json(self):
- results_and_times = {}
- results_and_times[self.RESULTS] = []
- results_and_times[self.TIMES] = []
- return results_and_times
-
- def _create_results_for_builder_json(self):
- results_for_builder = {}
- results_for_builder[self.TESTS] = {}
- return results_for_builder
-
- def _remove_items_over_max_number_of_builds(self, encoded_list):
- """Removes items from the run-length encoded list after the final
- item that exceeds the max number of builds to track.
-
- Args:
- encoded_results: run-length encoded results. An array of arrays, e.g.
- [[3,'A'],[1,'Q']] encodes AAAQ.
- """
- num_builds = 0
- index = 0
- for result in encoded_list:
- num_builds = num_builds + result[0]
- index = index + 1
- if num_builds > self.MAX_NUMBER_OF_BUILD_RESULTS_TO_LOG:
- return encoded_list[:index]
- return encoded_list
-
- def _normalize_results_json(self, test, test_name, tests):
- """ Prune tests where all runs pass or tests that no longer exist and
- truncate all results to maxNumberOfBuilds.
-
- Args:
- test: ResultsAndTimes object for this test.
- test_name: Name of the test.
- tests: The JSON object with all the test results for this builder.
- """
- test[self.RESULTS] = self._remove_items_over_max_number_of_builds(
- test[self.RESULTS])
- test[self.TIMES] = self._remove_items_over_max_number_of_builds(
- test[self.TIMES])
-
- is_all_pass = self._is_results_all_of_type(test[self.RESULTS],
- self.PASS_RESULT)
- is_all_no_data = self._is_results_all_of_type(test[self.RESULTS],
- self.NO_DATA_RESULT)
- max_time = max([time[1] for time in test[self.TIMES]])
-
- # Remove all passes/no-data from the results to reduce noise and
- # filesize. If a test passes every run, but takes > MIN_TIME to run,
- # don't throw away the data.
- if is_all_no_data or (is_all_pass and max_time <= self.MIN_TIME):
- del tests[test_name]
-
- def _is_results_all_of_type(self, results, type):
- """Returns whether all the results are of the given type
- (e.g. all passes)."""
- return len(results) == 1 and results[0][1] == type
-
-
-# A wrapper class for JSONResultsGeneratorBase.
-# Note: There's a script outside the WebKit codebase calling this script.
-# FIXME: Please keep the interface until the other script is cleaned up.
-# (http://src.chromium.org/viewvc/chrome/trunk/src/webkit/tools/layout_tests/webkitpy/layout_tests/test_output_xml_to_json.py?view=markup)
-class JSONResultsGenerator(JSONResultsGeneratorBase):
- # The flag is for backward compatibility.
- output_json_in_init = True
-
- def __init__(self, port, builder_name, build_name, build_number,
- results_file_base_path, builder_base_url,
- test_timings, failures, passed_tests, skipped_tests, all_tests,
- test_results_server=None, test_type=None, master_name=None):
- """Generates a JSON results file.
-
- Args
- builder_name: the builder name (e.g. Webkit).
- build_name: the build name (e.g. webkit-rel).
- build_number: the build number.
- results_file_base_path: Absolute path to the directory containing the
- results json file.
- builder_base_url: the URL where we have the archived test results.
- test_timings: Map of test name to a test_run-time.
- failures: Map of test name to a failure type (of test_expectations).
- passed_tests: A set containing all the passed tests.
- skipped_tests: A set containing all the skipped tests.
- all_tests: List of all the tests that were run. This should not
- include skipped tests.
- test_results_server: server that hosts test results json.
- test_type: the test type.
- master_name: the name of the buildbot master.
- """
-
- self._test_type = test_type
- self._results_directory = results_file_base_path
-
- # Create a map of (name, TestResult).
- test_results_map = dict()
- get = test_results_map.get
- for (test, time) in test_timings.iteritems():
- test_results_map[test] = TestResult(test, elapsed_time=time)
- for test in failures.iterkeys():
- test_results_map[test] = test_result = get(test, TestResult(test))
- test_result.failed = True
- for test in skipped_tests:
- test_results_map[test] = test_result = get(test, TestResult(test))
- for test in passed_tests:
- test_results_map[test] = test_result = get(test, TestResult(test))
- test_result.failed = False
- for test in all_tests:
- if test not in test_results_map:
- test_results_map[test] = TestResult(test)
-
- # Generate the JSON with incremental flag enabled.
- # (This should also output the full result for now.)
- super(JSONResultsGenerator, self).__init__(
- builder_name, build_name, build_number,
- results_file_base_path, builder_base_url, test_results_map,
- svn_repositories=port.test_repository_paths(),
- generate_incremental_results=True,
- test_results_server=test_results_server,
- test_type=test_type,
- master_name=master_name)
-
- if self.__class__.output_json_in_init:
- self.generate_json_output()
- self.upload_json_files([self.INCREMENTAL_RESULTS_FILENAME])
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator_unittest.py
deleted file mode 100644
index d6275ee..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator_unittest.py
+++ /dev/null
@@ -1,205 +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.
-
-"""Unit tests for json_results_generator.py."""
-
-import unittest
-import optparse
-import random
-import shutil
-import tempfile
-
-from webkitpy.layout_tests.layout_package import json_results_generator
-from webkitpy.layout_tests.layout_package import test_expectations
-from webkitpy.layout_tests import port
-
-
-class JSONGeneratorTest(unittest.TestCase):
- def setUp(self):
- json_results_generator.JSONResultsGenerator.output_json_in_init = False
- self.builder_name = 'DUMMY_BUILDER_NAME'
- self.build_name = 'DUMMY_BUILD_NAME'
- self.build_number = 'DUMMY_BUILDER_NUMBER'
- self._json = None
- self._num_runs = 0
- self._tests_set = set([])
- self._test_timings = {}
- self._failed_tests = set([])
-
- self._PASS_tests = set([])
- self._DISABLED_tests = set([])
- self._FLAKY_tests = set([])
- self._FAILS_tests = set([])
-
- def _test_json_generation(self, passed_tests_list, failed_tests_list):
- tests_set = set(passed_tests_list) | set(failed_tests_list)
-
- DISABLED_tests = set([t for t in tests_set
- if t.startswith('DISABLED_')])
- FLAKY_tests = set([t for t in tests_set
- if t.startswith('FLAKY_')])
- FAILS_tests = set([t for t in tests_set
- if t.startswith('FAILS_')])
- PASS_tests = tests_set - (DISABLED_tests | FLAKY_tests | FAILS_tests)
-
- passed_tests = set(passed_tests_list) - DISABLED_tests
- failed_tests = set(failed_tests_list)
-
- test_timings = {}
- i = 0
- for test in tests_set:
- test_timings[test] = float(self._num_runs * 100 + i)
- i += 1
-
- # For backward compatibility.
- reason = test_expectations.TEXT
- failed_tests_dict = dict([(name, reason) for name in failed_tests])
-
- port_obj = port.get(None)
- generator = json_results_generator.JSONResultsGenerator(port_obj,
- self.builder_name, self.build_name, self.build_number,
- '',
- None, # don't fetch past json results archive
- test_timings,
- failed_tests_dict,
- passed_tests,
- (),
- tests_set)
-
- # Test incremental json results
- incremental_json = generator.get_json(incremental=True)
- self._verify_json_results(
- tests_set,
- test_timings,
- failed_tests,
- PASS_tests,
- DISABLED_tests,
- FLAKY_tests,
- incremental_json,
- 1)
-
- # Test aggregated json results
- generator.set_archived_results(self._json)
- json = generator.get_json(incremental=False)
- self._json = json
- self._num_runs += 1
- self._tests_set |= tests_set
- self._test_timings.update(test_timings)
- self._failed_tests.update(failed_tests)
- self._PASS_tests |= PASS_tests
- self._DISABLED_tests |= DISABLED_tests
- self._FLAKY_tests |= FLAKY_tests
- self._verify_json_results(
- self._tests_set,
- self._test_timings,
- self._failed_tests,
- self._PASS_tests,
- self._DISABLED_tests,
- self._FLAKY_tests,
- self._json,
- self._num_runs)
-
- def _verify_json_results(self, tests_set, test_timings, failed_tests,
- PASS_tests, DISABLED_tests, FLAKY_tests,
- json, num_runs):
- # Aliasing to a short name for better access to its constants.
- JRG = json_results_generator.JSONResultsGenerator
-
- self.assertTrue(JRG.VERSION_KEY in json)
- self.assertTrue(self.builder_name in json)
-
- buildinfo = json[self.builder_name]
- self.assertTrue(JRG.FIXABLE in buildinfo)
- self.assertTrue(JRG.TESTS in buildinfo)
- self.assertEqual(len(buildinfo[JRG.BUILD_NUMBERS]), num_runs)
- self.assertEqual(buildinfo[JRG.BUILD_NUMBERS][0], self.build_number)
-
- if tests_set or DISABLED_tests:
- fixable = {}
- for fixable_items in buildinfo[JRG.FIXABLE]:
- for (type, count) in fixable_items.iteritems():
- if type in fixable:
- fixable[type] = fixable[type] + count
- else:
- fixable[type] = count
-
- if PASS_tests:
- self.assertEqual(fixable[JRG.PASS_RESULT], len(PASS_tests))
- else:
- self.assertTrue(JRG.PASS_RESULT not in fixable or
- fixable[JRG.PASS_RESULT] == 0)
- if DISABLED_tests:
- self.assertEqual(fixable[JRG.SKIP_RESULT], len(DISABLED_tests))
- else:
- self.assertTrue(JRG.SKIP_RESULT not in fixable or
- fixable[JRG.SKIP_RESULT] == 0)
- if FLAKY_tests:
- self.assertEqual(fixable[JRG.FLAKY_RESULT], len(FLAKY_tests))
- else:
- self.assertTrue(JRG.FLAKY_RESULT not in fixable or
- fixable[JRG.FLAKY_RESULT] == 0)
-
- if failed_tests:
- tests = buildinfo[JRG.TESTS]
- for test_name in failed_tests:
- self.assertTrue(test_name in tests)
- test = tests[test_name]
-
- failed = 0
- for result in test[JRG.RESULTS]:
- if result[1] == JRG.FAIL_RESULT:
- failed = result[0]
-
- self.assertEqual(1, failed)
-
- timing_count = 0
- for timings in test[JRG.TIMES]:
- if timings[1] == test_timings[test_name]:
- timing_count = timings[0]
- self.assertEqual(1, timing_count)
-
- fixable_count = len(DISABLED_tests | failed_tests)
- if DISABLED_tests or failed_tests:
- self.assertEqual(sum(buildinfo[JRG.FIXABLE_COUNT]), fixable_count)
-
- def test_json_generation(self):
- self._test_json_generation([], [])
- self._test_json_generation(['A1', 'B1'], [])
- self._test_json_generation([], ['FAILS_A2', 'FAILS_B2'])
- self._test_json_generation(['DISABLED_A3', 'DISABLED_B3'], [])
- self._test_json_generation(['A4'], ['B4', 'FAILS_C4'])
- self._test_json_generation(['DISABLED_C5', 'DISABLED_D5'], ['A5', 'B5'])
- self._test_json_generation(
- ['A6', 'B6', 'FAILS_C6', 'DISABLED_E6', 'DISABLED_F6'],
- ['FAILS_D6'])
- self._test_json_generation(
- ['A7', 'FLAKY_B7', 'DISABLED_C7'],
- ['FAILS_D7', 'FLAKY_D8'])
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/message_broker.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/message_broker.py
deleted file mode 100644
index e520a9c..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/message_broker.py
+++ /dev/null
@@ -1,197 +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.
-
-"""Module for handling messages, threads, processes, and concurrency for run-webkit-tests.
-
-Testing is accomplished by having a manager (TestRunner) gather all of the
-tests to be run, and sending messages to a pool of workers (TestShellThreads)
-to run each test. Each worker communicates with one driver (usually
-DumpRenderTree) to run one test at a time and then compare the output against
-what we expected to get.
-
-This modules provides a message broker that connects the manager to the
-workers: it provides a messaging abstraction and message loops, and
-handles launching threads and/or processes depending on the
-requested configuration.
-"""
-
-import logging
-import sys
-import time
-import traceback
-
-import dump_render_tree_thread
-
-_log = logging.getLogger(__name__)
-
-
-def get(port, options):
- """Return an instance of a WorkerMessageBroker."""
- worker_model = options.worker_model
- if worker_model == 'inline':
- return InlineBroker(port, options)
- if worker_model == 'threads':
- return MultiThreadedBroker(port, options)
- raise ValueError('unsupported value for --worker-model: %s' % worker_model)
-
-
-class _WorkerState(object):
- def __init__(self, name):
- self.name = name
- self.thread = None
-
-
-class WorkerMessageBroker(object):
- def __init__(self, port, options):
- self._port = port
- self._options = options
- self._num_workers = int(self._options.child_processes)
-
- # This maps worker names to their _WorkerState values.
- self._workers = {}
-
- def _threads(self):
- return tuple([w.thread for w in self._workers.values()])
-
- def start_workers(self, test_runner):
- """Starts up the pool of workers for running the tests.
-
- Args:
- test_runner: a handle to the manager/TestRunner object
- """
- self._test_runner = test_runner
- for worker_number in xrange(self._num_workers):
- worker = _WorkerState('worker-%d' % worker_number)
- worker.thread = self._start_worker(worker_number, worker.name)
- self._workers[worker.name] = worker
- return self._threads()
-
- def _start_worker(self, worker_number, worker_name):
- raise NotImplementedError
-
- def run_message_loop(self):
- """Loop processing messages until done."""
- raise NotImplementedError
-
- def cancel_workers(self):
- """Cancel/interrupt any workers that are still alive."""
- pass
-
- def cleanup(self):
- """Perform any necessary cleanup on shutdown."""
- pass
-
-
-class InlineBroker(WorkerMessageBroker):
- def _start_worker(self, worker_number, worker_name):
- # FIXME: Replace with something that isn't a thread.
- thread = dump_render_tree_thread.TestShellThread(self._port,
- self._options, worker_number, worker_name,
- self._test_runner._current_filename_queue,
- self._test_runner._result_queue)
- # Note: Don't start() the thread! If we did, it would actually
- # create another thread and start executing it, and we'd no longer
- # be single-threaded.
- return thread
-
- def run_message_loop(self):
- thread = self._threads()[0]
- thread.run_in_main_thread(self._test_runner,
- self._test_runner._current_result_summary)
- self._test_runner.update()
-
-
-class MultiThreadedBroker(WorkerMessageBroker):
- def _start_worker(self, worker_number, worker_name):
- thread = dump_render_tree_thread.TestShellThread(self._port,
- self._options, worker_number, worker_name,
- self._test_runner._current_filename_queue,
- self._test_runner._result_queue)
- thread.start()
- return thread
-
- def run_message_loop(self):
- threads = self._threads()
-
- # Loop through all the threads waiting for them to finish.
- some_thread_is_alive = True
- while some_thread_is_alive:
- some_thread_is_alive = False
- t = time.time()
- for thread in threads:
- exception_info = thread.exception_info()
- if exception_info is not None:
- # Re-raise the thread's exception here to make it
- # clear that testing was aborted. Otherwise,
- # the tests that did not run would be assumed
- # to have passed.
- raise exception_info[0], exception_info[1], exception_info[2]
-
- if thread.isAlive():
- some_thread_is_alive = True
- next_timeout = thread.next_timeout()
- if next_timeout and t > next_timeout:
- log_wedged_worker(thread.getName(), thread.id())
- thread.clear_next_timeout()
-
- self._test_runner.update()
-
- if some_thread_is_alive:
- time.sleep(0.01)
-
- def cancel_workers(self):
- threads = self._threads()
- for thread in threads:
- thread.cancel()
-
-
-def log_wedged_worker(name, id):
- """Log information about the given worker state."""
- stack = _find_thread_stack(id)
- assert(stack is not None)
- _log.error("")
- _log.error("%s (tid %d) is wedged" % (name, id))
- _log_stack(stack)
- _log.error("")
-
-
-def _find_thread_stack(id):
- """Returns a stack object that can be used to dump a stack trace for
- the given thread id (or None if the id is not found)."""
- for thread_id, stack in sys._current_frames().items():
- if thread_id == id:
- return stack
- return None
-
-
-def _log_stack(stack):
- """Log a stack trace to log.error()."""
- for filename, lineno, name, line in traceback.extract_stack(stack):
- _log.error('File: "%s", line %d, in %s' % (filename, lineno, name))
- if line:
- _log.error(' %s' % line.strip())
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/message_broker_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/message_broker_unittest.py
deleted file mode 100644
index 6f04fd3..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/message_broker_unittest.py
+++ /dev/null
@@ -1,183 +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.
-
-import logging
-import Queue
-import sys
-import thread
-import threading
-import time
-import unittest
-
-from webkitpy.common import array_stream
-from webkitpy.common.system import outputcapture
-from webkitpy.tool import mocktool
-
-from webkitpy.layout_tests import run_webkit_tests
-
-import message_broker
-
-
-class TestThread(threading.Thread):
- def __init__(self, started_queue, stopping_queue):
- threading.Thread.__init__(self)
- self._thread_id = None
- self._started_queue = started_queue
- self._stopping_queue = stopping_queue
- self._timeout = False
- self._timeout_queue = Queue.Queue()
- self._exception_info = None
-
- def id(self):
- return self._thread_id
-
- def getName(self):
- return "worker-0"
-
- def run(self):
- self._covered_run()
-
- def _covered_run(self):
- # FIXME: this is a separate routine to work around a bug
- # in coverage: see http://bitbucket.org/ned/coveragepy/issue/85.
- self._thread_id = thread.get_ident()
- try:
- self._started_queue.put('')
- msg = self._stopping_queue.get()
- if msg == 'KeyboardInterrupt':
- raise KeyboardInterrupt
- elif msg == 'Exception':
- raise ValueError()
- elif msg == 'Timeout':
- self._timeout = True
- self._timeout_queue.get()
- except:
- self._exception_info = sys.exc_info()
-
- def exception_info(self):
- return self._exception_info
-
- def next_timeout(self):
- if self._timeout:
- self._timeout_queue.put('done')
- return time.time() - 10
- return time.time()
-
- def clear_next_timeout(self):
- self._next_timeout = None
-
-class TestHandler(logging.Handler):
- def __init__(self, astream):
- logging.Handler.__init__(self)
- self._stream = astream
-
- def emit(self, record):
- self._stream.write(self.format(record))
-
-
-class MultiThreadedBrokerTest(unittest.TestCase):
- class MockTestRunner(object):
- def __init__(self):
- pass
-
- def __del__(self):
- pass
-
- def update(self):
- pass
-
- def run_one_thread(self, msg):
- runner = self.MockTestRunner()
- port = None
- options = mocktool.MockOptions(child_processes='1')
- starting_queue = Queue.Queue()
- stopping_queue = Queue.Queue()
- broker = message_broker.MultiThreadedBroker(port, options)
- broker._test_runner = runner
- child_thread = TestThread(starting_queue, stopping_queue)
- broker._workers['worker-0'] = message_broker._WorkerState('worker-0')
- broker._workers['worker-0'].thread = child_thread
- child_thread.start()
- started_msg = starting_queue.get()
- stopping_queue.put(msg)
- return broker.run_message_loop()
-
- def test_basic(self):
- interrupted = self.run_one_thread('')
- self.assertFalse(interrupted)
-
- def test_interrupt(self):
- self.assertRaises(KeyboardInterrupt, self.run_one_thread, 'KeyboardInterrupt')
-
- def test_timeout(self):
- oc = outputcapture.OutputCapture()
- oc.capture_output()
- interrupted = self.run_one_thread('Timeout')
- self.assertFalse(interrupted)
- oc.restore_output()
-
- def test_exception(self):
- self.assertRaises(ValueError, self.run_one_thread, 'Exception')
-
-
-class Test(unittest.TestCase):
- def test_find_thread_stack_found(self):
- id, stack = sys._current_frames().items()[0]
- found_stack = message_broker._find_thread_stack(id)
- self.assertNotEqual(found_stack, None)
-
- def test_find_thread_stack_not_found(self):
- found_stack = message_broker._find_thread_stack(0)
- self.assertEqual(found_stack, None)
-
- def test_log_wedged_worker(self):
- oc = outputcapture.OutputCapture()
- oc.capture_output()
- logger = message_broker._log
- astream = array_stream.ArrayStream()
- handler = TestHandler(astream)
- logger.addHandler(handler)
-
- starting_queue = Queue.Queue()
- stopping_queue = Queue.Queue()
- child_thread = TestThread(starting_queue, stopping_queue)
- child_thread.start()
- msg = starting_queue.get()
-
- message_broker.log_wedged_worker(child_thread.getName(),
- child_thread.id())
- stopping_queue.put('')
- child_thread.join(timeout=1.0)
-
- self.assertFalse(astream.empty())
- self.assertFalse(child_thread.isAlive())
- oc.restore_output()
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream.py
deleted file mode 100644
index 20646a1..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream.py
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/usr/bin/env 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.
-
-"""
-Package that implements a stream wrapper that has 'meters' as well as
-regular output. A 'meter' is a single line of text that can be erased
-and rewritten repeatedly, without producing multiple lines of output. It
-can be used to produce effects like progress bars.
-
-This package should only be called by the printing module in the layout_tests
-package.
-"""
-
-import logging
-
-_log = logging.getLogger("webkitpy.layout_tests.metered_stream")
-
-
-class MeteredStream:
- """This class is a wrapper around a stream that allows you to implement
- meters (progress bars, etc.).
-
- It can be used directly as a stream, by calling write(), but provides
- two other methods for output, update(), and progress().
-
- In normal usage, update() will overwrite the output of the immediately
- preceding update() (write() also will overwrite update()). So, calling
- multiple update()s in a row can provide an updating status bar (note that
- if an update string contains newlines, only the text following the last
- newline will be overwritten/erased).
-
- If the MeteredStream is constructed in "verbose" mode (i.e., by passing
- verbose=true), then update() no longer overwrite a previous update(), and
- instead the call is equivalent to write(), although the text is
- actually sent to the logger rather than to the stream passed
- to the constructor.
-
- progress() is just like update(), except that if you are in verbose mode,
- progress messages are not output at all (they are dropped). This is
- used for things like progress bars which are presumed to be unwanted in
- verbose mode.
-
- Note that the usual usage for this class is as a destination for
- a logger that can also be written to directly (i.e., some messages go
- through the logger, some don't). We thus have to dance around a
- layering inversion in update() for things to work correctly.
- """
-
- def __init__(self, verbose, stream):
- """
- Args:
- verbose: whether progress is a no-op and updates() aren't overwritten
- stream: output stream to write to
- """
- self._dirty = False
- self._verbose = verbose
- self._stream = stream
- self._last_update = ""
-
- def write(self, txt):
- """Write to the stream, overwriting and resetting the meter."""
- if self._dirty:
- self._write(txt)
- self._dirty = False
- self._last_update = ''
- else:
- self._stream.write(txt)
-
- def flush(self):
- """Flush any buffered output."""
- self._stream.flush()
-
- def progress(self, str):
- """
- Write a message to the stream that will get overwritten.
-
- This is used for progress updates that don't need to be preserved in
- the log. If the MeteredStream was initialized with verbose==True,
- then this output is discarded. We have this in case we are logging
- lots of output and the update()s will get lost or won't work
- properly (typically because verbose streams are redirected to files).
-
- """
- if self._verbose:
- return
- self._write(str)
-
- def update(self, str):
- """
- Write a message that is also included when logging verbosely.
-
- This routine preserves the same console logging behavior as progress(),
- but will also log the message if verbose() was true.
-
- """
- # Note this is a separate routine that calls either into the logger
- # or the metering stream. We have to be careful to avoid a layering
- # inversion (stream calling back into the logger).
- if self._verbose:
- _log.info(str)
- else:
- self._write(str)
-
- def _write(self, str):
- """Actually write the message to the stream."""
-
- # FIXME: Figure out if there is a way to detect if we're writing
- # to a stream that handles CRs correctly (e.g., terminals). That might
- # be a cleaner way of handling this.
-
- # Print the necessary number of backspaces to erase the previous
- # message.
- if len(self._last_update):
- 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
deleted file mode 100644
index 9421ff8..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/metered_stream_unittest.py
+++ /dev/null
@@ -1,115 +0,0 @@
-#!/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 metered_stream.py."""
-
-import os
-import optparse
-import pdb
-import sys
-import unittest
-
-from webkitpy.common.array_stream import ArrayStream
-from webkitpy.layout_tests.layout_package import metered_stream
-
-
-class TestMeteredStream(unittest.TestCase):
- def test_regular(self):
- a = ArrayStream()
- m = metered_stream.MeteredStream(verbose=False, stream=a)
- self.assertTrue(a.empty())
-
- # basic test - note that the flush() is a no-op, but we include it
- # for coverage.
- m.write("foo")
- m.flush()
- exp = ['foo']
- self.assertEquals(a.get(), exp)
-
- # now check that a second write() does not overwrite the first.
- m.write("bar")
- exp.append('bar')
- self.assertEquals(a.get(), exp)
-
- m.update("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")
- 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")
- 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")
- 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.
-
- # Note also that we do not back up to erase the most recent write(),
- # i.e., write()s do not get erased.
- a.reset()
- m.update("foo\nbar")
- m.update("baz")
- self.assertEquals(a.get(), ['foo\nbar', '\b\b\b \b\b\b', 'baz'])
-
- def test_verbose(self):
- a = ArrayStream()
- m = metered_stream.MeteredStream(verbose=True, stream=a)
- self.assertTrue(a.empty())
- m.write("foo")
- self.assertEquals(a.get(), ['foo'])
-
- import logging
- b = ArrayStream()
- logger = logging.getLogger()
- handler = logging.StreamHandler(b)
- logger.addHandler(handler)
- m.update("bar")
- logger.handlers.remove(handler)
- self.assertEquals(a.get(), ['foo'])
- self.assertEquals(b.get(), ['bar\n'])
-
- m.progress("dropped")
- self.assertEquals(a.get(), ['foo'])
- self.assertEquals(b.get(), ['bar\n'])
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py
deleted file mode 100644
index 7a6aad1..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing.py
+++ /dev/null
@@ -1,553 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Package that handles non-debug, non-file output for run-webkit-tests."""
-
-import logging
-import optparse
-import os
-import pdb
-
-from webkitpy.layout_tests.layout_package import metered_stream
-from webkitpy.layout_tests.layout_package import test_expectations
-
-_log = logging.getLogger("webkitpy.layout_tests.printer")
-
-TestExpectationsFile = test_expectations.TestExpectationsFile
-
-NUM_SLOW_TESTS_TO_LOG = 10
-
-PRINT_DEFAULT = ("misc,one-line-progress,one-line-summary,unexpected,"
- "unexpected-results,updates")
-PRINT_EVERYTHING = ("actual,config,expected,misc,one-line-progress,"
- "one-line-summary,slowest,timing,unexpected,"
- "unexpected-results,updates")
-
-HELP_PRINTING = """
-Output for run-webkit-tests is controlled by a comma-separated list of
-values passed to --print. Values either influence the overall output, or
-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
-
-At the beginning of the run:
- config print the test run configuration
- expected print a summary of what is expected to happen
- (# passes, # failures, etc.)
-
-During the run:
- detailed-progress print one dot per test completed
- one-line-progress print a one-line progress bar
- unexpected print any unexpected results as they occur
- updates print updates on which stage is executing
- trace-everything print detailed info on every test's results
- (baselines, expectation, time it took to run). If
- this is specified it will override the '*-progress'
- options, the 'trace-unexpected' option, and the
- 'unexpected' option.
- trace-unexpected like 'trace-everything', but only for tests with
- unexpected results. If this option is specified,
- it will override the 'unexpected' option.
-
-At the end of the run:
- actual print a summary of the actual results
- slowest print %(slowest)d slowest tests and the time they took
- timing print timing statistics
- unexpected-results print a list of the tests with unexpected results
- one-line-summary print a one-line summary of the run
-
-Notes:
- - 'detailed-progress' can only be used if running in a single thread
- (using --child-processes=1) or a single queue of tests (using
- --experimental-fully-parallel). If these conditions aren't true,
- 'one-line-progress' will be used instead.
- - If both 'detailed-progress' and 'one-line-progress' are specified (and
- both are possible), 'detailed-progress' will be used.
- - If 'nothing' is specified, it overrides all of the other options.
- - Specifying --verbose is equivalent to --print everything plus it
- changes the format of the log messages to add timestamps and other
- information. If you specify --verbose and --print X, then X overrides
- the --print everything implied by --verbose.
-
---print 'everything' is equivalent to --print '%(everything)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
- # 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.")),
- optparse.make_option("--help-printing", action="store_true",
- help="show detailed help on controlling print output"),
- optparse.make_option("-v", "--verbose", action="store_true",
- default=False, help="include debug-level logging"),
- ]
-
-
-def parse_print_options(print_options, verbose, child_processes,
- is_fully_parallel):
- """Parse the options provided to --print and dedup and rank them.
-
- Returns
- a set() of switches that govern how logging is done
-
- """
- if print_options:
- switches = set(print_options.split(','))
- elif verbose:
- switches = set(PRINT_EVERYTHING.split(','))
- else:
- switches = set(PRINT_DEFAULT.split(','))
-
- if 'nothing' in switches:
- return set()
-
- if (child_processes != 1 and not is_fully_parallel and
- 'detailed-progress' in switches):
- _log.warn("Can only print 'detailed-progress' if running "
- "with --child-processes=1 or "
- "with --experimental-fully-parallel. "
- "Using 'one-line-progress' instead.")
- switches.discard('detailed-progress')
- switches.add('one-line-progress')
-
- if 'everything' in switches:
- 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')
-
- if 'trace-everything' in switches:
- switches.discard('detailed-progress')
- switches.discard('one-line-progress')
- switches.discard('trace-unexpected')
- switches.discard('unexpected')
-
- if 'trace-unexpected' in switches:
- switches.discard('unexpected')
-
- return switches
-
-
-def _configure_logging(stream, verbose):
- log_fmt = '%(message)s'
- log_datefmt = '%y%m%d %H:%M:%S'
- log_level = logging.INFO
- if verbose:
- log_fmt = ('%(asctime)s %(process)d %(filename)s:%(lineno)d '
- '%(levelname)s %(message)s')
- log_level = logging.DEBUG
-
- root = logging.getLogger()
- handler = logging.StreamHandler(stream)
- handler.setFormatter(logging.Formatter(log_fmt, None))
- root.addHandler(handler)
- root.setLevel(log_level)
- return handler
-
-
-def _restore_logging(handler_to_remove):
- root = logging.getLogger()
- root.handlers.remove(handler_to_remove)
-
-
-class Printer(object):
- """Class handling all non-debug-logging printing done by run-webkit-tests.
-
- Printing from run-webkit-tests falls into two buckets: general or
- regular output that is read only by humans and can be changed at any
- time, and output that is parsed by buildbots (and humans) and hence
- must be changed more carefully and in coordination with the buildbot
- parsing code (in chromium.org's buildbot/master.chromium/scripts/master/
- log_parser/webkit_test_command.py script).
-
- By default the buildbot-parsed code gets logged to stdout, and regular
- output gets logged to stderr."""
- def __init__(self, port, options, regular_output, buildbot_output,
- child_processes, is_fully_parallel):
- """
- Args
- port interface to port-specific routines
- options OptionParser object with command line settings
- regular_output stream to which output intended only for humans
- should be written
- buildbot_output stream to which output intended to be read by
- the buildbots (and humans) should be written
- child_processes number of parallel threads running (usually
- controlled by --child-processes)
- is_fully_parallel are the tests running in a single queue, or
- in shards (usually controlled by
- --experimental-fully-parallel)
-
- Note that the last two args are separate rather than bundled into
- the options structure so that this object does not assume any flags
- set in options that weren't returned from logging_options(), above.
- The two are used to determine whether or not we can sensibly use
- the 'detailed-progress' option, or can only use 'one-line-progress'.
- """
- self._buildbot_stream = buildbot_output
- self._options = options
- self._port = port
- self._stream = regular_output
-
- # These are used for --print detailed-progress to track status by
- # directory.
- self._current_dir = None
- self._current_progress_str = ""
- self._current_test_number = 0
-
- self._meter = metered_stream.MeteredStream(options.verbose,
- regular_output)
- self._logging_handler = _configure_logging(self._meter,
- options.verbose)
-
- self.switches = parse_print_options(options.print_options,
- options.verbose, child_processes, is_fully_parallel)
-
- def cleanup(self):
- """Restore logging configuration to its initial settings."""
- if self._logging_handler:
- _restore_logging(self._logging_handler)
- self._logging_handler = None
-
- def __del__(self):
- self.cleanup()
-
- # These two routines just hide the implementation of the switches.
- def disabled(self, option):
- return not option in self.switches
-
- def enabled(self, option):
- return option in self.switches
-
- def help_printing(self):
- self._write(HELP_PRINTING)
-
- def print_actual(self, msg):
- if self.disabled('actual'):
- return
- self._buildbot_stream.write("%s\n" % msg)
-
- def print_config(self, msg):
- self.write(msg, 'config')
-
- def print_expected(self, msg):
- self.write(msg, 'expected')
-
- def print_timing(self, msg):
- self.write(msg, 'timing')
-
- def print_one_line_summary(self, total, expected, unexpected):
- """Print a one-line summary of the test run to stdout.
-
- Args:
- total: total number of tests run
- expected: number of expected results
- unexpected: number of unexpected results
- """
- if self.disabled('one-line-summary'):
- return
-
- incomplete = total - expected - unexpected
- if incomplete:
- self._write("")
- incomplete_str = " (%d didn't run)" % incomplete
- expected_str = str(expected)
- else:
- incomplete_str = ""
- expected_str = "All %d" % expected
-
- if unexpected == 0:
- self._write("%s tests ran as expected%s." %
- (expected_str, incomplete_str))
- elif expected == 1:
- self._write("1 test ran as expected, %d didn't%s:" %
- (unexpected, incomplete_str))
- else:
- self._write("%d tests ran as expected, %d didn't%s:" %
- (expected, unexpected, incomplete_str))
- self._write("")
-
- def print_test_result(self, result, expected, exp_str, got_str):
- """Print the result of the test as determined by --print.
-
- This routine is used to print the details of each test as it completes.
-
- Args:
- result - The actual TestResult object
- expected - Whether the result we got was an expected result
- exp_str - What we expected to get (used for tracing)
- got_str - What we actually got (used for tracing)
-
- Note that we need all of these arguments even though they seem
- somewhat redundant, in order to keep this routine from having to
- known anything about the set of expectations.
- """
- if (self.enabled('trace-everything') or
- self.enabled('trace-unexpected') and not expected):
- self._print_test_trace(result, exp_str, got_str)
- elif (not expected and self.enabled('unexpected') and
- self.disabled('detailed-progress')):
- # Note: 'detailed-progress' handles unexpected results internally,
- # so we skip it here.
- self._print_unexpected_test_result(result)
-
- def _print_test_trace(self, result, exp_str, got_str):
- """Print detailed results of a test (triggered by --print trace-*).
- For each test, print:
- - location of the expected baselines
- - expected results
- - actual result
- - timing info
- """
- filename = result.filename
- test_name = self._port.relative_test_filename(filename)
- self._write('trace: %s' % test_name)
- txt_file = self._port.expected_filename(filename, '.txt')
- if self._port.path_exists(txt_file):
- self._write(' txt: %s' %
- self._port.relative_test_filename(txt_file))
- else:
- self._write(' txt: <none>')
- checksum_file = self._port.expected_filename(filename, '.checksum')
- if self._port.path_exists(checksum_file):
- self._write(' sum: %s' %
- self._port.relative_test_filename(checksum_file))
- else:
- self._write(' sum: <none>')
- png_file = self._port.expected_filename(filename, '.png')
- if self._port.path_exists(png_file):
- self._write(' png: %s' %
- self._port.relative_test_filename(png_file))
- else:
- self._write(' png: <none>')
- self._write(' exp: %s' % exp_str)
- self._write(' got: %s' % got_str)
- self._write(' took: %-.3f' % result.test_run_time)
- self._write('')
-
- def _print_unexpected_test_result(self, result):
- """Prints one unexpected test result line."""
- desc = TestExpectationsFile.EXPECTATION_DESCRIPTIONS[result.type][0]
- self.write(" %s -> unexpected %s" %
- (self._port.relative_test_filename(result.filename),
- desc), "unexpected")
-
- def print_progress(self, result_summary, retrying, test_list):
- """Print progress through the tests as determined by --print."""
- if self.enabled('detailed-progress'):
- self._print_detailed_progress(result_summary, test_list)
- elif self.enabled('one-line-progress'):
- self._print_one_line_progress(result_summary, retrying)
- else:
- return
-
- if result_summary.remaining == 0:
- self._meter.update('')
-
- def _print_one_line_progress(self, result_summary, retrying):
- """Displays the progress through the test run."""
- percent_complete = 100 * (result_summary.expected +
- result_summary.unexpected) / result_summary.total
- action = "Testing"
- if retrying:
- action = "Retrying"
- self._meter.progress("%s (%d%%): %d ran as expected, %d didn't,"
- " %d left" % (action, percent_complete, result_summary.expected,
- result_summary.unexpected, result_summary.remaining))
-
- def _print_detailed_progress(self, result_summary, test_list):
- """Display detailed progress output where we print the directory name
- and one dot for each completed test. This is triggered by
- "--log detailed-progress"."""
- if self._current_test_number == len(test_list):
- return
-
- next_test = test_list[self._current_test_number]
- next_dir = os.path.dirname(
- self._port.relative_test_filename(next_test))
- if self._current_progress_str == "":
- self._current_progress_str = "%s: " % (next_dir)
- self._current_dir = next_dir
-
- while next_test in result_summary.results:
- if next_dir != self._current_dir:
- self._meter.write("%s\n" % (self._current_progress_str))
- self._current_progress_str = "%s: ." % (next_dir)
- self._current_dir = next_dir
- else:
- self._current_progress_str += "."
-
- if (next_test in result_summary.unexpected_results and
- self.enabled('unexpected')):
- self._meter.write("%s\n" % self._current_progress_str)
- test_result = result_summary.results[next_test]
- self._print_unexpected_test_result(test_result)
- self._current_progress_str = "%s: " % self._current_dir
-
- self._current_test_number += 1
- if self._current_test_number == len(test_list):
- break
-
- next_test = test_list[self._current_test_number]
- next_dir = os.path.dirname(
- self._port.relative_test_filename(next_test))
-
- if result_summary.remaining:
- remain_str = " (%d)" % (result_summary.remaining)
- self._meter.progress("%s%s" % (self._current_progress_str,
- remain_str))
- else:
- self._meter.progress("%s" % (self._current_progress_str))
-
- def print_unexpected_results(self, unexpected_results):
- """Prints a list of the unexpected results to the buildbot stream."""
- if self.disabled('unexpected-results'):
- return
-
- passes = {}
- flaky = {}
- regressions = {}
-
- for test, results in unexpected_results['tests'].iteritems():
- actual = results['actual'].split(" ")
- expected = results['expected'].split(" ")
- if actual == ['PASS']:
- if 'CRASH' in expected:
- _add_to_dict_of_lists(passes,
- 'Expected to crash, but passed',
- test)
- elif 'TIMEOUT' in expected:
- _add_to_dict_of_lists(passes,
- 'Expected to timeout, but passed',
- test)
- else:
- _add_to_dict_of_lists(passes,
- 'Expected to fail, but passed',
- test)
- elif len(actual) > 1:
- # We group flaky tests by the first actual result we got.
- _add_to_dict_of_lists(flaky, actual[0], test)
- else:
- _add_to_dict_of_lists(regressions, results['actual'], test)
-
- if len(passes) or len(flaky) or len(regressions):
- self._buildbot_stream.write("\n")
-
- if len(passes):
- for key, tests in passes.iteritems():
- self._buildbot_stream.write("%s: (%d)\n" % (key, len(tests)))
- tests.sort()
- for test in tests:
- self._buildbot_stream.write(" %s\n" % test)
- self._buildbot_stream.write("\n")
- self._buildbot_stream.write("\n")
-
- if len(flaky):
- descriptions = TestExpectationsFile.EXPECTATION_DESCRIPTIONS
- for key, tests in flaky.iteritems():
- result = TestExpectationsFile.EXPECTATIONS[key.lower()]
- self._buildbot_stream.write("Unexpected flakiness: %s (%d)\n"
- % (descriptions[result][1], len(tests)))
- tests.sort()
-
- for test in tests:
- result = unexpected_results['tests'][test]
- actual = result['actual'].split(" ")
- expected = result['expected'].split(" ")
- result = TestExpectationsFile.EXPECTATIONS[key.lower()]
- new_expectations_list = list(set(actual) | set(expected))
- self._buildbot_stream.write(" %s = %s\n" %
- (test, " ".join(new_expectations_list)))
- self._buildbot_stream.write("\n")
- self._buildbot_stream.write("\n")
-
- if len(regressions):
- descriptions = TestExpectationsFile.EXPECTATION_DESCRIPTIONS
- for key, tests in regressions.iteritems():
- result = TestExpectationsFile.EXPECTATIONS[key.lower()]
- self._buildbot_stream.write(
- "Regressions: Unexpected %s : (%d)\n" % (
- descriptions[result][1], len(tests)))
- tests.sort()
- for test in tests:
- self._buildbot_stream.write(" %s = %s\n" % (test, key))
- self._buildbot_stream.write("\n")
- self._buildbot_stream.write("\n")
-
- if len(unexpected_results['tests']) and self._options.verbose:
- self._buildbot_stream.write("%s\n" % ("-" * 78))
-
- def print_update(self, msg):
- if self.disabled('updates'):
- return
- self._meter.update(msg)
-
- def write(self, msg, option="misc"):
- if self.disabled(option):
- return
- self._write(msg)
-
- def _write(self, msg):
- # FIXME: we could probably get away with calling _log.info() all of
- # the time, but there doesn't seem to be a good way to test the output
- # from the logger :(.
- if self._options.verbose:
- _log.info(msg)
- else:
- self._meter.write("%s\n" % msg)
-
-#
-# Utility routines used by the Controller class
-#
-
-
-def _add_to_dict_of_lists(dict, key, value):
- dict.setdefault(key, []).append(value)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing_unittest.py
deleted file mode 100644
index 27a6a29..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/printing_unittest.py
+++ /dev/null
@@ -1,606 +0,0 @@
-#!/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 printing.py."""
-
-import os
-import optparse
-import pdb
-import sys
-import unittest
-import logging
-
-from webkitpy.common import array_stream
-from webkitpy.common.system import logtesting
-from webkitpy.layout_tests import port
-from webkitpy.layout_tests.layout_package import printing
-from webkitpy.layout_tests.layout_package import test_results
-from webkitpy.layout_tests.layout_package import test_expectations
-from webkitpy.layout_tests.layout_package import test_failures
-from webkitpy.layout_tests import run_webkit_tests
-
-
-def get_options(args):
- print_options = printing.print_options()
- option_parser = optparse.OptionParser(option_list=print_options)
- return option_parser.parse_args(args)
-
-
-class TestUtilityFunctions(unittest.TestCase):
- def test_configure_logging(self):
- options, args = get_options([])
- stream = array_stream.ArrayStream()
- handler = printing._configure_logging(stream, options.verbose)
- logging.info("this should be logged")
- self.assertFalse(stream.empty())
-
- stream.reset()
- logging.debug("this should not be logged")
- self.assertTrue(stream.empty())
-
- printing._restore_logging(handler)
-
- stream.reset()
- options, args = get_options(['--verbose'])
- handler = printing._configure_logging(stream, options.verbose)
- logging.debug("this should be logged")
- self.assertFalse(stream.empty())
- printing._restore_logging(handler)
-
- def test_print_options(self):
- options, args = get_options([])
- self.assertTrue(options is not None)
-
- def test_parse_print_options(self):
- def test_switches(args, expected_switches_str,
- verbose=False, child_processes=1,
- is_fully_parallel=False):
- 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([], printing.PRINT_DEFAULT)
-
- # test that verbose defaults to everything
- test_switches([], printing.PRINT_EVERYTHING, verbose=True)
-
- # test that --print default does what it's supposed to
- test_switches(['--print', 'default'], printing.PRINT_DEFAULT)
-
- # test that --print nothing does what it's supposed to
- test_switches(['--print', 'nothing'], None)
-
- # test that --print everything does what it's supposed to
- test_switches(['--print', 'everything'], printing.PRINT_EVERYTHING)
-
- # this tests that '--print X' overrides '--verbose'
- test_switches(['--print', 'actual'], 'actual', verbose=True)
-
-
-
-class Testprinter(unittest.TestCase):
- def get_printer(self, args=None, single_threaded=False,
- is_fully_parallel=False):
- printing_options = printing.print_options()
- option_parser = optparse.OptionParser(option_list=printing_options)
- options, args = option_parser.parse_args(args)
- self._port = port.get('test', options)
- nproc = 2
- if single_threaded:
- nproc = 1
-
- regular_output = array_stream.ArrayStream()
- buildbot_output = array_stream.ArrayStream()
- printer = printing.Printer(self._port, options, regular_output,
- buildbot_output, single_threaded,
- is_fully_parallel)
- return printer, regular_output, buildbot_output
-
- def get_result(self, test, result_type=test_expectations.PASS, run_time=0):
- failures = []
- if result_type == test_expectations.TIMEOUT:
- failures = [test_failures.FailureTimeout()]
- elif result_type == test_expectations.CRASH:
- failures = [test_failures.FailureCrash()]
- path = os.path.join(self._port.layout_tests_dir(), test)
- return test_results.TestResult(path, failures, run_time,
- total_time_for_all_diffs=0,
- time_for_diffs=0)
-
- def get_result_summary(self, tests, expectations_str):
- test_paths = [os.path.join(self._port.layout_tests_dir(), test) for
- test in tests]
- expectations = test_expectations.TestExpectations(
- self._port, test_paths, expectations_str,
- self._port.test_platform_name(), is_debug_mode=False,
- is_lint_mode=False)
-
- rs = run_webkit_tests.ResultSummary(expectations, test_paths)
- return test_paths, rs, expectations
-
- def test_help_printer(self):
- # Here and below we'll call the "regular" printer err and the
- # buildbot printer out; this corresponds to how things run on the
- # bots with stderr and stdout.
- printer, err, out = self.get_printer()
-
- # This routine should print something to stdout. testing what it is
- # is kind of pointless.
- printer.help_printing()
- self.assertFalse(err.empty())
- self.assertTrue(out.empty())
-
- def do_switch_tests(self, method_name, switch, to_buildbot,
- message='hello', exp_err=None, exp_bot=None):
- def do_helper(method_name, switch, message, exp_err, exp_bot):
- printer, err, bot = self.get_printer(['--print', switch])
- getattr(printer, method_name)(message)
- self.assertEqual(err.get(), exp_err)
- self.assertEqual(bot.get(), exp_bot)
-
- if to_buildbot:
- if exp_err is None:
- exp_err = []
- if exp_bot is None:
- exp_bot = [message + "\n"]
- else:
- if exp_err is None:
- exp_err = [message + "\n"]
- if exp_bot is None:
- exp_bot = []
- do_helper(method_name, 'nothing', 'hello', [], [])
- do_helper(method_name, switch, 'hello', exp_err, exp_bot)
- do_helper(method_name, 'everything', 'hello', exp_err, exp_bot)
-
- def test_configure_and_cleanup(self):
- # This test verifies that calling cleanup repeatedly and deleting
- # the object is safe.
- printer, err, out = self.get_printer(['--print', 'everything'])
- printer.cleanup()
- printer.cleanup()
- printer = None
-
- def test_print_actual(self):
- # Actual results need to be logged to the buildbot's stream.
- self.do_switch_tests('print_actual', 'actual', to_buildbot=True)
-
- def test_print_actual_buildbot(self):
- # FIXME: Test that the format of the actual results matches what the
- # buildbot is expecting.
- pass
-
- def test_print_config(self):
- self.do_switch_tests('print_config', 'config', to_buildbot=False)
-
- def test_print_expected(self):
- self.do_switch_tests('print_expected', 'expected', to_buildbot=False)
-
- def test_print_timing(self):
- self.do_switch_tests('print_timing', 'timing', to_buildbot=False)
-
- def test_print_update(self):
- # Note that there shouldn't be a carriage return here; updates()
- # are meant to be overwritten.
- self.do_switch_tests('print_update', 'updates', to_buildbot=False,
- message='hello', exp_err=['hello'])
-
- def test_print_one_line_summary(self):
- printer, err, out = self.get_printer(['--print', 'nothing'])
- printer.print_one_line_summary(1, 1, 0)
- self.assertTrue(err.empty())
-
- printer, err, out = self.get_printer(['--print', 'one-line-summary'])
- printer.print_one_line_summary(1, 1, 0)
- 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, 0)
- self.assertEquals(err.get(), ["All 1 tests ran as expected.\n", "\n"])
-
- err.reset()
- printer.print_one_line_summary(2, 1, 1)
- self.assertEquals(err.get(),
- ["1 test ran as expected, 1 didn't:\n", "\n"])
-
- err.reset()
- printer.print_one_line_summary(3, 2, 1)
- self.assertEquals(err.get(),
- ["2 tests ran as expected, 1 didn't:\n", "\n"])
-
- err.reset()
- printer.print_one_line_summary(3, 2, 0)
- self.assertEquals(err.get(),
- ['\n', "2 tests ran as expected (1 didn't run).\n",
- '\n'])
-
-
- def test_print_test_result(self):
- # Note here that we don't use meaningful exp_str and got_str values;
- # the actual contents of the string are treated opaquely by
- # print_test_result() when tracing, and usually we don't want
- # to test what exactly is printed, just that something
- # was printed (or that nothing was printed).
- #
- # FIXME: this is actually some goofy layering; it would be nice
- # we could refactor it so that the args weren't redundant. Maybe
- # the TestResult should contain what was expected, and the
- # strings could be derived from the TestResult?
- printer, err, out = self.get_printer(['--print', 'nothing'])
- result = self.get_result('passes/image.html')
- printer.print_test_result(result, expected=False, exp_str='',
- got_str='')
- self.assertTrue(err.empty())
-
- printer, err, out = self.get_printer(['--print', 'unexpected'])
- printer.print_test_result(result, expected=True, exp_str='',
- got_str='')
- self.assertTrue(err.empty())
- printer.print_test_result(result, expected=False, exp_str='',
- got_str='')
- self.assertEquals(err.get(),
- [' passes/image.html -> unexpected pass\n'])
-
- printer, err, out = self.get_printer(['--print', 'everything'])
- printer.print_test_result(result, expected=True, exp_str='',
- got_str='')
- self.assertTrue(err.empty())
-
- printer.print_test_result(result, expected=False, exp_str='',
- got_str='')
- self.assertEquals(err.get(),
- [' passes/image.html -> unexpected pass\n'])
-
- printer, err, out = self.get_printer(['--print', 'nothing'])
- printer.print_test_result(result, expected=False, exp_str='',
- got_str='')
- self.assertTrue(err.empty())
-
- printer, err, out = self.get_printer(['--print',
- 'trace-unexpected'])
- printer.print_test_result(result, expected=True, exp_str='',
- got_str='')
- self.assertTrue(err.empty())
-
- printer, err, out = self.get_printer(['--print',
- 'trace-unexpected'])
- printer.print_test_result(result, expected=False, exp_str='',
- got_str='')
- self.assertFalse(err.empty())
-
- printer, err, out = self.get_printer(['--print',
- 'trace-unexpected'])
- result = self.get_result("passes/text.html")
- printer.print_test_result(result, expected=False, exp_str='',
- got_str='')
- self.assertFalse(err.empty())
-
- err.reset()
- printer.print_test_result(result, expected=False, exp_str='',
- got_str='')
- self.assertFalse(err.empty())
-
- printer, err, out = self.get_printer(['--print', 'trace-everything'])
- result = self.get_result('passes/image.html')
- printer.print_test_result(result, expected=True, exp_str='',
- got_str='')
- result = self.get_result('failures/expected/missing_text.html')
- printer.print_test_result(result, expected=True, exp_str='',
- got_str='')
- result = self.get_result('failures/expected/missing_check.html')
- printer.print_test_result(result, expected=True, exp_str='',
- got_str='')
- result = self.get_result('failures/expected/missing_image.html')
- printer.print_test_result(result, expected=True, exp_str='',
- got_str='')
- self.assertFalse(err.empty())
-
- err.reset()
- printer.print_test_result(result, expected=False, exp_str='',
- got_str='')
-
- def test_print_progress(self):
- expectations = ''
-
- # test that we print nothing
- printer, err, out = self.get_printer(['--print', 'nothing'])
- tests = ['passes/text.html', 'failures/expected/timeout.html',
- 'failures/expected/crash.html']
- paths, rs, exp = self.get_result_summary(tests, expectations)
-
- printer.print_progress(rs, False, paths)
- self.assertTrue(out.empty())
- self.assertTrue(err.empty())
-
- printer.print_progress(rs, True, paths)
- self.assertTrue(out.empty())
- self.assertTrue(err.empty())
-
- # test regular functionality
- printer, err, out = self.get_printer(['--print',
- 'one-line-progress'])
- printer.print_progress(rs, False, paths)
- self.assertTrue(out.empty())
- self.assertFalse(err.empty())
-
- err.reset()
- out.reset()
- printer.print_progress(rs, True, paths)
- self.assertFalse(err.empty())
- self.assertTrue(out.empty())
-
- def test_print_progress__detailed(self):
- tests = ['passes/text.html', 'failures/expected/timeout.html',
- 'failures/expected/crash.html']
- expectations = 'failures/expected/timeout.html = TIMEOUT'
-
- # first, test that it is disabled properly
- # should still print one-line-progress
- printer, err, out = self.get_printer(
- ['--print', 'detailed-progress'], single_threaded=False)
- paths, rs, exp = self.get_result_summary(tests, expectations)
- printer.print_progress(rs, False, paths)
- self.assertFalse(err.empty())
- self.assertTrue(out.empty())
-
- # now test the enabled paths
- printer, err, out = self.get_printer(
- ['--print', 'detailed-progress'], single_threaded=True)
- paths, rs, exp = self.get_result_summary(tests, expectations)
- printer.print_progress(rs, False, paths)
- self.assertFalse(err.empty())
- self.assertTrue(out.empty())
-
- err.reset()
- out.reset()
- printer.print_progress(rs, True, paths)
- self.assertFalse(err.empty())
- self.assertTrue(out.empty())
-
- rs.add(self.get_result('passes/text.html', test_expectations.TIMEOUT), False)
- rs.add(self.get_result('failures/expected/timeout.html'), True)
- rs.add(self.get_result('failures/expected/crash.html', test_expectations.CRASH), True)
- err.reset()
- out.reset()
- printer.print_progress(rs, False, paths)
- self.assertFalse(err.empty())
- self.assertTrue(out.empty())
-
- # We only clear the meter when retrying w/ detailed-progress.
- err.reset()
- out.reset()
- printer.print_progress(rs, True, paths)
- self.assertFalse(err.empty())
- self.assertTrue(out.empty())
-
- printer, err, out = self.get_printer(
- ['--print', 'detailed-progress,unexpected'], single_threaded=True)
- paths, rs, exp = self.get_result_summary(tests, expectations)
- printer.print_progress(rs, False, paths)
- self.assertFalse(err.empty())
- self.assertTrue(out.empty())
-
- err.reset()
- out.reset()
- printer.print_progress(rs, True, paths)
- self.assertFalse(err.empty())
- self.assertTrue(out.empty())
-
- rs.add(self.get_result('passes/text.html', test_expectations.TIMEOUT), False)
- rs.add(self.get_result('failures/expected/timeout.html'), True)
- rs.add(self.get_result('failures/expected/crash.html', test_expectations.CRASH), True)
- err.reset()
- out.reset()
- printer.print_progress(rs, False, paths)
- self.assertFalse(err.empty())
- self.assertTrue(out.empty())
-
- # We only clear the meter when retrying w/ detailed-progress.
- err.reset()
- out.reset()
- printer.print_progress(rs, True, paths)
- self.assertFalse(err.empty())
- self.assertTrue(out.empty())
-
- def test_write_nothing(self):
- printer, err, out = self.get_printer(['--print', 'nothing'])
- printer.write("foo")
- self.assertTrue(err.empty())
-
- def test_write_misc(self):
- printer, err, out = self.get_printer(['--print', 'misc'])
- printer.write("foo")
- self.assertFalse(err.empty())
- err.reset()
- printer.write("foo", "config")
- self.assertTrue(err.empty())
-
- def test_write_everything(self):
- printer, err, out = self.get_printer(['--print', 'everything'])
- printer.write("foo")
- self.assertFalse(err.empty())
- err.reset()
- printer.write("foo", "config")
- self.assertFalse(err.empty())
-
- def test_write_verbose(self):
- printer, err, out = self.get_printer(['--verbose'])
- printer.write("foo")
- self.assertTrue(not err.empty() and "foo" in err.get()[0])
- self.assertTrue(out.empty())
-
- def test_print_unexpected_results(self):
- # This routine is the only one that prints stuff that the bots
- # care about.
- #
- # FIXME: there's some weird layering going on here. It seems
- # like we shouldn't be both using an expectations string and
- # having to specify whether or not the result was expected.
- # This whole set of tests should probably be rewritten.
- #
- # FIXME: Plus, the fact that we're having to call into
- # run_webkit_tests is clearly a layering inversion.
- def get_unexpected_results(expected, passing, flaky):
- """Return an unexpected results summary matching the input description.
-
- There are a lot of different combinations of test results that
- can be tested; this routine produces various combinations based
- on the values of the input flags.
-
- Args
- expected: whether the tests ran as expected
- passing: whether the tests should all pass
- flaky: whether the tests should be flaky (if False, they
- produce the same results on both runs; if True, they
- all pass on the second run).
-
- """
- paths, rs, exp = self.get_result_summary(tests, expectations)
- if expected:
- rs.add(self.get_result('passes/text.html', test_expectations.PASS),
- expected)
- rs.add(self.get_result('failures/expected/timeout.html',
- test_expectations.TIMEOUT), expected)
- rs.add(self.get_result('failures/expected/crash.html', test_expectations.CRASH),
- expected)
- elif passing:
- rs.add(self.get_result('passes/text.html'), expected)
- rs.add(self.get_result('failures/expected/timeout.html'), expected)
- rs.add(self.get_result('failures/expected/crash.html'), expected)
- else:
- rs.add(self.get_result('passes/text.html', test_expectations.TIMEOUT),
- expected)
- rs.add(self.get_result('failures/expected/timeout.html',
- test_expectations.CRASH), expected)
- rs.add(self.get_result('failures/expected/crash.html',
- test_expectations.TIMEOUT),
- expected)
- retry = rs
- if flaky:
- paths, retry, exp = self.get_result_summary(tests,
- expectations)
- retry.add(self.get_result('passes/text.html'), True)
- retry.add(self.get_result('failures/expected/timeout.html'), True)
- retry.add(self.get_result('failures/expected/crash.html'), True)
- unexpected_results = run_webkit_tests.summarize_unexpected_results(
- self._port, exp, rs, retry)
- return unexpected_results
-
- tests = ['passes/text.html', 'failures/expected/timeout.html',
- 'failures/expected/crash.html']
- expectations = ''
-
- printer, err, out = self.get_printer(['--print', 'nothing'])
- ur = get_unexpected_results(expected=False, passing=False, flaky=False)
- printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertTrue(out.empty())
-
- printer, err, out = self.get_printer(['--print',
- 'unexpected-results'])
-
- # test everything running as expected
- ur = get_unexpected_results(expected=True, passing=False, flaky=False)
- printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertTrue(out.empty())
-
- # test failures
- err.reset()
- out.reset()
- ur = get_unexpected_results(expected=False, passing=False, flaky=False)
- printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertFalse(out.empty())
-
- # test unexpected flaky results
- err.reset()
- out.reset()
- ur = get_unexpected_results(expected=False, passing=True, flaky=False)
- printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertFalse(out.empty())
-
- # test unexpected passes
- err.reset()
- out.reset()
- ur = get_unexpected_results(expected=False, passing=False, flaky=True)
- printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertFalse(out.empty())
-
- err.reset()
- out.reset()
- printer, err, out = self.get_printer(['--print', 'everything'])
- ur = get_unexpected_results(expected=False, passing=False, flaky=False)
- printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertFalse(out.empty())
-
- expectations = """
-failures/expected/crash.html = CRASH
-failures/expected/timeout.html = TIMEOUT
-"""
- err.reset()
- out.reset()
- ur = get_unexpected_results(expected=False, passing=False, flaky=False)
- printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertFalse(out.empty())
-
- err.reset()
- out.reset()
- ur = get_unexpected_results(expected=False, passing=True, flaky=False)
- printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertFalse(out.empty())
-
- # Test handling of --verbose as well.
- err.reset()
- out.reset()
- printer, err, out = self.get_printer(['--verbose'])
- ur = get_unexpected_results(expected=False, passing=False, flaky=False)
- printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertFalse(out.empty())
-
- def test_print_unexpected_results_buildbot(self):
- # FIXME: Test that print_unexpected_results() produces the printer the
- # buildbot is expecting.
- pass
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py
deleted file mode 100644
index 67873a8..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py
+++ /dev/null
@@ -1,843 +0,0 @@
-#!/usr/bin/env 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.
-
-"""A helper class for reading in and dealing with tests expectations
-for layout tests.
-"""
-
-import logging
-import os
-import re
-import sys
-
-import webkitpy.thirdparty.simplejson as simplejson
-
-_log = logging.getLogger("webkitpy.layout_tests.layout_package."
- "test_expectations")
-
-# Test expectation and modifier constants.
-(PASS, FAIL, TEXT, IMAGE, IMAGE_PLUS_TEXT, TIMEOUT, CRASH, SKIP, WONTFIX,
- SLOW, REBASELINE, MISSING, FLAKY, NOW, NONE) = range(15)
-
-# Test expectation file update action constants
-(NO_CHANGE, REMOVE_TEST, REMOVE_PLATFORM, ADD_PLATFORMS_EXCEPT_THIS) = range(4)
-
-
-def result_was_expected(result, expected_results, test_needs_rebaselining,
- test_is_skipped):
- """Returns whether we got a result we were expecting.
- Args:
- result: actual result of a test execution
- expected_results: set of results listed in test_expectations
- test_needs_rebaselining: whether test was marked as REBASELINE
- test_is_skipped: whether test was marked as SKIP"""
- if result in expected_results:
- return True
- if result in (IMAGE, TEXT, IMAGE_PLUS_TEXT) and FAIL in expected_results:
- return True
- if result == MISSING and test_needs_rebaselining:
- return True
- if result == SKIP and test_is_skipped:
- return True
- return False
-
-
-def remove_pixel_failures(expected_results):
- """Returns a copy of the expected results for a test, except that we
- drop any pixel failures and return the remaining expectations. For example,
- if we're not running pixel tests, then tests expected to fail as IMAGE
- will PASS."""
- expected_results = expected_results.copy()
- if IMAGE in expected_results:
- expected_results.remove(IMAGE)
- expected_results.add(PASS)
- if IMAGE_PLUS_TEXT in expected_results:
- expected_results.remove(IMAGE_PLUS_TEXT)
- expected_results.add(TEXT)
- return expected_results
-
-
-class TestExpectations:
- TEST_LIST = "test_expectations.txt"
-
- def __init__(self, port, tests, expectations, test_platform_name,
- is_debug_mode, is_lint_mode, overrides=None):
- """Loads and parses the test expectations given in the string.
- Args:
- port: handle to object containing platform-specific functionality
- test: list of all of the test files
- expectations: test expectations as a string
- test_platform_name: name of the platform to match expectations
- against. Note that this may be different than
- port.test_platform_name() when is_lint_mode is True.
- is_debug_mode: whether to use the DEBUG or RELEASE modifiers
- in the expectations
- is_lint_mode: If True, just parse the expectations string
- looking for errors.
- overrides: test expectations that are allowed to override any
- entries in |expectations|. This is used by callers
- that need to manage two sets of expectations (e.g., upstream
- and downstream expectations).
- """
- self._expected_failures = TestExpectationsFile(port, expectations,
- tests, test_platform_name, is_debug_mode, is_lint_mode,
- overrides=overrides)
-
- # TODO(ojan): Allow for removing skipped tests when getting the list of
- # tests to run, but not when getting metrics.
- # TODO(ojan): Replace the Get* calls here with the more sane API exposed
- # by TestExpectationsFile below. Maybe merge the two classes entirely?
-
- def get_expectations_json_for_all_platforms(self):
- return (
- self._expected_failures.get_expectations_json_for_all_platforms())
-
- def get_rebaselining_failures(self):
- return (self._expected_failures.get_test_set(REBASELINE, FAIL) |
- self._expected_failures.get_test_set(REBASELINE, IMAGE) |
- self._expected_failures.get_test_set(REBASELINE, TEXT) |
- self._expected_failures.get_test_set(REBASELINE,
- IMAGE_PLUS_TEXT))
-
- def get_options(self, test):
- return self._expected_failures.get_options(test)
-
- def get_expectations(self, test):
- return self._expected_failures.get_expectations(test)
-
- def get_expectations_string(self, test):
- """Returns the expectatons for the given test as an uppercase string.
- If there are no expectations for the test, then "PASS" is returned."""
- expectations = self.get_expectations(test)
- retval = []
-
- for expectation in expectations:
- retval.append(self.expectation_to_string(expectation))
-
- return " ".join(retval)
-
- def expectation_to_string(self, expectation):
- """Return the uppercased string equivalent of a given expectation."""
- for item in TestExpectationsFile.EXPECTATIONS.items():
- if item[1] == expectation:
- return item[0].upper()
- raise ValueError(expectation)
-
- def get_tests_with_result_type(self, result_type):
- return self._expected_failures.get_tests_with_result_type(result_type)
-
- def get_tests_with_timeline(self, timeline):
- return self._expected_failures.get_tests_with_timeline(timeline)
-
- def matches_an_expected_result(self, test, result,
- pixel_tests_are_enabled):
- expected_results = self._expected_failures.get_expectations(test)
- if not pixel_tests_are_enabled:
- expected_results = remove_pixel_failures(expected_results)
- return result_was_expected(result, expected_results,
- self.is_rebaselining(test), self.has_modifier(test, SKIP))
-
- def is_rebaselining(self, test):
- return self._expected_failures.has_modifier(test, REBASELINE)
-
- def has_modifier(self, test, modifier):
- return self._expected_failures.has_modifier(test, modifier)
-
- def remove_platform_from_expectations(self, tests, platform):
- return self._expected_failures.remove_platform_from_expectations(
- tests, platform)
-
-
-def strip_comments(line):
- """Strips comments from a line and return None if the line is empty
- or else the contents of line with leading and trailing spaces removed
- and all other whitespace collapsed"""
-
- commentIndex = line.find('//')
- if commentIndex is -1:
- commentIndex = len(line)
-
- line = re.sub(r'\s+', ' ', line[:commentIndex].strip())
- if line == '':
- return None
- else:
- return line
-
-
-class ModifiersAndExpectations:
- """A holder for modifiers and expectations on a test that serializes to
- JSON."""
-
- def __init__(self, modifiers, expectations):
- self.modifiers = modifiers
- self.expectations = expectations
-
-
-class ExpectationsJsonEncoder(simplejson.JSONEncoder):
- """JSON encoder that can handle ModifiersAndExpectations objects."""
- def default(self, obj):
- # A ModifiersAndExpectations object has two fields, each of which
- # is a dict. Since JSONEncoders handle all the builtin types directly,
- # the only time this routine should be called is on the top level
- # object (i.e., the encoder shouldn't recurse).
- assert isinstance(obj, ModifiersAndExpectations)
- return {"modifiers": obj.modifiers,
- "expectations": obj.expectations}
-
-
-class TestExpectationsFile:
- """Test expectation files consist of lines with specifications of what
- to expect from layout test cases. The test cases can be directories
- in which case the expectations apply to all test cases in that
- directory and any subdirectory. The format of the file is along the
- lines of:
-
- LayoutTests/fast/js/fixme.js = FAIL
- LayoutTests/fast/js/flaky.js = FAIL PASS
- LayoutTests/fast/js/crash.js = CRASH TIMEOUT FAIL PASS
- ...
-
- To add other options:
- SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT PASS
- DEBUG : LayoutTests/fast/js/no-good.js = TIMEOUT PASS
- DEBUG SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT PASS
- LINUX DEBUG SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT PASS
- LINUX WIN : LayoutTests/fast/js/no-good.js = TIMEOUT PASS
-
- SKIP: Doesn't run the test.
- SLOW: The test takes a long time to run, but does not timeout indefinitely.
- WONTFIX: For tests that we never intend to pass on a given platform.
- DEBUG: Expectations apply only to the debug build.
- RELEASE: Expectations apply only to release build.
- LINUX/WIN/WIN-XP/WIN-VISTA/WIN-7/MAC: Expectations apply only to these
- platforms.
-
- Notes:
- -A test cannot be both SLOW and TIMEOUT
- -A test should only be one of IMAGE, TEXT, IMAGE+TEXT, or FAIL. FAIL is
- a migratory state that currently means either IMAGE, TEXT, or
- IMAGE+TEXT. Once we have finished migrating the expectations, we will
- change FAIL to have the meaning of IMAGE+TEXT and remove the IMAGE+TEXT
- identifier.
- -A test can be included twice, but not via the same path.
- -If a test is included twice, then the more precise path wins.
- -CRASH tests cannot be WONTFIX
- """
-
- EXPECTATIONS = {'pass': PASS,
- 'fail': FAIL,
- 'text': TEXT,
- 'image': IMAGE,
- 'image+text': IMAGE_PLUS_TEXT,
- 'timeout': TIMEOUT,
- 'crash': CRASH,
- 'missing': MISSING}
-
- EXPECTATION_DESCRIPTIONS = {SKIP: ('skipped', 'skipped'),
- PASS: ('pass', 'passes'),
- FAIL: ('failure', 'failures'),
- TEXT: ('text diff mismatch',
- 'text diff mismatch'),
- IMAGE: ('image mismatch', 'image mismatch'),
- IMAGE_PLUS_TEXT: ('image and text mismatch',
- 'image and text mismatch'),
- CRASH: ('DumpRenderTree crash',
- 'DumpRenderTree crashes'),
- TIMEOUT: ('test timed out', 'tests timed out'),
- MISSING: ('no expected result found',
- 'no expected results found')}
-
- EXPECTATION_ORDER = (PASS, CRASH, TIMEOUT, MISSING, IMAGE_PLUS_TEXT,
- TEXT, IMAGE, FAIL, SKIP)
-
- BUILD_TYPES = ('debug', 'release')
-
- MODIFIERS = {'skip': SKIP,
- 'wontfix': WONTFIX,
- 'slow': SLOW,
- 'rebaseline': REBASELINE,
- 'none': NONE}
-
- TIMELINES = {'wontfix': WONTFIX,
- 'now': NOW}
-
- RESULT_TYPES = {'skip': SKIP,
- 'pass': PASS,
- 'fail': FAIL,
- 'flaky': FLAKY}
-
- def __init__(self, port, expectations, full_test_list, test_platform_name,
- is_debug_mode, is_lint_mode, suppress_errors=False, overrides=None):
- """
- expectations: Contents of the expectations file
- full_test_list: The list of all tests to be run pending processing of
- the expections for those tests.
- test_platform_name: name of the platform to match expectations
- against. Note that this may be different than
- port.test_platform_name() when is_lint_mode is True.
- is_debug_mode: Whether we testing a test_shell built debug mode.
- is_lint_mode: Whether this is just linting test_expecatations.txt.
- suppress_errors: Whether to suppress lint errors.
- overrides: test expectations that are allowed to override any
- entries in |expectations|. This is used by callers
- that need to manage two sets of expectations (e.g., upstream
- and downstream expectations).
- """
-
- self._port = port
- self._expectations = expectations
- self._full_test_list = full_test_list
- self._test_platform_name = test_platform_name
- self._is_debug_mode = is_debug_mode
- self._is_lint_mode = is_lint_mode
- self._overrides = overrides
- self._suppress_errors = suppress_errors
- self._errors = []
- self._non_fatal_errors = []
-
- # Maps relative test paths as listed in the expectations file to a
- # list of maps containing modifiers and expectations for each time
- # the test is listed in the expectations file.
- self._all_expectations = {}
-
- # Maps a test to its list of expectations.
- self._test_to_expectations = {}
-
- # Maps a test to its list of options (string values)
- self._test_to_options = {}
-
- # Maps a test to its list of modifiers: the constants associated with
- # the options minus any bug or platform strings
- self._test_to_modifiers = {}
-
- # Maps a test to the base path that it was listed with in the list.
- self._test_list_paths = {}
-
- self._modifier_to_tests = self._dict_of_sets(self.MODIFIERS)
- self._expectation_to_tests = self._dict_of_sets(self.EXPECTATIONS)
- self._timeline_to_tests = self._dict_of_sets(self.TIMELINES)
- self._result_type_to_tests = self._dict_of_sets(self.RESULT_TYPES)
-
- self._read(self._get_iterable_expectations(self._expectations),
- overrides_allowed=False)
-
- # List of tests that are in the overrides file (used for checking for
- # duplicates inside the overrides file itself). Note that just because
- # a test is in this set doesn't mean it's necessarily overridding a
- # expectation in the regular expectations; the test might not be
- # mentioned in the regular expectations file at all.
- self._overridding_tests = set()
-
- if overrides:
- self._read(self._get_iterable_expectations(self._overrides),
- overrides_allowed=True)
-
- self._handle_any_read_errors()
- self._process_tests_without_expectations()
-
- def _handle_any_read_errors(self):
- if not self._suppress_errors and (
- len(self._errors) or len(self._non_fatal_errors)):
- if self._is_debug_mode:
- build_type = 'DEBUG'
- else:
- build_type = 'RELEASE'
- _log.error('')
- _log.error("FAILURES FOR PLATFORM: %s, BUILD_TYPE: %s" %
- (self._test_platform_name.upper(), build_type))
-
- for error in self._non_fatal_errors:
- _log.error(error)
- _log.error('')
-
- if len(self._errors):
- raise SyntaxError('\n'.join(map(str, self._errors)))
-
- def _process_tests_without_expectations(self):
- expectations = set([PASS])
- options = []
- modifiers = []
- if self._full_test_list:
- for test in self._full_test_list:
- if not test in self._test_list_paths:
- self._add_test(test, modifiers, expectations, options,
- overrides_allowed=False)
-
- def _dict_of_sets(self, strings_to_constants):
- """Takes a dict of strings->constants and returns a dict mapping
- each constant to an empty set."""
- d = {}
- for c in strings_to_constants.values():
- d[c] = set()
- return d
-
- def _get_iterable_expectations(self, expectations_str):
- """Returns an object that can be iterated over. Allows for not caring
- about whether we're iterating over a file or a new-line separated
- string."""
- iterable = [x + "\n" for x in expectations_str.split("\n")]
- # Strip final entry if it's empty to avoid added in an extra
- # newline.
- if iterable[-1] == "\n":
- return iterable[:-1]
- return iterable
-
- def get_test_set(self, modifier, expectation=None, include_skips=True):
- if expectation is None:
- tests = self._modifier_to_tests[modifier]
- else:
- tests = (self._expectation_to_tests[expectation] &
- self._modifier_to_tests[modifier])
-
- if not include_skips:
- tests = tests - self.get_test_set(SKIP, expectation)
-
- return tests
-
- def get_tests_with_result_type(self, result_type):
- return self._result_type_to_tests[result_type]
-
- def get_tests_with_timeline(self, timeline):
- return self._timeline_to_tests[timeline]
-
- def get_options(self, test):
- """This returns the entire set of options for the given test
- (the modifiers plus the BUGXXXX identifier). This is used by the
- LTTF dashboard."""
- return self._test_to_options[test]
-
- def has_modifier(self, test, modifier):
- return test in self._modifier_to_tests[modifier]
-
- def get_expectations(self, test):
- return self._test_to_expectations[test]
-
- def get_expectations_json_for_all_platforms(self):
- # Specify separators in order to get compact encoding.
- return ExpectationsJsonEncoder(separators=(',', ':')).encode(
- self._all_expectations)
-
- def get_non_fatal_errors(self):
- return self._non_fatal_errors
-
- def remove_platform_from_expectations(self, tests, platform):
- """Returns a copy of the expectations with the tests matching the
- platform removed.
-
- If a test is in the test list and has an option that matches the given
- platform, remove the matching platform and save the updated test back
- to the file. If no other platforms remaining after removal, delete the
- test from the file.
-
- Args:
- tests: list of tests that need to update..
- platform: which platform option to remove.
-
- Returns:
- the updated string.
- """
-
- assert(platform)
- f_orig = self._get_iterable_expectations(self._expectations)
- f_new = []
-
- tests_removed = 0
- tests_updated = 0
- lineno = 0
- for line in f_orig:
- lineno += 1
- action = self._get_platform_update_action(line, lineno, tests,
- platform)
- assert(action in (NO_CHANGE, REMOVE_TEST, REMOVE_PLATFORM,
- ADD_PLATFORMS_EXCEPT_THIS))
- if action == NO_CHANGE:
- # Save the original line back to the file
- _log.debug('No change to test: %s', line)
- f_new.append(line)
- elif action == REMOVE_TEST:
- tests_removed += 1
- _log.info('Test removed: %s', line)
- elif action == REMOVE_PLATFORM:
- parts = line.split(':')
- new_options = parts[0].replace(platform.upper() + ' ', '', 1)
- new_line = ('%s:%s' % (new_options, parts[1]))
- f_new.append(new_line)
- tests_updated += 1
- _log.info('Test updated: ')
- _log.info(' old: %s', line)
- _log.info(' new: %s', new_line)
- elif action == ADD_PLATFORMS_EXCEPT_THIS:
- parts = line.split(':')
- new_options = parts[0]
- for p in self._port.test_platform_names():
- p = p.upper()
- # This is a temp solution for rebaselining tool.
- # Do not add tags WIN-7 and WIN-VISTA to test expectations
- # if the original line does not specify the platform
- # option.
- # TODO(victorw): Remove WIN-VISTA and WIN-7 once we have
- # reliable Win 7 and Win Vista buildbots setup.
- if not p in (platform.upper(), 'WIN-VISTA', 'WIN-7'):
- new_options += p + ' '
- new_line = ('%s:%s' % (new_options, parts[1]))
- f_new.append(new_line)
- tests_updated += 1
- _log.info('Test updated: ')
- _log.info(' old: %s', line)
- _log.info(' new: %s', new_line)
-
- _log.info('Total tests removed: %d', tests_removed)
- _log.info('Total tests updated: %d', tests_updated)
-
- return "".join(f_new)
-
- def parse_expectations_line(self, line, lineno):
- """Parses a line from test_expectations.txt and returns a tuple
- with the test path, options as a list, expectations as a list."""
- line = strip_comments(line)
- if not line:
- return (None, None, None)
-
- options = []
- if line.find(":") is -1:
- test_and_expectation = line.split("=")
- else:
- parts = line.split(":")
- options = self._get_options_list(parts[0])
- test_and_expectation = parts[1].split('=')
-
- test = test_and_expectation[0].strip()
- if (len(test_and_expectation) is not 2):
- self._add_error(lineno, "Missing expectations.",
- test_and_expectation)
- expectations = None
- else:
- expectations = self._get_options_list(test_and_expectation[1])
-
- return (test, options, expectations)
-
- def _get_platform_update_action(self, line, lineno, tests, platform):
- """Check the platform option and return the action needs to be taken.
-
- Args:
- line: current line in test expectations file.
- lineno: current line number of line
- tests: list of tests that need to update..
- platform: which platform option to remove.
-
- Returns:
- NO_CHANGE: no change to the line (comments, test not in the list etc)
- REMOVE_TEST: remove the test from file.
- REMOVE_PLATFORM: remove this platform option from the test.
- ADD_PLATFORMS_EXCEPT_THIS: add all the platforms except this one.
- """
- test, options, expectations = self.parse_expectations_line(line,
- lineno)
- if not test or test not in tests:
- return NO_CHANGE
-
- has_any_platform = False
- for option in options:
- if option in self._port.test_platform_names():
- has_any_platform = True
- if not option == platform:
- return REMOVE_PLATFORM
-
- # If there is no platform specified, then it means apply to all
- # platforms. Return the action to add all the platforms except this
- # one.
- if not has_any_platform:
- return ADD_PLATFORMS_EXCEPT_THIS
-
- return REMOVE_TEST
-
- def _has_valid_modifiers_for_current_platform(self, options, lineno,
- test_and_expectations, modifiers):
- """Returns true if the current platform is in the options list or if
- no platforms are listed and if there are no fatal errors in the
- options list.
-
- Args:
- options: List of lowercase options.
- lineno: The line in the file where the test is listed.
- test_and_expectations: The path and expectations for the test.
- modifiers: The set to populate with modifiers.
- """
- has_any_platform = False
- has_bug_id = False
- for option in options:
- if option in self.MODIFIERS:
- modifiers.add(option)
- elif option in self._port.test_platform_names():
- has_any_platform = True
- elif option.startswith('bug'):
- has_bug_id = True
- elif option not in self.BUILD_TYPES:
- self._add_error(lineno, 'Invalid modifier for test: %s' %
- option, test_and_expectations)
-
- if has_any_platform and not self._match_platform(options):
- return False
-
- if not has_bug_id and 'wontfix' not in options:
- # TODO(ojan): Turn this into an AddError call once all the
- # tests have BUG identifiers.
- self._log_non_fatal_error(lineno, 'Test lacks BUG modifier.',
- test_and_expectations)
-
- if 'release' in options or 'debug' in options:
- if self._is_debug_mode and 'debug' not in options:
- return False
- if not self._is_debug_mode and 'release' not in options:
- return False
-
- if self._is_lint_mode and 'rebaseline' in options:
- self._add_error(lineno,
- 'REBASELINE should only be used for running rebaseline.py. '
- 'Cannot be checked in.', test_and_expectations)
-
- return True
-
- def _match_platform(self, options):
- """Match the list of options against our specified platform. If any
- of the options prefix-match self._platform, return True. This handles
- the case where a test is marked WIN and the platform is WIN-VISTA.
-
- Args:
- options: list of options
- """
- for opt in options:
- if self._test_platform_name.startswith(opt):
- return True
- return False
-
- def _add_to_all_expectations(self, test, options, expectations):
- # Make all paths unix-style so the dashboard doesn't need to.
- test = test.replace('\\', '/')
- if not test in self._all_expectations:
- self._all_expectations[test] = []
- self._all_expectations[test].append(
- ModifiersAndExpectations(options, expectations))
-
- def _read(self, expectations, overrides_allowed):
- """For each test in an expectations iterable, generate the
- expectations for it."""
- lineno = 0
- for line in expectations:
- lineno += 1
-
- test_list_path, options, expectations = \
- self.parse_expectations_line(line, lineno)
- if not expectations:
- continue
-
- self._add_to_all_expectations(test_list_path,
- " ".join(options).upper(),
- " ".join(expectations).upper())
-
- modifiers = set()
- if options and not self._has_valid_modifiers_for_current_platform(
- options, lineno, test_list_path, modifiers):
- continue
-
- expectations = self._parse_expectations(expectations, lineno,
- test_list_path)
-
- if 'slow' in options and TIMEOUT in expectations:
- self._add_error(lineno,
- 'A test can not be both slow and timeout. If it times out '
- 'indefinitely, then it should be just timeout.',
- test_list_path)
-
- full_path = os.path.join(self._port.layout_tests_dir(),
- test_list_path)
- full_path = os.path.normpath(full_path)
- # WebKit's way of skipping tests is to add a -disabled suffix.
- # So we should consider the path existing if the path or the
- # -disabled version exists.
- if (not self._port.path_exists(full_path)
- and not self._port.path_exists(full_path + '-disabled')):
- # Log a non fatal error here since you hit this case any
- # time you update test_expectations.txt without syncing
- # the LayoutTests directory
- self._log_non_fatal_error(lineno, 'Path does not exist.',
- test_list_path)
- continue
-
- if not self._full_test_list:
- tests = [test_list_path]
- else:
- tests = self._expand_tests(test_list_path)
-
- self._add_tests(tests, expectations, test_list_path, lineno,
- modifiers, options, overrides_allowed)
-
- def _get_options_list(self, listString):
- return [part.strip().lower() for part in listString.strip().split(' ')]
-
- def _parse_expectations(self, expectations, lineno, test_list_path):
- result = set()
- for part in expectations:
- if not part in self.EXPECTATIONS:
- self._add_error(lineno, 'Unsupported expectation: %s' % part,
- test_list_path)
- continue
- expectation = self.EXPECTATIONS[part]
- result.add(expectation)
- return result
-
- def _expand_tests(self, test_list_path):
- """Convert the test specification to an absolute, normalized
- path and make sure directories end with the OS path separator."""
- path = os.path.join(self._port.layout_tests_dir(), test_list_path)
- path = os.path.normpath(path)
- if self._port.path_isdir(path):
- path = os.path.join(path, '')
-
- result = []
- for test in self._full_test_list:
- if test.startswith(path):
- result.append(test)
- return result
-
- def _add_tests(self, tests, expectations, test_list_path, lineno,
- modifiers, options, overrides_allowed):
- for test in tests:
- if self._already_seen_test(test, test_list_path, lineno,
- overrides_allowed):
- continue
-
- self._clear_expectations_for_test(test, test_list_path)
- self._add_test(test, modifiers, expectations, options,
- overrides_allowed)
-
- def _add_test(self, test, modifiers, expectations, options,
- overrides_allowed):
- """Sets the expected state for a given test.
-
- This routine assumes the test has not been added before. If it has,
- use _ClearExpectationsForTest() to reset the state prior to
- calling this.
-
- Args:
- test: test to add
- modifiers: sequence of modifier keywords ('wontfix', 'slow', etc.)
- expectations: sequence of expectations (PASS, IMAGE, etc.)
- options: sequence of keywords and bug identifiers.
- overrides_allowed: whether we're parsing the regular expectations
- or the overridding expectations"""
- self._test_to_expectations[test] = expectations
- for expectation in expectations:
- self._expectation_to_tests[expectation].add(test)
-
- self._test_to_options[test] = options
- self._test_to_modifiers[test] = set()
- for modifier in modifiers:
- mod_value = self.MODIFIERS[modifier]
- self._modifier_to_tests[mod_value].add(test)
- self._test_to_modifiers[test].add(mod_value)
-
- if 'wontfix' in modifiers:
- self._timeline_to_tests[WONTFIX].add(test)
- else:
- self._timeline_to_tests[NOW].add(test)
-
- if 'skip' in modifiers:
- self._result_type_to_tests[SKIP].add(test)
- elif expectations == set([PASS]):
- self._result_type_to_tests[PASS].add(test)
- elif len(expectations) > 1:
- self._result_type_to_tests[FLAKY].add(test)
- else:
- self._result_type_to_tests[FAIL].add(test)
-
- if overrides_allowed:
- self._overridding_tests.add(test)
-
- def _clear_expectations_for_test(self, test, test_list_path):
- """Remove prexisting expectations for this test.
- This happens if we are seeing a more precise path
- than a previous listing.
- """
- if test in self._test_list_paths:
- self._test_to_expectations.pop(test, '')
- self._remove_from_sets(test, self._expectation_to_tests)
- self._remove_from_sets(test, self._modifier_to_tests)
- self._remove_from_sets(test, self._timeline_to_tests)
- self._remove_from_sets(test, self._result_type_to_tests)
-
- self._test_list_paths[test] = os.path.normpath(test_list_path)
-
- def _remove_from_sets(self, test, dict):
- """Removes the given test from the sets in the dictionary.
-
- Args:
- test: test to look for
- dict: dict of sets of files"""
- for set_of_tests in dict.itervalues():
- if test in set_of_tests:
- set_of_tests.remove(test)
-
- def _already_seen_test(self, test, test_list_path, lineno,
- allow_overrides):
- """Returns true if we've already seen a more precise path for this test
- than the test_list_path.
- """
- if not test in self._test_list_paths:
- return False
-
- prev_base_path = self._test_list_paths[test]
- if (prev_base_path == os.path.normpath(test_list_path)):
- if (not allow_overrides or test in self._overridding_tests):
- if allow_overrides:
- expectation_source = "override"
- else:
- expectation_source = "expectation"
- self._add_error(lineno, 'Duplicate %s.' % expectation_source,
- test)
- return True
- else:
- # We have seen this path, but that's okay because its
- # in the overrides and the earlier path was in the
- # expectations.
- return False
-
- # Check if we've already seen a more precise path.
- return prev_base_path.startswith(os.path.normpath(test_list_path))
-
- def _add_error(self, lineno, msg, path):
- """Reports an error that will prevent running the tests. Does not
- immediately raise an exception because we'd like to aggregate all the
- errors so they can all be printed out."""
- self._errors.append('\nLine:%s %s %s' % (lineno, msg, path))
-
- def _log_non_fatal_error(self, lineno, msg, path):
- """Reports an error that will not prevent running the tests. These are
- still errors, but not bad enough to warrant breaking test running."""
- self._non_fatal_errors.append('Line:%s %s %s' % (lineno, msg, path))
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations_unittest.py
deleted file mode 100644
index 55eaf99..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_expectations_unittest.py
+++ /dev/null
@@ -1,313 +0,0 @@
-#!/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 test_expectations.py."""
-
-import os
-import sys
-import unittest
-
-from webkitpy.layout_tests import port
-from webkitpy.layout_tests.layout_package.test_expectations import *
-
-class FunctionsTest(unittest.TestCase):
- def test_result_was_expected(self):
- # test basics
- self.assertEquals(result_was_expected(PASS, set([PASS]),
- False, False), True)
- self.assertEquals(result_was_expected(TEXT, set([PASS]),
- False, False), False)
-
- # test handling of FAIL expectations
- self.assertEquals(result_was_expected(IMAGE_PLUS_TEXT, set([FAIL]),
- False, False), True)
- self.assertEquals(result_was_expected(IMAGE, set([FAIL]),
- False, False), True)
- self.assertEquals(result_was_expected(TEXT, set([FAIL]),
- False, False), True)
- self.assertEquals(result_was_expected(CRASH, set([FAIL]),
- False, False), False)
-
- # test handling of SKIPped tests and results
- self.assertEquals(result_was_expected(SKIP, set([CRASH]),
- False, True), True)
- self.assertEquals(result_was_expected(SKIP, set([CRASH]),
- False, False), False)
-
- # test handling of MISSING results and the REBASELINE modifier
- self.assertEquals(result_was_expected(MISSING, set([PASS]),
- True, False), True)
- self.assertEquals(result_was_expected(MISSING, set([PASS]),
- False, False), False)
-
- def test_remove_pixel_failures(self):
- self.assertEquals(remove_pixel_failures(set([TEXT])),
- set([TEXT]))
- self.assertEquals(remove_pixel_failures(set([PASS])),
- set([PASS]))
- self.assertEquals(remove_pixel_failures(set([IMAGE])),
- set([PASS]))
- self.assertEquals(remove_pixel_failures(set([IMAGE_PLUS_TEXT])),
- set([TEXT]))
- self.assertEquals(remove_pixel_failures(set([PASS, IMAGE, CRASH])),
- set([PASS, CRASH]))
-
-
-class Base(unittest.TestCase):
- def __init__(self, testFunc, setUp=None, tearDown=None, description=None):
- self._port = port.get('test', None)
- self._exp = None
- unittest.TestCase.__init__(self, testFunc)
-
- def get_test(self, test_name):
- return os.path.join(self._port.layout_tests_dir(), test_name)
-
- def get_basic_tests(self):
- return [self.get_test('failures/expected/text.html'),
- self.get_test('failures/expected/image_checksum.html'),
- self.get_test('failures/expected/crash.html'),
- self.get_test('failures/expected/missing_text.html'),
- self.get_test('failures/expected/image.html'),
- self.get_test('passes/text.html')]
-
- def get_basic_expectations(self):
- return """
-BUG_TEST : failures/expected/text.html = TEXT
-BUG_TEST WONTFIX SKIP : failures/expected/crash.html = CRASH
-BUG_TEST REBASELINE : failures/expected/missing_image.html = MISSING
-BUG_TEST WONTFIX : failures/expected/image_checksum.html = IMAGE
-BUG_TEST WONTFIX WIN : failures/expected/image.html = IMAGE
-"""
-
- def parse_exp(self, expectations, overrides=None, is_lint_mode=False,
- is_debug_mode=False):
- self._exp = TestExpectations(self._port,
- tests=self.get_basic_tests(),
- expectations=expectations,
- test_platform_name=self._port.test_platform_name(),
- is_debug_mode=is_debug_mode,
- is_lint_mode=is_lint_mode,
- overrides=overrides)
-
- def assert_exp(self, test, result):
- self.assertEquals(self._exp.get_expectations(self.get_test(test)),
- set([result]))
-
-
-class TestExpectationsTest(Base):
- def test_basic(self):
- self.parse_exp(self.get_basic_expectations())
- self.assert_exp('failures/expected/text.html', TEXT)
- self.assert_exp('failures/expected/image_checksum.html', IMAGE)
- self.assert_exp('passes/text.html', PASS)
- self.assert_exp('failures/expected/image.html', PASS)
-
- def test_multiple_results(self):
- self.parse_exp('BUGX : failures/expected/text.html = TEXT CRASH')
- self.assertEqual(self._exp.get_expectations(
- self.get_test('failures/expected/text.html')),
- set([TEXT, CRASH]))
-
- def test_precedence(self):
- # This tests handling precedence of specific lines over directories
- # and tests expectations covering entire directories.
- exp_str = """
-BUGX : failures/expected/text.html = TEXT
-BUGX WONTFIX : failures/expected = IMAGE
-"""
- self.parse_exp(exp_str)
- self.assert_exp('failures/expected/text.html', TEXT)
- self.assert_exp('failures/expected/crash.html', IMAGE)
-
- def test_release_mode(self):
- self.parse_exp('BUGX DEBUG : failures/expected/text.html = TEXT',
- is_debug_mode=True)
- self.assert_exp('failures/expected/text.html', TEXT)
- self.parse_exp('BUGX RELEASE : failures/expected/text.html = TEXT',
- is_debug_mode=True)
- self.assert_exp('failures/expected/text.html', PASS)
- self.parse_exp('BUGX DEBUG : failures/expected/text.html = TEXT',
- is_debug_mode=False)
- self.assert_exp('failures/expected/text.html', PASS)
- self.parse_exp('BUGX RELEASE : failures/expected/text.html = TEXT',
- is_debug_mode=False)
- self.assert_exp('failures/expected/text.html', TEXT)
-
- def test_get_options(self):
- self.parse_exp(self.get_basic_expectations())
- self.assertEqual(self._exp.get_options(
- self.get_test('passes/text.html')), [])
-
- def test_expectations_json_for_all_platforms(self):
- self.parse_exp(self.get_basic_expectations())
- json_str = self._exp.get_expectations_json_for_all_platforms()
- # FIXME: test actual content?
- self.assertTrue(json_str)
-
- def test_get_expectations_string(self):
- self.parse_exp(self.get_basic_expectations())
- self.assertEquals(self._exp.get_expectations_string(
- self.get_test('failures/expected/text.html')),
- 'TEXT')
-
- def test_expectation_to_string(self):
- # Normal cases are handled by other tests.
- self.parse_exp(self.get_basic_expectations())
- self.assertRaises(ValueError, self._exp.expectation_to_string,
- -1)
-
- def test_get_test_set(self):
- # Handle some corner cases for this routine not covered by other tests.
- self.parse_exp(self.get_basic_expectations())
- s = self._exp._expected_failures.get_test_set(WONTFIX)
- self.assertEqual(s,
- set([self.get_test('failures/expected/crash.html'),
- self.get_test('failures/expected/image_checksum.html')]))
- s = self._exp._expected_failures.get_test_set(WONTFIX, CRASH)
- self.assertEqual(s,
- set([self.get_test('failures/expected/crash.html')]))
- s = self._exp._expected_failures.get_test_set(WONTFIX, CRASH,
- include_skips=False)
- self.assertEqual(s, set([]))
-
- def test_syntax_missing_expectation(self):
- # This is missing the expectation.
- self.assertRaises(SyntaxError, self.parse_exp,
- 'BUG_TEST: failures/expected/text.html',
- is_debug_mode=True)
-
- def test_syntax_invalid_option(self):
- self.assertRaises(SyntaxError, self.parse_exp,
- 'BUG_TEST FOO: failures/expected/text.html = PASS')
-
- def test_syntax_invalid_expectation(self):
- # This is missing the expectation.
- self.assertRaises(SyntaxError, self.parse_exp,
- 'BUG_TEST: failures/expected/text.html = FOO')
-
- def test_syntax_missing_bugid(self):
- # This should log a non-fatal error.
- self.parse_exp('SLOW : failures/expected/text.html = TEXT')
- self.assertEqual(
- len(self._exp._expected_failures.get_non_fatal_errors()), 1)
-
- def test_semantic_slow_and_timeout(self):
- # A test cannot be SLOW and expected to TIMEOUT.
- self.assertRaises(SyntaxError, self.parse_exp,
- 'BUG_TEST SLOW : failures/expected/timeout.html = TIMEOUT')
-
- def test_semantic_rebaseline(self):
- # Can't lint a file w/ 'REBASELINE' in it.
- self.assertRaises(SyntaxError, self.parse_exp,
- 'BUG_TEST REBASELINE : failures/expected/text.html = TEXT',
- is_lint_mode=True)
-
- def test_semantic_duplicates(self):
- self.assertRaises(SyntaxError, self.parse_exp, """
-BUG_TEST : failures/expected/text.html = TEXT
-BUG_TEST : failures/expected/text.html = IMAGE""")
-
- self.assertRaises(SyntaxError, self.parse_exp,
- self.get_basic_expectations(), """
-BUG_TEST : failures/expected/text.html = TEXT
-BUG_TEST : failures/expected/text.html = IMAGE""")
-
- def test_semantic_missing_file(self):
- # This should log a non-fatal error.
- self.parse_exp('BUG_TEST : missing_file.html = TEXT')
- self.assertEqual(
- len(self._exp._expected_failures.get_non_fatal_errors()), 1)
-
-
- def test_overrides(self):
- self.parse_exp(self.get_basic_expectations(), """
-BUG_OVERRIDE : failures/expected/text.html = IMAGE""")
- self.assert_exp('failures/expected/text.html', IMAGE)
-
- def test_matches_an_expected_result(self):
-
- def match(test, result, pixel_tests_enabled):
- return self._exp.matches_an_expected_result(
- self.get_test(test), result, pixel_tests_enabled)
-
- self.parse_exp(self.get_basic_expectations())
- self.assertTrue(match('failures/expected/text.html', TEXT, True))
- self.assertTrue(match('failures/expected/text.html', TEXT, False))
- self.assertFalse(match('failures/expected/text.html', CRASH, True))
- self.assertFalse(match('failures/expected/text.html', CRASH, False))
- self.assertTrue(match('failures/expected/image_checksum.html', IMAGE,
- True))
- self.assertTrue(match('failures/expected/image_checksum.html', PASS,
- False))
- self.assertTrue(match('failures/expected/crash.html', SKIP, False))
- self.assertTrue(match('passes/text.html', PASS, False))
-
-
-class RebaseliningTest(Base):
- """Test rebaselining-specific functionality."""
- def assertRemove(self, platform, input_expectations, expected_expectations):
- self.parse_exp(input_expectations)
- test = self.get_test('failures/expected/text.html')
- actual_expectations = self._exp.remove_platform_from_expectations(
- test, platform)
- self.assertEqual(expected_expectations, actual_expectations)
-
- def test_no_get_rebaselining_failures(self):
- self.parse_exp(self.get_basic_expectations())
- self.assertEqual(len(self._exp.get_rebaselining_failures()), 0)
-
- def test_get_rebaselining_failures_expand(self):
- self.parse_exp("""
-BUG_TEST REBASELINE : failures/expected/text.html = TEXT
-""")
- self.assertEqual(len(self._exp.get_rebaselining_failures()), 1)
-
- def test_remove_expand(self):
- self.assertRemove('mac',
- 'BUGX REBASELINE : failures/expected/text.html = TEXT\n',
- 'BUGX REBASELINE WIN : failures/expected/text.html = TEXT\n')
-
- def test_remove_mac_win(self):
- self.assertRemove('mac',
- 'BUGX REBASELINE MAC WIN : failures/expected/text.html = TEXT\n',
- 'BUGX REBASELINE WIN : failures/expected/text.html = TEXT\n')
-
- def test_remove_mac_mac(self):
- self.assertRemove('mac',
- 'BUGX REBASELINE MAC : failures/expected/text.html = TEXT\n',
- '')
-
- def test_remove_nothing(self):
- self.assertRemove('mac',
- '\n\n',
- '\n\n')
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py
deleted file mode 100644
index 6d55761..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py
+++ /dev/null
@@ -1,282 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Classes for failures that occur during tests."""
-
-import os
-import test_expectations
-
-import cPickle
-
-
-def determine_result_type(failure_list):
- """Takes a set of test_failures and returns which result type best fits
- the list of failures. "Best fits" means we use the worst type of failure.
-
- Returns:
- one of the test_expectations result types - PASS, TEXT, CRASH, etc."""
-
- if not failure_list or len(failure_list) == 0:
- return test_expectations.PASS
-
- failure_types = [type(f) for f in failure_list]
- if FailureCrash in failure_types:
- return test_expectations.CRASH
- elif FailureTimeout in failure_types:
- return test_expectations.TIMEOUT
- elif (FailureMissingResult in failure_types or
- FailureMissingImage in failure_types or
- FailureMissingImageHash in failure_types):
- return test_expectations.MISSING
- else:
- is_text_failure = FailureTextMismatch in failure_types
- is_image_failure = (FailureImageHashIncorrect in failure_types or
- FailureImageHashMismatch in failure_types)
- if is_text_failure and is_image_failure:
- return test_expectations.IMAGE_PLUS_TEXT
- elif is_text_failure:
- return test_expectations.TEXT
- elif is_image_failure:
- return test_expectations.IMAGE
- else:
- raise ValueError("unclassifiable set of failures: "
- + str(failure_types))
-
-
-class TestFailure(object):
- """Abstract base class that defines the failure interface."""
-
- @staticmethod
- def loads(s):
- """Creates a TestFailure object from the specified string."""
- return cPickle.loads(s)
-
- @staticmethod
- def message():
- """Returns a string describing the failure in more detail."""
- raise NotImplementedError
-
- def __eq__(self, other):
- return self.__class__.__name__ == other.__class__.__name__
-
- def __ne__(self, other):
- return self.__class__.__name__ != other.__class__.__name__
-
- def dumps(self):
- """Returns the string/JSON representation of a TestFailure."""
- return cPickle.dumps(self)
-
- def result_html_output(self, filename):
- """Returns an HTML string to be included on the results.html page."""
- raise NotImplementedError
-
- def should_kill_dump_render_tree(self):
- """Returns True if we should kill DumpRenderTree before the next
- test."""
- return False
-
- def relative_output_filename(self, filename, modifier):
- """Returns a relative filename inside the output dir that contains
- modifier.
-
- For example, if filename is fast\dom\foo.html and modifier is
- "-expected.txt", the return value is fast\dom\foo-expected.txt
-
- Args:
- filename: relative filename to test file
- modifier: a string to replace the extension of filename with
-
- Return:
- The relative windows path to the output filename
- """
- return os.path.splitext(filename)[0] + modifier
-
-
-class FailureWithType(TestFailure):
- """Base class that produces standard HTML output based on the test type.
-
- Subclasses may commonly choose to override the ResultHtmlOutput, but still
- use the standard OutputLinks.
- """
-
- def __init__(self):
- TestFailure.__init__(self)
-
- # Filename suffixes used by ResultHtmlOutput.
- OUT_FILENAMES = ()
-
- def output_links(self, filename, out_names):
- """Returns a string holding all applicable output file links.
-
- Args:
- filename: the test filename, used to construct the result file names
- out_names: list of filename suffixes for the files. If three or more
- suffixes are in the list, they should be [actual, expected, diff,
- wdiff]. Two suffixes should be [actual, expected], and a
- single item is the [actual] filename suffix.
- If out_names is empty, returns the empty string.
- """
- # FIXME: Seems like a bad idea to separate the display name data
- # from the path data by hard-coding the display name here
- # and passing in the path information via out_names.
- #
- # FIXME: Also, we don't know for sure that these files exist,
- # and we shouldn't be creating links to files that don't exist
- # (for example, if we don't actually have wdiff output).
- links = ['']
- uris = [self.relative_output_filename(filename, fn) for
- fn in out_names]
- if len(uris) > 1:
- links.append("<a href='%s'>expected</a>" % uris[1])
- if len(uris) > 0:
- links.append("<a href='%s'>actual</a>" % uris[0])
- if len(uris) > 2:
- links.append("<a href='%s'>diff</a>" % uris[2])
- if len(uris) > 3:
- links.append("<a href='%s'>wdiff</a>" % uris[3])
- if len(uris) > 4:
- links.append("<a href='%s'>pretty diff</a>" % uris[4])
- return ' '.join(links)
-
- def result_html_output(self, filename):
- return self.message() + self.output_links(filename, self.OUT_FILENAMES)
-
-
-class FailureTimeout(TestFailure):
- """Test timed out. We also want to restart DumpRenderTree if this
- happens."""
-
- @staticmethod
- def message():
- return "Test timed out"
-
- def result_html_output(self, filename):
- return "<strong>%s</strong>" % self.message()
-
- def should_kill_dump_render_tree(self):
- return True
-
-
-class FailureCrash(TestFailure):
- """Test shell crashed."""
-
- @staticmethod
- def message():
- return "Test shell crashed"
-
- def result_html_output(self, filename):
- # FIXME: create a link to the minidump file
- stack = self.relative_output_filename(filename, "-stack.txt")
- return "<strong>%s</strong> <a href=%s>stack</a>" % (self.message(),
- stack)
-
- def should_kill_dump_render_tree(self):
- return True
-
-
-class FailureMissingResult(FailureWithType):
- """Expected result was missing."""
- OUT_FILENAMES = ("-actual.txt",)
-
- @staticmethod
- def message():
- return "No expected results found"
-
- def result_html_output(self, filename):
- return ("<strong>%s</strong>" % self.message() +
- self.output_links(filename, self.OUT_FILENAMES))
-
-
-class FailureTextMismatch(FailureWithType):
- """Text diff output failed."""
- # Filename suffixes used by ResultHtmlOutput.
- # FIXME: Why don't we use the constants from TestTypeBase here?
- OUT_FILENAMES = ("-actual.txt", "-expected.txt", "-diff.txt",
- "-wdiff.html", "-pretty-diff.html")
-
- @staticmethod
- def message():
- return "Text diff mismatch"
-
-
-class FailureMissingImageHash(FailureWithType):
- """Actual result hash was missing."""
- # Chrome doesn't know to display a .checksum file as text, so don't bother
- # putting in a link to the actual result.
-
- @staticmethod
- def message():
- return "No expected image hash found"
-
- def result_html_output(self, filename):
- return "<strong>%s</strong>" % self.message()
-
-
-class FailureMissingImage(FailureWithType):
- """Actual result image was missing."""
- OUT_FILENAMES = ("-actual.png",)
-
- @staticmethod
- def message():
- return "No expected image found"
-
- def result_html_output(self, filename):
- return ("<strong>%s</strong>" % self.message() +
- self.output_links(filename, self.OUT_FILENAMES))
-
-
-class FailureImageHashMismatch(FailureWithType):
- """Image hashes didn't match."""
- OUT_FILENAMES = ("-actual.png", "-expected.png", "-diff.png")
-
- @staticmethod
- def message():
- # We call this a simple image mismatch to avoid confusion, since
- # we link to the PNGs rather than the checksums.
- return "Image mismatch"
-
-
-class FailureImageHashIncorrect(FailureWithType):
- """Actual result hash is incorrect."""
- # Chrome doesn't know to display a .checksum file as text, so don't bother
- # putting in a link to the actual result.
-
- @staticmethod
- def message():
- return "Images match, expected image hash incorrect. "
-
- def result_html_output(self, filename):
- return "<strong>%s</strong>" % self.message()
-
-# Convenient collection of all failure classes for anything that might
-# need to enumerate over them all.
-ALL_FAILURE_CLASSES = (FailureTimeout, FailureCrash, FailureMissingResult,
- FailureTextMismatch, FailureMissingImageHash,
- FailureMissingImage, FailureImageHashMismatch,
- FailureImageHashIncorrect)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures_unittest.py
deleted file mode 100644
index 3e3528d..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_failures_unittest.py
+++ /dev/null
@@ -1,84 +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.
-
-""""Tests code paths not covered by the regular unit tests."""
-
-import unittest
-
-from webkitpy.layout_tests.layout_package.test_failures import *
-
-
-class Test(unittest.TestCase):
- def assertResultHtml(self, failure_obj):
- self.assertNotEqual(failure_obj.result_html_output('foo'), None)
-
- def assert_loads(self, cls):
- failure_obj = cls()
- s = failure_obj.dumps()
- new_failure_obj = TestFailure.loads(s)
- self.assertTrue(isinstance(new_failure_obj, cls))
-
- self.assertEqual(failure_obj, new_failure_obj)
-
- # Also test that != is implemented.
- self.assertFalse(failure_obj != new_failure_obj)
-
- def test_crash(self):
- self.assertResultHtml(FailureCrash())
-
- def test_hash_incorrect(self):
- self.assertResultHtml(FailureImageHashIncorrect())
-
- def test_missing(self):
- self.assertResultHtml(FailureMissingResult())
-
- def test_missing_image(self):
- self.assertResultHtml(FailureMissingImage())
-
- def test_missing_image_hash(self):
- self.assertResultHtml(FailureMissingImageHash())
-
- def test_timeout(self):
- self.assertResultHtml(FailureTimeout())
-
- def test_unknown_failure_type(self):
- class UnknownFailure(TestFailure):
- pass
-
- failure_obj = UnknownFailure()
- self.assertRaises(ValueError, determine_result_type, [failure_obj])
- self.assertRaises(NotImplementedError, failure_obj.message)
- self.assertRaises(NotImplementedError, failure_obj.result_html_output,
- "foo.txt")
-
- def test_loads(self):
- for c in ALL_FAILURE_CLASSES:
- self.assert_loads(c)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_output.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_output.py
deleted file mode 100644
index e809be6..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_output.py
+++ /dev/null
@@ -1,56 +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.
-
-
-class TestOutput(object):
- """Groups information about a test output for easy passing of data.
-
- This is used not only for a actual test output, but also for grouping
- expected test output.
- """
-
- def __init__(self, text, image, image_hash,
- crash=None, test_time=None, timeout=None, error=None):
- """Initializes a TestOutput object.
-
- Args:
- text: a text output
- image: an image output
- image_hash: a string containing the checksum of the image
- crash: a boolean indicating whether the driver crashed on the test
- test_time: a time which the test has taken
- timeout: a boolean indicating whehter the test timed out
- error: any unexpected or additional (or error) text output
- """
- self.text = text
- self.image = image
- self.image_hash = image_hash
- self.crash = crash
- self.test_time = test_time
- self.timeout = timeout
- self.error = error
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results.py
deleted file mode 100644
index 2417fb7..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results.py
+++ /dev/null
@@ -1,61 +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.
-
-import cPickle
-
-import test_failures
-
-
-class TestResult(object):
- """Data object containing the results of a single test."""
-
- @staticmethod
- def loads(str):
- return cPickle.loads(str)
-
- def __init__(self, filename, failures, test_run_time,
- total_time_for_all_diffs, time_for_diffs):
- self.failures = failures
- self.filename = filename
- self.test_run_time = test_run_time
- self.time_for_diffs = time_for_diffs
- self.total_time_for_all_diffs = total_time_for_all_diffs
- self.type = test_failures.determine_result_type(failures)
-
- def __eq__(self, other):
- return (self.filename == other.filename and
- self.failures == other.failures and
- self.test_run_time == other.test_run_time and
- self.time_for_diffs == other.time_for_diffs and
- self.total_time_for_all_diffs == other.total_time_for_all_diffs)
-
- def __ne__(self, other):
- return not (self == other)
-
- def dumps(self):
- return cPickle.dumps(self)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results_unittest.py
deleted file mode 100644
index 5921666..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results_unittest.py
+++ /dev/null
@@ -1,52 +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.
-
-import unittest
-
-from test_results import TestResult
-
-
-class Test(unittest.TestCase):
- def test_loads(self):
- result = TestResult(filename='foo',
- failures=[],
- test_run_time=1.1,
- total_time_for_all_diffs=0.5,
- time_for_diffs=0.5)
- s = result.dumps()
- new_result = TestResult.loads(s)
- self.assertTrue(isinstance(new_result, TestResult))
-
- self.assertEqual(new_result, result)
-
- # Also check that != is implemented.
- self.assertFalse(new_result != result)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results_uploader.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results_uploader.py
deleted file mode 100644
index 033c8c6..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/test_results_uploader.py
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/usr/bin/env 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.
-
-from __future__ import with_statement
-
-import codecs
-import mimetypes
-import socket
-import urllib2
-
-from webkitpy.common.net.networktransaction import NetworkTransaction
-
-def get_mime_type(filename):
- return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
-
-
-def _encode_multipart_form_data(fields, files):
- """Encode form fields for multipart/form-data.
-
- Args:
- fields: A sequence of (name, value) elements for regular form fields.
- files: A sequence of (name, filename, value) elements for data to be
- uploaded as files.
- Returns:
- (content_type, body) ready for httplib.HTTP instance.
-
- Source:
- http://code.google.com/p/rietveld/source/browse/trunk/upload.py
- """
- BOUNDARY = '-M-A-G-I-C---B-O-U-N-D-A-R-Y-'
- CRLF = '\r\n'
- lines = []
-
- for key, value in fields:
- lines.append('--' + BOUNDARY)
- lines.append('Content-Disposition: form-data; name="%s"' % key)
- lines.append('')
- if isinstance(value, unicode):
- value = value.encode('utf-8')
- lines.append(value)
-
- for key, filename, value in files:
- lines.append('--' + BOUNDARY)
- lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
- lines.append('Content-Type: %s' % get_mime_type(filename))
- lines.append('')
- if isinstance(value, unicode):
- value = value.encode('utf-8')
- lines.append(value)
-
- lines.append('--' + BOUNDARY + '--')
- lines.append('')
- body = CRLF.join(lines)
- content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
- return content_type, body
-
-
-class TestResultsUploader:
- def __init__(self, host):
- self._host = host
-
- def _upload_files(self, attrs, file_objs):
- url = "http://%s/testfile/upload" % self._host
- content_type, data = _encode_multipart_form_data(attrs, file_objs)
- headers = {"Content-Type": content_type}
- request = urllib2.Request(url, data, headers)
- urllib2.urlopen(request)
-
- def upload(self, params, files, timeout_seconds):
- file_objs = []
- for filename, path in files:
- with codecs.open(path, "rb") as file:
- file_objs.append(('file', filename, file.read()))
-
- orig_timeout = socket.getdefaulttimeout()
- try:
- socket.setdefaulttimeout(timeout_seconds)
- NetworkTransaction(timeout_seconds=timeout_seconds).run(
- lambda: self._upload_files(params, file_objs))
- finally:
- socket.setdefaulttimeout(orig_timeout)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/__init__.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/__init__.py
deleted file mode 100644
index e3ad6f4..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/__init__.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Port-specific entrypoints for the layout tests test infrastructure."""
-
-from factory import get
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/apache_http_server.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/apache_http_server.py
deleted file mode 100644
index 46617f6..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/apache_http_server.py
+++ /dev/null
@@ -1,230 +0,0 @@
-#!/usr/bin/env 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.
-
-"""A class to start/stop the apache http server used by layout tests."""
-
-
-from __future__ import with_statement
-
-import codecs
-import logging
-import optparse
-import os
-import re
-import subprocess
-import sys
-
-import http_server_base
-
-_log = logging.getLogger("webkitpy.layout_tests.port.apache_http_server")
-
-
-class LayoutTestApacheHttpd(http_server_base.HttpServerBase):
-
- def __init__(self, port_obj, output_dir):
- """Args:
- port_obj: handle to the platform-specific routines
- output_dir: the absolute path to the layout test result directory
- """
- http_server_base.HttpServerBase.__init__(self, port_obj)
- self._output_dir = output_dir
- self._httpd_proc = None
- port_obj.maybe_make_directory(output_dir)
-
- self.mappings = [{'port': 8000},
- {'port': 8080},
- {'port': 8081},
- {'port': 8443, 'sslcert': True}]
-
- # The upstream .conf file assumed the existence of /tmp/WebKit for
- # placing apache files like the lock file there.
- self._runtime_path = os.path.join("/tmp", "WebKit")
- port_obj.maybe_make_directory(self._runtime_path)
-
- # The PID returned when Apache is started goes away (due to dropping
- # privileges?). The proper controlling PID is written to a file in the
- # apache runtime directory.
- self._pid_file = os.path.join(self._runtime_path, 'httpd.pid')
-
- test_dir = self._port_obj.layout_tests_dir()
- js_test_resources_dir = self._cygwin_safe_join(test_dir, "fast", "js",
- "resources")
- mime_types_path = self._cygwin_safe_join(test_dir, "http", "conf",
- "mime.types")
- cert_file = self._cygwin_safe_join(test_dir, "http", "conf",
- "webkit-httpd.pem")
- access_log = self._cygwin_safe_join(output_dir, "access_log.txt")
- error_log = self._cygwin_safe_join(output_dir, "error_log.txt")
- document_root = self._cygwin_safe_join(test_dir, "http", "tests")
-
- # FIXME: We shouldn't be calling a protected method of _port_obj!
- executable = self._port_obj._path_to_apache()
- if self._is_cygwin():
- executable = self._get_cygwin_path(executable)
-
- cmd = [executable,
- '-f', "\"%s\"" % self._get_apache_config_file_path(test_dir, output_dir),
- '-C', "\'DocumentRoot \"%s\"\'" % document_root,
- '-c', "\'Alias /js-test-resources \"%s\"'" % js_test_resources_dir,
- '-C', "\'Listen %s\'" % "127.0.0.1:8000",
- '-C', "\'Listen %s\'" % "127.0.0.1:8081",
- '-c', "\'TypesConfig \"%s\"\'" % mime_types_path,
- '-c', "\'CustomLog \"%s\" common\'" % access_log,
- '-c', "\'ErrorLog \"%s\"\'" % error_log,
- '-C', "\'User \"%s\"\'" % os.environ.get("USERNAME",
- os.environ.get("USER", ""))]
-
- if self._is_cygwin():
- cygbin = self._port_obj._path_from_base('third_party', 'cygwin',
- 'bin')
- # Not entirely sure why, but from cygwin we need to run the
- # httpd command through bash.
- self._start_cmd = [
- os.path.join(cygbin, 'bash.exe'),
- '-c',
- 'PATH=%s %s' % (self._get_cygwin_path(cygbin), " ".join(cmd)),
- ]
- else:
- # TODO(ojan): When we get cygwin using Apache 2, use set the
- # cert file for cygwin as well.
- cmd.extend(['-c', "\'SSLCertificateFile %s\'" % cert_file])
- # Join the string here so that Cygwin/Windows and Mac/Linux
- # can use the same code. Otherwise, we could remove the single
- # quotes above and keep cmd as a sequence.
- self._start_cmd = " ".join(cmd)
-
- def _is_cygwin(self):
- return sys.platform in ("win32", "cygwin")
-
- def _cygwin_safe_join(self, *parts):
- """Returns a platform appropriate path."""
- path = os.path.join(*parts)
- if self._is_cygwin():
- return self._get_cygwin_path(path)
- return path
-
- def _get_cygwin_path(self, path):
- """Convert a Windows path to a cygwin path.
-
- The cygpath utility insists on converting paths that it thinks are
- Cygwin root paths to what it thinks the correct roots are. So paths
- such as "C:\b\slave\webkit-release\build\third_party\cygwin\bin"
- are converted to plain "/usr/bin". To avoid this, we
- do the conversion manually.
-
- The path is expected to be an absolute path, on any drive.
- """
- drive_regexp = re.compile(r'([a-z]):[/\\]', re.IGNORECASE)
-
- def lower_drive(matchobj):
- return '/cygdrive/%s/' % matchobj.group(1).lower()
- path = drive_regexp.sub(lower_drive, path)
- return path.replace('\\', '/')
-
- def _get_apache_config_file_path(self, test_dir, output_dir):
- """Returns the path to the apache config file to use.
- Args:
- test_dir: absolute path to the LayoutTests directory.
- output_dir: absolute path to the layout test results directory.
- """
- httpd_config = self._port_obj._path_to_apache_config_file()
- httpd_config_copy = os.path.join(output_dir, "httpd.conf")
- # httpd.conf is always utf-8 according to http://archive.apache.org/gnats/10125
- with codecs.open(httpd_config, "r", "utf-8") as httpd_config_file:
- httpd_conf = httpd_config_file.read()
- if self._is_cygwin():
- # This is a gross hack, but it lets us use the upstream .conf file
- # and our checked in cygwin. This tells the server the root
- # directory to look in for .so modules. It will use this path
- # plus the relative paths to the .so files listed in the .conf
- # file. We have apache/cygwin checked into our tree so
- # people don't have to install it into their cygwin.
- cygusr = self._port_obj._path_from_base('third_party', 'cygwin',
- 'usr')
- httpd_conf = httpd_conf.replace('ServerRoot "/usr"',
- 'ServerRoot "%s"' % self._get_cygwin_path(cygusr))
-
- with codecs.open(httpd_config_copy, "w", "utf-8") as file:
- file.write(httpd_conf)
-
- if self._is_cygwin():
- return self._get_cygwin_path(httpd_config_copy)
- return httpd_config_copy
-
- def _get_virtual_host_config(self, document_root, port, ssl=False):
- """Returns a <VirtualHost> directive block for an httpd.conf file.
- It will listen to 127.0.0.1 on each of the given port.
- """
- return '\n'.join(('<VirtualHost 127.0.0.1:%s>' % port,
- 'DocumentRoot "%s"' % document_root,
- ssl and 'SSLEngine On' or '',
- '</VirtualHost>', ''))
-
- def _start_httpd_process(self):
- """Starts the httpd process and returns whether there were errors."""
- # Use shell=True because we join the arguments into a string for
- # the sake of Window/Cygwin and it needs quoting that breaks
- # shell=False.
- # FIXME: We should not need to be joining shell arguments into strings.
- # shell=True is a trail of tears.
- # Note: Not thread safe: http://bugs.python.org/issue2320
- self._httpd_proc = subprocess.Popen(self._start_cmd,
- stderr=subprocess.PIPE,
- shell=True)
- err = self._httpd_proc.stderr.read()
- if len(err):
- _log.debug(err)
- return False
- return True
-
- def start(self):
- """Starts the apache http server."""
- # Stop any currently running servers.
- self.stop()
-
- _log.debug("Starting apache http server")
- server_started = self.wait_for_action(self._start_httpd_process)
- if server_started:
- _log.debug("Apache started. Testing ports")
- server_started = self.wait_for_action(
- self.is_server_running_on_all_ports)
-
- if server_started:
- _log.debug("Server successfully started")
- else:
- raise Exception('Failed to start http server')
-
- def stop(self):
- """Stops the apache http server."""
- _log.debug("Shutting down any running http servers")
- httpd_pid = None
- if os.path.exists(self._pid_file):
- httpd_pid = int(open(self._pid_file).readline())
- # FIXME: We shouldn't be calling a protected method of _port_obj!
- self._port_obj._shut_down_http_server(httpd_pid)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
deleted file mode 100644
index 757318d..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
+++ /dev/null
@@ -1,861 +0,0 @@
-#!/usr/bin/env 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 Google name 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.
-
-"""Abstract base class of Port-specific entrypoints for the layout tests
-test infrastructure (the Port and Driver classes)."""
-
-import cgi
-import difflib
-import errno
-import os
-import shlex
-import sys
-import time
-
-import apache_http_server
-import config as port_config
-import http_lock
-import http_server
-import test_files
-import websocket_server
-
-from webkitpy.common import system
-from webkitpy.common.system import filesystem
-from webkitpy.common.system import logutils
-from webkitpy.common.system import path
-from webkitpy.common.system.executive import Executive, ScriptError
-from webkitpy.common.system.user import User
-
-
-_log = logutils.get_logger(__file__)
-
-
-class DummyOptions(object):
- """Fake implementation of optparse.Values. Cloned from
- webkitpy.tool.mocktool.MockOptions.
-
- """
-
- def __init__(self, **kwargs):
- # The caller can set option values using keyword arguments. We don't
- # set any values by default because we don't know how this
- # object will be used. Generally speaking unit tests should
- # subclass this or provider wrapper functions that set a common
- # set of options.
- for key, value in kwargs.items():
- self.__dict__[key] = value
-
-
-# FIXME: This class should merge with webkitpy.webkit_port at some point.
-class Port(object):
- """Abstract class for Port-specific hooks for the layout_test package."""
-
- def __init__(self, port_name=None, options=None,
- executive=None,
- user=None,
- filesystem=None,
- config=None,
- **kwargs):
- self._name = port_name
- self._options = options
- if self._options is None:
- # FIXME: Ideally we'd have a package-wide way to get a
- # well-formed options object that had all of the necessary
- # options defined on it.
- self._options = DummyOptions()
- self._executive = executive or Executive()
- self._user = user or User()
- self._filesystem = filesystem or system.filesystem.FileSystem()
- self._config = config or port_config.Config(self._executive,
- self._filesystem)
- self._helper = None
- self._http_server = None
- self._webkit_base_dir = None
- self._websocket_server = None
- self._http_lock = None
-
- # Python's Popen has a bug that causes any pipes opened to a
- # process that can't be executed to be leaked. Since this
- # code is specifically designed to tolerate exec failures
- # to gracefully handle cases where wdiff is not installed,
- # the bug results in a massive file descriptor leak. As a
- # workaround, if an exec failure is ever experienced for
- # wdiff, assume it's not available. This will leak one
- # file descriptor but that's better than leaking each time
- # wdiff would be run.
- #
- # http://mail.python.org/pipermail/python-list/
- # 2008-August/505753.html
- # http://bugs.python.org/issue3210
- self._wdiff_available = True
-
- self._pretty_patch_path = self.path_from_webkit_base("BugsSite",
- "PrettyPatch", "prettify.rb")
- self._pretty_patch_available = True
- self.set_option_default('configuration', None)
- if self._options.configuration is None:
- self._options.configuration = self.default_configuration()
-
- def default_child_processes(self):
- """Return the number of DumpRenderTree instances to use for this
- port."""
- return self._executive.cpu_count()
-
- def baseline_path(self):
- """Return the absolute path to the directory to store new baselines
- in for this port."""
- raise NotImplementedError('Port.baseline_path')
-
- def baseline_search_path(self):
- """Return a list of absolute paths to directories to search under for
- baselines. The directories are searched in order."""
- raise NotImplementedError('Port.baseline_search_path')
-
- def check_build(self, needs_http):
- """This routine is used to ensure that the build is up to date
- and all the needed binaries are present."""
- raise NotImplementedError('Port.check_build')
-
- def check_sys_deps(self, needs_http):
- """If the port needs to do some runtime checks to ensure that the
- tests can be run successfully, it should override this routine.
- This step can be skipped with --nocheck-sys-deps.
-
- Returns whether the system is properly configured."""
- return True
-
- def check_image_diff(self, override_step=None, logging=True):
- """This routine is used to check whether image_diff binary exists."""
- raise NotImplementedError('Port.check_image_diff')
-
- def check_pretty_patch(self):
- """Checks whether we can use the PrettyPatch ruby script."""
-
- # check if Ruby is installed
- try:
- result = self._executive.run_command(['ruby', '--version'])
- except OSError, e:
- if e.errno in [errno.ENOENT, errno.EACCES, errno.ECHILD]:
- _log.error("Ruby is not installed; "
- "can't generate pretty patches.")
- _log.error('')
- return False
-
- if not self.path_exists(self._pretty_patch_path):
- _log.error('Unable to find %s .' % self._pretty_patch_path)
- _log.error("Can't generate pretty patches.")
- _log.error('')
- return False
-
- return True
-
- def compare_text(self, expected_text, actual_text):
- """Return whether or not the two strings are *not* equal. This
- routine is used to diff text output.
-
- While this is a generic routine, we include it in the Port
- interface so that it can be overriden for testing purposes."""
- return expected_text != actual_text
-
- def diff_image(self, expected_contents, actual_contents,
- diff_filename=None, tolerance=0):
- """Compare two images and produce a delta image file.
-
- Return True if the two images are different, False if they are the same.
- Also produce a delta image of the two images and write that into
- |diff_filename| if it is not None.
-
- |tolerance| should be a percentage value (0.0 - 100.0).
- If it is omitted, the port default tolerance value is used.
-
- """
- raise NotImplementedError('Port.diff_image')
-
-
- def diff_text(self, expected_text, actual_text,
- expected_filename, actual_filename):
- """Returns a string containing the diff of the two text strings
- in 'unified diff' format.
-
- While this is a generic routine, we include it in the Port
- interface so that it can be overriden for testing purposes."""
-
- # The filenames show up in the diff output, make sure they're
- # raw bytes and not unicode, so that they don't trigger join()
- # trying to decode the input.
- def to_raw_bytes(str):
- if isinstance(str, unicode):
- return str.encode('utf-8')
- return str
- expected_filename = to_raw_bytes(expected_filename)
- actual_filename = to_raw_bytes(actual_filename)
- diff = difflib.unified_diff(expected_text.splitlines(True),
- actual_text.splitlines(True),
- expected_filename,
- actual_filename)
- return ''.join(diff)
-
- def driver_name(self):
- """Returns the name of the actual binary that is performing the test,
- so that it can be referred to in log messages. In most cases this
- will be DumpRenderTree, but if a port uses a binary with a different
- name, it can be overridden here."""
- return "DumpRenderTree"
-
- def expected_baselines(self, filename, suffix, all_baselines=False):
- """Given a test name, finds where the baseline results are located.
-
- Args:
- filename: absolute filename to test file
- suffix: file suffix of the expected results, including dot; e.g.
- '.txt' or '.png'. This should not be None, but may be an empty
- string.
- all_baselines: If True, return an ordered list of all baseline paths
- for the given platform. If False, return only the first one.
- Returns
- a list of ( platform_dir, results_filename ), where
- platform_dir - abs path to the top of the results tree (or test
- tree)
- results_filename - relative path from top of tree to the results
- file
- (os.path.join of the two gives you the full path to the file,
- unless None was returned.)
- Return values will be in the format appropriate for the current
- platform (e.g., "\\" for path separators on Windows). If the results
- file is not found, then None will be returned for the directory,
- but the expected relative pathname will still be returned.
-
- This routine is generic but lives here since it is used in
- conjunction with the other baseline and filename routines that are
- platform specific.
- """
- testname = os.path.splitext(self.relative_test_filename(filename))[0]
-
- baseline_filename = testname + '-expected' + suffix
-
- baseline_search_path = self.baseline_search_path()
-
- baselines = []
- for platform_dir in baseline_search_path:
- if self.path_exists(self._filesystem.join(platform_dir,
- baseline_filename)):
- baselines.append((platform_dir, baseline_filename))
-
- if not all_baselines and baselines:
- return baselines
-
- # If it wasn't found in a platform directory, return the expected
- # result in the test directory, even if no such file actually exists.
- platform_dir = self.layout_tests_dir()
- if self.path_exists(self._filesystem.join(platform_dir,
- baseline_filename)):
- baselines.append((platform_dir, baseline_filename))
-
- if baselines:
- return baselines
-
- return [(None, baseline_filename)]
-
- def expected_filename(self, filename, suffix):
- """Given a test name, returns an absolute path to its expected results.
-
- If no expected results are found in any of the searched directories,
- the directory in which the test itself is located will be returned.
- The return value is in the format appropriate for the platform
- (e.g., "\\" for path separators on windows).
-
- Args:
- filename: absolute filename to test file
- suffix: file suffix of the expected results, including dot; e.g. '.txt'
- or '.png'. This should not be None, but may be an empty string.
- platform: the most-specific directory name to use to build the
- search list of directories, e.g., 'chromium-win', or
- 'chromium-mac-leopard' (we follow the WebKit format)
-
- This routine is generic but is implemented here to live alongside
- the other baseline and filename manipulation routines.
- """
- platform_dir, baseline_filename = self.expected_baselines(
- filename, suffix)[0]
- if platform_dir:
- return self._filesystem.join(platform_dir, baseline_filename)
- return self._filesystem.join(self.layout_tests_dir(), baseline_filename)
-
- def expected_checksum(self, test):
- """Returns the checksum of the image we expect the test to produce, or None if it is a text-only test."""
- path = self.expected_filename(test, '.checksum')
- if not self.path_exists(path):
- return None
- return self._filesystem.read_text_file(path)
-
- def expected_image(self, test):
- """Returns the image we expect the test to produce."""
- path = self.expected_filename(test, '.png')
- if not self.path_exists(path):
- return None
- return self._filesystem.read_binary_file(path)
-
- def expected_text(self, test):
- """Returns the text output we expect the test to produce."""
- # FIXME: DRT output is actually utf-8, but since we don't decode the
- # output from DRT (instead treating it as a binary string), we read the
- # baselines as a binary string, too.
- path = self.expected_filename(test, '.txt')
- if not self.path_exists(path):
- return ''
- text = self._filesystem.read_binary_file(path)
- return text.strip("\r\n").replace("\r\n", "\n") + "\n"
-
- def filename_to_uri(self, filename):
- """Convert a test file (which is an absolute path) to a URI."""
- LAYOUTTEST_HTTP_DIR = "http/tests/"
- LAYOUTTEST_WEBSOCKET_DIR = "http/tests/websocket/tests/"
-
- relative_path = self.relative_test_filename(filename)
- port = None
- use_ssl = False
-
- if (relative_path.startswith(LAYOUTTEST_WEBSOCKET_DIR)
- or relative_path.startswith(LAYOUTTEST_HTTP_DIR)):
- relative_path = relative_path[len(LAYOUTTEST_HTTP_DIR):]
- port = 8000
-
- # Make http/tests/local run as local files. This is to mimic the
- # logic in run-webkit-tests.
- #
- # TODO(dpranke): remove the media reference and the SSL reference?
- if (port and not relative_path.startswith("local/") and
- not relative_path.startswith("media/")):
- if relative_path.startswith("ssl/"):
- port += 443
- protocol = "https"
- else:
- protocol = "http"
- return "%s://127.0.0.1:%u/%s" % (protocol, port, relative_path)
-
- return path.abspath_to_uri(os.path.abspath(filename))
-
- def tests(self, paths):
- """Return the list of tests found (relative to layout_tests_dir()."""
- return test_files.find(self, paths)
-
- def test_dirs(self):
- """Returns the list of top-level test directories.
-
- Used by --clobber-old-results."""
- layout_tests_dir = self.layout_tests_dir()
- return filter(lambda x: self._filesystem.isdir(self._filesystem.join(layout_tests_dir, x)),
- self._filesystem.listdir(layout_tests_dir))
-
- def path_isdir(self, path):
- """Return True if the path refers to a directory of tests."""
- # Used by test_expectations.py to apply rules to whole directories.
- return self._filesystem.isdir(path)
-
- def path_exists(self, path):
- """Return True if the path refers to an existing test or baseline."""
- # Used by test_expectations.py to determine if an entry refers to a
- # valid test and by printing.py to determine if baselines exist.
- return self._filesystem.exists(path)
-
- def driver_cmd_line(self):
- """Prints the DRT command line that will be used."""
- driver = self.create_driver(0)
- return driver.cmd_line()
-
- def update_baseline(self, path, data, encoding):
- """Updates the baseline for a test.
-
- Args:
- path: the actual path to use for baseline, not the path to
- the test. This function is used to update either generic or
- platform-specific baselines, but we can't infer which here.
- data: contents of the baseline.
- encoding: file encoding to use for the baseline.
- """
- # FIXME: remove the encoding parameter in favor of text/binary
- # functions.
- if encoding is None:
- self._filesystem.write_binary_file(path, data)
- else:
- self._filesystem.write_text_file(path, data)
-
- def uri_to_test_name(self, uri):
- """Return the base layout test name for a given URI.
-
- This returns the test name for a given URI, e.g., if you passed in
- "file:///src/LayoutTests/fast/html/keygen.html" it would return
- "fast/html/keygen.html".
-
- """
- test = uri
- if uri.startswith("file:///"):
- prefix = path.abspath_to_uri(self.layout_tests_dir()) + "/"
- return test[len(prefix):]
-
- if uri.startswith("http://127.0.0.1:8880/"):
- # websocket tests
- return test.replace('http://127.0.0.1:8880/', '')
-
- if uri.startswith("http://"):
- # regular HTTP test
- return test.replace('http://127.0.0.1:8000/', 'http/tests/')
-
- if uri.startswith("https://"):
- return test.replace('https://127.0.0.1:8443/', 'http/tests/')
-
- raise NotImplementedError('unknown url type: %s' % uri)
-
- def layout_tests_dir(self):
- """Return the absolute path to the top of the LayoutTests directory."""
- return self.path_from_webkit_base('LayoutTests')
-
- def skips_layout_test(self, test_name):
- """Figures out if the givent test is being skipped or not.
-
- Test categories are handled as well."""
- for test_or_category in self.skipped_layout_tests():
- if test_or_category == test_name:
- return True
- category = self._filesystem.join(self.layout_tests_dir(),
- test_or_category)
- if (self._filesystem.isdir(category) and
- test_name.startswith(test_or_category)):
- return True
- return False
-
- def maybe_make_directory(self, *path):
- """Creates the specified directory if it doesn't already exist."""
- self._filesystem.maybe_make_directory(*path)
-
- def name(self):
- """Return the name of the port (e.g., 'mac', 'chromium-win-xp').
-
- Note that this is different from the test_platform_name(), which
- may be different (e.g., 'win-xp' instead of 'chromium-win-xp'."""
- return self._name
-
- def get_option(self, name, default_value=None):
- # FIXME: Eventually we should not have to do a test for
- # hasattr(), and we should be able to just do
- # self.options.value. See additional FIXME in the constructor.
- if hasattr(self._options, name):
- return getattr(self._options, name)
- return default_value
-
- def set_option_default(self, name, default_value):
- if not hasattr(self._options, name):
- return setattr(self._options, name, default_value)
-
- def path_from_webkit_base(self, *comps):
- """Returns the full path to path made by joining the top of the
- WebKit source tree and the list of path components in |*comps|."""
- return self._config.path_from_webkit_base(*comps)
-
- def script_path(self, script_name):
- return self._config.script_path(script_name)
-
- def path_to_test_expectations_file(self):
- """Update the test expectations to the passed-in string.
-
- This is used by the rebaselining tool. Raises NotImplementedError
- if the port does not use expectations files."""
- raise NotImplementedError('Port.path_to_test_expectations_file')
-
- def relative_test_filename(self, filename):
- """Relative unix-style path for a filename under the LayoutTests
- directory. Filenames outside the LayoutTests directory should raise
- an error."""
- #assert(filename.startswith(self.layout_tests_dir()))
- return filename[len(self.layout_tests_dir()) + 1:]
-
- def results_directory(self):
- """Absolute path to the place to store the test results."""
- raise NotImplementedError('Port.results_directory')
-
- def setup_test_run(self):
- """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.copy()
-
- def show_results_html_file(self, results_filename):
- """This routine should display the HTML file pointed at by
- results_filename in a users' browser."""
- return self._user.open_url(results_filename)
-
- def create_driver(self, worker_number):
- """Return a newly created base.Driver subclass for starting/stopping
- the test driver."""
- raise NotImplementedError('Port.create_driver')
-
- def start_helper(self):
- """If a port needs to reconfigure graphics settings or do other
- things to ensure a known test configuration, it should override this
- method."""
- pass
-
- def start_http_server(self):
- """Start a web server if it is available. Do nothing if
- it isn't. This routine is allowed to (and may) fail if a server
- is already running."""
- if self.get_option('use_apache'):
- self._http_server = apache_http_server.LayoutTestApacheHttpd(self,
- self.get_option('results_directory'))
- else:
- self._http_server = http_server.Lighttpd(self,
- self.get_option('results_directory'))
- self._http_server.start()
-
- def start_websocket_server(self):
- """Start a websocket server if it is available. Do nothing if
- it isn't. This routine is allowed to (and may) fail if a server
- is already running."""
- self._websocket_server = websocket_server.PyWebSocket(self,
- self.get_option('results_directory'))
- self._websocket_server.start()
-
- def acquire_http_lock(self):
- self._http_lock = http_lock.HttpLock(None)
- self._http_lock.wait_for_httpd_lock()
-
- def stop_helper(self):
- """Shut down the test helper if it is running. Do nothing if
- it isn't, or it isn't available. If a port overrides start_helper()
- it must override this routine as well."""
- pass
-
- def stop_http_server(self):
- """Shut down the http server if it is running. Do nothing if
- it isn't, or it isn't available."""
- if self._http_server:
- self._http_server.stop()
-
- def stop_websocket_server(self):
- """Shut down the websocket server if it is running. Do nothing if
- it isn't, or it isn't available."""
- if self._websocket_server:
- self._websocket_server.stop()
-
- def release_http_lock(self):
- if self._http_lock:
- self._http_lock.cleanup_http_lock()
-
- def test_expectations(self):
- """Returns the test expectations for this port.
-
- Basically this string should contain the equivalent of a
- test_expectations file. See test_expectations.py for more details."""
- raise NotImplementedError('Port.test_expectations')
-
- def test_expectations_overrides(self):
- """Returns an optional set of overrides for the test_expectations.
-
- This is used by ports that have code in two repositories, and where
- it is possible that you might need "downstream" expectations that
- temporarily override the "upstream" expectations until the port can
- sync up the two repos."""
- return None
-
- def test_base_platform_names(self):
- """Return a list of the 'base' platforms on your port. The base
- platforms represent different architectures, operating systems,
- or implementations (as opposed to different versions of a single
- platform). For example, 'mac' and 'win' might be different base
- platforms, wherease 'mac-tiger' and 'mac-leopard' might be
- different platforms. This routine is used by the rebaselining tool
- and the dashboards, and the strings correspond to the identifiers
- in your test expectations (*not* necessarily the platform names
- themselves)."""
- raise NotImplementedError('Port.base_test_platforms')
-
- def test_platform_name(self):
- """Returns the string that corresponds to the given platform name
- in the test expectations. This may be the same as name(), or it
- may be different. For example, chromium returns 'mac' for
- 'chromium-mac'."""
- raise NotImplementedError('Port.test_platform_name')
-
- def test_platforms(self):
- """Returns the list of test platform identifiers as used in the
- test_expectations and on dashboards, the rebaselining tool, etc.
-
- Note that this is not necessarily the same as the list of ports,
- which must be globally unique (e.g., both 'chromium-mac' and 'mac'
- might return 'mac' as a test_platform name'."""
- raise NotImplementedError('Port.platforms')
-
- def test_platform_name_to_name(self, test_platform_name):
- """Returns the Port platform name that corresponds to the name as
- referenced in the expectations file. E.g., "mac" returns
- "chromium-mac" on the Chromium ports."""
- raise NotImplementedError('Port.test_platform_name_to_name')
-
- def version(self):
- """Returns a string indicating the version of a given platform, e.g.
- '-leopard' or '-xp'.
-
- This is used to help identify the exact port when parsing test
- expectations, determining search paths, and logging information."""
- raise NotImplementedError('Port.version')
-
- def test_repository_paths(self):
- """Returns a list of (repository_name, repository_path) tuples
- of its depending code base. By default it returns a list that only
- contains a ('webkit', <webkitRepossitoryPath>) tuple.
- """
- return [('webkit', self.layout_tests_dir())]
-
-
- _WDIFF_DEL = '##WDIFF_DEL##'
- _WDIFF_ADD = '##WDIFF_ADD##'
- _WDIFF_END = '##WDIFF_END##'
-
- def _format_wdiff_output_as_html(self, wdiff):
- wdiff = cgi.escape(wdiff)
- wdiff = wdiff.replace(self._WDIFF_DEL, "<span class=del>")
- wdiff = wdiff.replace(self._WDIFF_ADD, "<span class=add>")
- wdiff = wdiff.replace(self._WDIFF_END, "</span>")
- html = "<head><style>.del { background: #faa; } "
- html += ".add { background: #afa; }</style></head>"
- html += "<pre>%s</pre>" % wdiff
- return html
-
- def _wdiff_command(self, actual_filename, expected_filename):
- executable = self._path_to_wdiff()
- return [executable,
- "--start-delete=%s" % self._WDIFF_DEL,
- "--end-delete=%s" % self._WDIFF_END,
- "--start-insert=%s" % self._WDIFF_ADD,
- "--end-insert=%s" % self._WDIFF_END,
- actual_filename,
- expected_filename]
-
- @staticmethod
- def _handle_wdiff_error(script_error):
- # Exit 1 means the files differed, any other exit code is an error.
- if script_error.exit_code != 1:
- raise script_error
-
- def _run_wdiff(self, actual_filename, expected_filename):
- """Runs wdiff and may throw exceptions.
- This is mostly a hook for unit testing."""
- # Diffs are treated as binary as they may include multiple files
- # with conflicting encodings. Thus we do not decode the output.
- command = self._wdiff_command(actual_filename, expected_filename)
- wdiff = self._executive.run_command(command, decode_output=False,
- error_handler=self._handle_wdiff_error)
- return self._format_wdiff_output_as_html(wdiff)
-
- def wdiff_text(self, actual_filename, expected_filename):
- """Returns a string of HTML indicating the word-level diff of the
- contents of the two filenames. Returns an empty string if word-level
- diffing isn't available."""
- if not self._wdiff_available:
- return ""
- try:
- # It's possible to raise a ScriptError we pass wdiff invalid paths.
- return self._run_wdiff(actual_filename, expected_filename)
- except OSError, e:
- if e.errno in [errno.ENOENT, errno.EACCES, errno.ECHILD]:
- # Silently ignore cases where wdiff is missing.
- self._wdiff_available = False
- return ""
- raise
-
- # This is a class variable so we can test error output easily.
- _pretty_patch_error_html = "Failed to run PrettyPatch, see error log."
-
- def pretty_patch_text(self, diff_path):
- if not self._pretty_patch_available:
- return self._pretty_patch_error_html
- command = ("ruby", "-I", os.path.dirname(self._pretty_patch_path),
- self._pretty_patch_path, diff_path)
- try:
- # Diffs are treated as binary (we pass decode_output=False) as they
- # may contain multiple files of conflicting encodings.
- return self._executive.run_command(command, decode_output=False)
- except OSError, e:
- # If the system is missing ruby log the error and stop trying.
- self._pretty_patch_available = False
- _log.error("Failed to run PrettyPatch (%s): %s" % (command, e))
- return self._pretty_patch_error_html
- except ScriptError, e:
- # If ruby failed to run for some reason, log the command
- # output and stop trying.
- self._pretty_patch_available = False
- _log.error("Failed to run PrettyPatch (%s):\n%s" % (command,
- e.message_with_output()))
- return self._pretty_patch_error_html
-
- def default_configuration(self):
- return self._config.default_configuration()
-
- #
- # PROTECTED ROUTINES
- #
- # The routines below should only be called by routines in this class
- # or any of its subclasses.
- #
- def _webkit_build_directory(self, args):
- return self._config.build_directory(args[0])
-
- def _path_to_apache(self):
- """Returns the full path to the apache binary.
-
- This is needed only by ports that use the apache_http_server module."""
- raise NotImplementedError('Port.path_to_apache')
-
- def _path_to_apache_config_file(self):
- """Returns the full path to the apache binary.
-
- This is needed only by ports that use the apache_http_server module."""
- raise NotImplementedError('Port.path_to_apache_config_file')
-
- def _path_to_driver(self, configuration=None):
- """Returns the full path to the test driver (DumpRenderTree)."""
- raise NotImplementedError('Port._path_to_driver')
-
- def _path_to_webcore_library(self):
- """Returns the full path to a built copy of WebCore."""
- raise NotImplementedError('Port.path_to_webcore_library')
-
- def _path_to_helper(self):
- """Returns the full path to the layout_test_helper binary, which
- is used to help configure the system for the test run, or None
- if no helper is needed.
-
- This is likely only used by start/stop_helper()."""
- raise NotImplementedError('Port._path_to_helper')
-
- def _path_to_image_diff(self):
- """Returns the full path to the image_diff binary, or None if it
- is not available.
-
- This is likely used only by diff_image()"""
- raise NotImplementedError('Port.path_to_image_diff')
-
- def _path_to_lighttpd(self):
- """Returns the path to the LigHTTPd binary.
-
- This is needed only by ports that use the http_server.py module."""
- raise NotImplementedError('Port._path_to_lighttpd')
-
- def _path_to_lighttpd_modules(self):
- """Returns the path to the LigHTTPd modules directory.
-
- This is needed only by ports that use the http_server.py module."""
- raise NotImplementedError('Port._path_to_lighttpd_modules')
-
- def _path_to_lighttpd_php(self):
- """Returns the path to the LigHTTPd PHP executable.
-
- This is needed only by ports that use the http_server.py module."""
- raise NotImplementedError('Port._path_to_lighttpd_php')
-
- def _path_to_wdiff(self):
- """Returns the full path to the wdiff binary, or None if it is
- not available.
-
- This is likely used only by wdiff_text()"""
- raise NotImplementedError('Port._path_to_wdiff')
-
- def _shut_down_http_server(self, pid):
- """Forcefully and synchronously kills the web server.
-
- This routine should only be called from http_server.py or its
- subclasses."""
- raise NotImplementedError('Port._shut_down_http_server')
-
- def _webkit_baseline_path(self, platform):
- """Return the full path to the top of the baseline tree for a
- given platform."""
- return self._filesystem.join(self.layout_tests_dir(), 'platform',
- platform)
-
-
-class Driver:
- """Abstract interface for the DumpRenderTree interface."""
-
- def __init__(self, port, worker_number):
- """Initialize a Driver to subsequently run tests.
-
- Typically this routine will spawn DumpRenderTree in a config
- ready for subsequent input.
-
- port - reference back to the port object.
- worker_number - identifier for a particular worker/driver instance
- """
- raise NotImplementedError('Driver.__init__')
-
- def run_test(self, test_input):
- """Run a single test and return the results.
-
- Note that it is okay if a test times out or crashes and leaves
- the driver in an indeterminate state. The upper layers of the program
- are responsible for cleaning up and ensuring things are okay.
-
- Args:
- test_input: a TestInput object
-
- Returns a TestOutput object.
- """
- raise NotImplementedError('Driver.run_test')
-
- # FIXME: This is static so we can test it w/o creating a Base instance.
- @classmethod
- def _command_wrapper(cls, wrapper_option):
- # Hook for injecting valgrind or other runtime instrumentation,
- # used by e.g. tools/valgrind/valgrind_tests.py.
- wrapper = []
- browser_wrapper = os.environ.get("BROWSER_WRAPPER", None)
- if browser_wrapper:
- # FIXME: There seems to be no reason to use BROWSER_WRAPPER over --wrapper.
- # Remove this code any time after the date listed below.
- _log.error("BROWSER_WRAPPER is deprecated, please use --wrapper instead.")
- _log.error("BROWSER_WRAPPER will be removed any time after June 1st 2010 and your scripts will break.")
- wrapper += [browser_wrapper]
-
- if wrapper_option:
- wrapper += shlex.split(wrapper_option)
- return wrapper
-
- def poll(self):
- """Returns None if the Driver is still running. Returns the returncode
- if it has exited."""
- raise NotImplementedError('Driver.poll')
-
- def stop(self):
- raise NotImplementedError('Driver.stop')
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py
deleted file mode 100644
index 8d586e3..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py
+++ /dev/null
@@ -1,315 +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.
-
-import optparse
-import os
-import sys
-import tempfile
-import unittest
-
-from webkitpy.common.system.executive import Executive, ScriptError
-from webkitpy.common.system import executive_mock
-from webkitpy.common.system import filesystem
-from webkitpy.common.system import outputcapture
-from webkitpy.common.system.path import abspath_to_uri
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.tool import mocktool
-
-import base
-import config
-import config_mock
-
-
-class PortTest(unittest.TestCase):
- def test_format_wdiff_output_as_html(self):
- output = "OUTPUT %s %s %s" % (base.Port._WDIFF_DEL, base.Port._WDIFF_ADD, base.Port._WDIFF_END)
- html = base.Port()._format_wdiff_output_as_html(output)
- expected_html = "<head><style>.del { background: #faa; } .add { background: #afa; }</style></head><pre>OUTPUT <span class=del> <span class=add> </span></pre>"
- self.assertEqual(html, expected_html)
-
- def test_wdiff_command(self):
- port = base.Port()
- port._path_to_wdiff = lambda: "/path/to/wdiff"
- command = port._wdiff_command("/actual/path", "/expected/path")
- expected_command = [
- "/path/to/wdiff",
- "--start-delete=##WDIFF_DEL##",
- "--end-delete=##WDIFF_END##",
- "--start-insert=##WDIFF_ADD##",
- "--end-insert=##WDIFF_END##",
- "/actual/path",
- "/expected/path",
- ]
- self.assertEqual(command, expected_command)
-
- def _file_with_contents(self, contents, encoding="utf-8"):
- new_file = tempfile.NamedTemporaryFile()
- new_file.write(contents.encode(encoding))
- new_file.flush()
- return new_file
-
- def test_pretty_patch_os_error(self):
- port = base.Port(executive=executive_mock.MockExecutive2(exception=OSError))
- oc = outputcapture.OutputCapture()
- oc.capture_output()
- self.assertEqual(port.pretty_patch_text("patch.txt"),
- port._pretty_patch_error_html)
-
- # This tests repeated calls to make sure we cache the result.
- self.assertEqual(port.pretty_patch_text("patch.txt"),
- port._pretty_patch_error_html)
- oc.restore_output()
-
- def test_pretty_patch_script_error(self):
- # FIXME: This is some ugly white-box test hacking ...
- base._pretty_patch_available = True
- port = base.Port(executive=executive_mock.MockExecutive2(exception=ScriptError))
- self.assertEqual(port.pretty_patch_text("patch.txt"),
- port._pretty_patch_error_html)
-
- # This tests repeated calls to make sure we cache the result.
- self.assertEqual(port.pretty_patch_text("patch.txt"),
- port._pretty_patch_error_html)
-
- def test_run_wdiff(self):
- executive = Executive()
- # This may fail on some systems. We could ask the port
- # object for the wdiff path, but since we don't know what
- # port object to use, this is sufficient for now.
- try:
- wdiff_path = executive.run_command(["which", "wdiff"]).rstrip()
- except Exception, e:
- wdiff_path = None
-
- port = base.Port()
- port._path_to_wdiff = lambda: wdiff_path
-
- if wdiff_path:
- # "with tempfile.NamedTemporaryFile() as actual" does not seem to work in Python 2.5
- actual = self._file_with_contents(u"foo")
- expected = self._file_with_contents(u"bar")
- wdiff = port._run_wdiff(actual.name, expected.name)
- expected_wdiff = "<head><style>.del { background: #faa; } .add { background: #afa; }</style></head><pre><span class=del>foo</span><span class=add>bar</span></pre>"
- self.assertEqual(wdiff, expected_wdiff)
- # Running the full wdiff_text method should give the same result.
- port._wdiff_available = True # In case it's somehow already disabled.
- wdiff = port.wdiff_text(actual.name, expected.name)
- self.assertEqual(wdiff, expected_wdiff)
- # wdiff should still be available after running wdiff_text with a valid diff.
- self.assertTrue(port._wdiff_available)
- actual.close()
- expected.close()
-
- # Bogus paths should raise a script error.
- self.assertRaises(ScriptError, port._run_wdiff, "/does/not/exist", "/does/not/exist2")
- self.assertRaises(ScriptError, port.wdiff_text, "/does/not/exist", "/does/not/exist2")
- # wdiff will still be available after running wdiff_text with invalid paths.
- self.assertTrue(port._wdiff_available)
- base._wdiff_available = True
-
- # If wdiff does not exist _run_wdiff should throw an OSError.
- port._path_to_wdiff = lambda: "/invalid/path/to/wdiff"
- self.assertRaises(OSError, port._run_wdiff, "foo", "bar")
-
- # wdiff_text should not throw an error if wdiff does not exist.
- self.assertEqual(port.wdiff_text("foo", "bar"), "")
- # However wdiff should not be available after running wdiff_text if wdiff is missing.
- self.assertFalse(port._wdiff_available)
-
- def test_diff_text(self):
- port = base.Port()
- # Make sure that we don't run into decoding exceptions when the
- # filenames are unicode, with regular or malformed input (expected or
- # actual input is always raw bytes, not unicode).
- port.diff_text('exp', 'act', 'exp.txt', 'act.txt')
- port.diff_text('exp', 'act', u'exp.txt', 'act.txt')
- port.diff_text('exp', 'act', u'a\xac\u1234\u20ac\U00008000', 'act.txt')
-
- port.diff_text('exp' + chr(255), 'act', 'exp.txt', 'act.txt')
- port.diff_text('exp' + chr(255), 'act', u'exp.txt', 'act.txt')
-
- # Though expected and actual files should always be read in with no
- # encoding (and be stored as str objects), test unicode inputs just to
- # be safe.
- port.diff_text(u'exp', 'act', 'exp.txt', 'act.txt')
- port.diff_text(
- u'a\xac\u1234\u20ac\U00008000', 'act', 'exp.txt', 'act.txt')
-
- # And make sure we actually get diff output.
- diff = port.diff_text('foo', 'bar', 'exp.txt', 'act.txt')
- self.assertTrue('foo' in diff)
- self.assertTrue('bar' in diff)
- self.assertTrue('exp.txt' in diff)
- self.assertTrue('act.txt' in diff)
- self.assertFalse('nosuchthing' in diff)
-
- def test_default_configuration_notfound(self):
- # Test that we delegate to the config object properly.
- port = base.Port(config=config_mock.MockConfig(default_configuration='default'))
- self.assertEqual(port.default_configuration(), 'default')
-
- def test_layout_tests_skipping(self):
- port = base.Port()
- port.skipped_layout_tests = lambda: ['foo/bar.html', 'media']
- self.assertTrue(port.skips_layout_test('foo/bar.html'))
- self.assertTrue(port.skips_layout_test('media/video-zoom.html'))
- self.assertFalse(port.skips_layout_test('foo/foo.html'))
-
- def test_setup_test_run(self):
- port = base.Port()
- # This routine is a no-op. We just test it for coverage.
- port.setup_test_run()
-
- def test_test_dirs(self):
- port = base.Port()
- dirs = port.test_dirs()
- self.assertTrue('canvas' in dirs)
- self.assertTrue('css2.1' in dirs)
-
- def test_filename_to_uri(self):
- port = base.Port()
- layout_test_dir = port.layout_tests_dir()
- test_file = os.path.join(layout_test_dir, "foo", "bar.html")
-
- # On Windows, absolute paths are of the form "c:\foo.txt". However,
- # all current browsers (except for Opera) normalize file URLs by
- # prepending an additional "/" as if the absolute path was
- # "/c:/foo.txt". This means that all file URLs end up with "file:///"
- # at the beginning.
- if sys.platform == 'win32':
- prefix = "file:///"
- path = test_file.replace("\\", "/")
- else:
- prefix = "file://"
- path = test_file
-
- self.assertEqual(port.filename_to_uri(test_file),
- abspath_to_uri(test_file))
-
- def test_get_option__set(self):
- options, args = optparse.OptionParser().parse_args([])
- options.foo = 'bar'
- port = base.Port(options=options)
- self.assertEqual(port.get_option('foo'), 'bar')
-
- def test_get_option__unset(self):
- port = base.Port()
- self.assertEqual(port.get_option('foo'), None)
-
- def test_get_option__default(self):
- port = base.Port()
- self.assertEqual(port.get_option('foo', 'bar'), 'bar')
-
- def test_set_option_default__unset(self):
- port = base.Port()
- port.set_option_default('foo', 'bar')
- self.assertEqual(port.get_option('foo'), 'bar')
-
- def test_set_option_default__set(self):
- options, args = optparse.OptionParser().parse_args([])
- options.foo = 'bar'
- port = base.Port(options=options)
- # This call should have no effect.
- port.set_option_default('foo', 'new_bar')
- self.assertEqual(port.get_option('foo'), 'bar')
-
- def test_name__unset(self):
- port = base.Port()
- self.assertEqual(port.name(), None)
-
- def test_name__set(self):
- port = base.Port(port_name='foo')
- self.assertEqual(port.name(), 'foo')
-
-
-class VirtualTest(unittest.TestCase):
- """Tests that various methods expected to be virtual are."""
- def assertVirtual(self, method, *args, **kwargs):
- self.assertRaises(NotImplementedError, method, *args, **kwargs)
-
- def test_virtual_methods(self):
- port = base.Port()
- self.assertVirtual(port.baseline_path)
- self.assertVirtual(port.baseline_search_path)
- self.assertVirtual(port.check_build, None)
- self.assertVirtual(port.check_image_diff)
- self.assertVirtual(port.create_driver, 0)
- self.assertVirtual(port.diff_image, None, None)
- self.assertVirtual(port.path_to_test_expectations_file)
- self.assertVirtual(port.test_platform_name)
- self.assertVirtual(port.results_directory)
- self.assertVirtual(port.test_expectations)
- self.assertVirtual(port.test_base_platform_names)
- self.assertVirtual(port.test_platform_name)
- self.assertVirtual(port.test_platforms)
- self.assertVirtual(port.test_platform_name_to_name, None)
- self.assertVirtual(port.version)
- self.assertVirtual(port._path_to_apache)
- self.assertVirtual(port._path_to_apache_config_file)
- self.assertVirtual(port._path_to_driver)
- self.assertVirtual(port._path_to_helper)
- self.assertVirtual(port._path_to_image_diff)
- self.assertVirtual(port._path_to_lighttpd)
- self.assertVirtual(port._path_to_lighttpd_modules)
- self.assertVirtual(port._path_to_lighttpd_php)
- self.assertVirtual(port._path_to_wdiff)
- self.assertVirtual(port._shut_down_http_server, None)
-
- def test_virtual_driver_method(self):
- self.assertRaises(NotImplementedError, base.Driver, base.Port(),
- 0)
-
- def test_virtual_driver_methods(self):
- class VirtualDriver(base.Driver):
- def __init__(self):
- pass
-
- driver = VirtualDriver()
- self.assertVirtual(driver.run_test, None)
- self.assertVirtual(driver.poll)
- self.assertVirtual(driver.stop)
-
-
-class DriverTest(unittest.TestCase):
-
- def _assert_wrapper(self, wrapper_string, expected_wrapper):
- wrapper = base.Driver._command_wrapper(wrapper_string)
- self.assertEqual(wrapper, expected_wrapper)
-
- def test_command_wrapper(self):
- self._assert_wrapper(None, [])
- self._assert_wrapper("valgrind", ["valgrind"])
-
- # Validate that shlex works as expected.
- command_with_spaces = "valgrind --smc-check=\"check with spaces!\" --foo"
- expected_parse = ["valgrind", "--smc-check=check with spaces!", "--foo"]
- self._assert_wrapper(command_with_spaces, expected_parse)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py
deleted file mode 100644
index 8fe685a..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py
+++ /dev/null
@@ -1,555 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Chromium implementations of the Port interface."""
-
-from __future__ import with_statement
-
-import codecs
-import errno
-import logging
-import os
-import re
-import shutil
-import signal
-import subprocess
-import sys
-import tempfile
-import time
-import webbrowser
-
-from webkitpy.common.system.path import cygpath
-from webkitpy.layout_tests.layout_package import test_expectations
-from webkitpy.layout_tests.layout_package import test_output
-
-import base
-import http_server
-
-# Chromium DRT on OSX uses WebKitDriver.
-if sys.platform == 'darwin':
- import webkit
-
-import websocket_server
-
-_log = logging.getLogger("webkitpy.layout_tests.port.chromium")
-
-
-# FIXME: This function doesn't belong in this package.
-def check_file_exists(path_to_file, file_description, override_step=None,
- logging=True):
- """Verify the file is present where expected or log an error.
-
- Args:
- file_name: The (human friendly) name or description of the file
- you're looking for (e.g., "HTTP Server"). Used for error logging.
- override_step: An optional string to be logged if the check fails.
- logging: Whether or not log the error messages."""
- if not os.path.exists(path_to_file):
- if logging:
- _log.error('Unable to find %s' % file_description)
- _log.error(' at %s' % path_to_file)
- if override_step:
- _log.error(' %s' % override_step)
- _log.error('')
- return False
- return True
-
-
-class ChromiumPort(base.Port):
- """Abstract base class for Chromium implementations of the Port class."""
-
- def __init__(self, **kwargs):
- base.Port.__init__(self, **kwargs)
- self._chromium_base_dir = None
-
- def baseline_path(self):
- return self._webkit_baseline_path(self._name)
-
- def check_build(self, needs_http):
- result = True
-
- dump_render_tree_binary_path = self._path_to_driver()
- result = check_file_exists(dump_render_tree_binary_path,
- 'test driver') and result
- if result and self.get_option('build'):
- result = self._check_driver_build_up_to_date(
- self.get_option('configuration'))
- else:
- _log.error('')
-
- helper_path = self._path_to_helper()
- if helper_path:
- result = check_file_exists(helper_path,
- 'layout test helper') and result
-
- if self.get_option('pixel_tests'):
- result = self.check_image_diff(
- 'To override, invoke with --no-pixel-tests') and result
-
- # It's okay if pretty patch isn't available, but we will at
- # least log a message.
- self.check_pretty_patch()
-
- return result
-
- def check_sys_deps(self, needs_http):
- cmd = [self._path_to_driver(), '--check-layout-test-sys-deps']
- if self._executive.run_command(cmd, return_exit_code=True):
- _log.error('System dependencies check failed.')
- _log.error('To override, invoke with --nocheck-sys-deps')
- _log.error('')
- return False
- return True
-
- def check_image_diff(self, override_step=None, logging=True):
- image_diff_path = self._path_to_image_diff()
- return check_file_exists(image_diff_path, 'image diff exe',
- override_step, logging)
-
- def diff_image(self, expected_contents, actual_contents,
- diff_filename=None):
- executable = self._path_to_image_diff()
-
- tempdir = tempfile.mkdtemp()
- expected_filename = os.path.join(tempdir, "expected.png")
- with open(expected_filename, 'w+b') as file:
- file.write(expected_contents)
- actual_filename = os.path.join(tempdir, "actual.png")
- with open(actual_filename, 'w+b') as file:
- file.write(actual_contents)
-
- if diff_filename:
- cmd = [executable, '--diff', expected_filename,
- actual_filename, diff_filename]
- else:
- cmd = [executable, expected_filename, actual_filename]
-
- result = True
- try:
- exit_code = self._executive.run_command(cmd, return_exit_code=True)
- if exit_code == 0:
- # The images are the same.
- result = False
- elif exit_code != 1:
- _log.error("image diff returned an exit code of "
- + str(exit_code))
- # Returning False here causes the script to think that we
- # successfully created the diff even though we didn't. If
- # we return True, we think that the images match but the hashes
- # don't match.
- # FIXME: Figure out why image_diff returns other values.
- result = False
- except OSError, e:
- if e.errno == errno.ENOENT or e.errno == errno.EACCES:
- _compare_available = False
- else:
- raise e
- finally:
- shutil.rmtree(tempdir, ignore_errors=True)
- return result
-
- def driver_name(self):
- if self._options.use_drt:
- return "DumpRenderTree"
- return "test_shell"
-
- def path_from_chromium_base(self, *comps):
- """Returns the full path to path made by joining the top of the
- Chromium source tree and the list of path components in |*comps|."""
- if not self._chromium_base_dir:
- abspath = os.path.abspath(__file__)
- offset = abspath.find('third_party')
- if offset == -1:
- self._chromium_base_dir = os.path.join(
- abspath[0:abspath.find('WebKitTools')],
- 'WebKit', 'chromium')
- else:
- self._chromium_base_dir = abspath[0:offset]
- return os.path.join(self._chromium_base_dir, *comps)
-
- def path_to_test_expectations_file(self):
- return self.path_from_webkit_base('LayoutTests', 'platform',
- 'chromium', 'test_expectations.txt')
-
- def results_directory(self):
- try:
- return self.path_from_chromium_base('webkit',
- self.get_option('configuration'),
- self.get_option('results_directory'))
- except AssertionError:
- return self._build_path(self.get_option('configuration'),
- self.get_option('results_directory'))
-
- def setup_test_run(self):
- # Delete the disk cache if any to ensure a clean test run.
- dump_render_tree_binary_path = self._path_to_driver()
- cachedir = os.path.split(dump_render_tree_binary_path)[0]
- cachedir = os.path.join(cachedir, "cache")
- if os.path.exists(cachedir):
- shutil.rmtree(cachedir)
-
- def create_driver(self, worker_number):
- """Starts a new Driver and returns a handle to it."""
- if self.get_option('use_drt') and sys.platform == 'darwin':
- return webkit.WebKitDriver(self, worker_number)
- return ChromiumDriver(self, worker_number)
-
- def start_helper(self):
- helper_path = self._path_to_helper()
- if helper_path:
- _log.debug("Starting layout helper %s" % helper_path)
- # Note: Not thread safe: http://bugs.python.org/issue2320
- self._helper = subprocess.Popen([helper_path],
- stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None)
- is_ready = self._helper.stdout.readline()
- if not is_ready.startswith('ready'):
- _log.error("layout_test_helper failed to be ready")
-
- def stop_helper(self):
- if self._helper:
- _log.debug("Stopping layout test helper")
- self._helper.stdin.write("x\n")
- self._helper.stdin.close()
- # wait() is not threadsafe and can throw OSError due to:
- # http://bugs.python.org/issue1731717
- self._helper.wait()
-
- def test_base_platform_names(self):
- return ('linux', 'mac', 'win')
-
- def test_expectations(self):
- """Returns the test expectations for this port.
-
- Basically this string should contain the equivalent of a
- test_expectations file. See test_expectations.py for more details."""
- expectations_path = self.path_to_test_expectations_file()
- with codecs.open(expectations_path, "r", "utf-8") as file:
- return file.read()
-
- def test_expectations_overrides(self):
- # FIXME: This drt_overrides handling should be removed when we switch
- # from tes_shell to DRT.
- drt_overrides = ''
- if self.get_option('use_drt'):
- drt_overrides_path = self.path_from_webkit_base('LayoutTests',
- 'platform', 'chromium', 'drt_expectations.txt')
- if os.path.exists(drt_overrides_path):
- with codecs.open(drt_overrides_path, "r", "utf-8") as file:
- drt_overrides = file.read()
-
- try:
- overrides_path = self.path_from_chromium_base('webkit', 'tools',
- 'layout_tests', 'test_expectations.txt')
- except AssertionError:
- return None
- if not os.path.exists(overrides_path):
- return None
- with codecs.open(overrides_path, "r", "utf-8") as file:
- return file.read() + drt_overrides
-
- def skipped_layout_tests(self, extra_test_files=None):
- expectations_str = self.test_expectations()
- overrides_str = self.test_expectations_overrides()
- test_platform_name = self.test_platform_name()
- is_debug_mode = False
-
- all_test_files = self.tests([])
- if extra_test_files:
- all_test_files.update(extra_test_files)
-
- expectations = test_expectations.TestExpectations(
- self, all_test_files, expectations_str, test_platform_name,
- is_debug_mode, is_lint_mode=True, overrides=overrides_str)
- tests_dir = self.layout_tests_dir()
- return [self.relative_test_filename(test)
- for test in expectations.get_tests_with_result_type(test_expectations.SKIP)]
-
- def test_platform_names(self):
- return self.test_base_platform_names() + ('win-xp',
- 'win-vista', 'win-7')
-
- def test_platform_name_to_name(self, test_platform_name):
- if test_platform_name in self.test_platform_names():
- return 'chromium-' + test_platform_name
- raise ValueError('Unsupported test_platform_name: %s' %
- test_platform_name)
-
- def test_repository_paths(self):
- # Note: for JSON file's backward-compatibility we use 'chrome' rather
- # than 'chromium' here.
- repos = super(ChromiumPort, self).test_repository_paths()
- repos.append(('chrome', self.path_from_chromium_base()))
- return repos
-
- #
- # PROTECTED METHODS
- #
- # These routines should only be called by other methods in this file
- # or any subclasses.
- #
-
- def _check_driver_build_up_to_date(self, configuration):
- if configuration in ('Debug', 'Release'):
- try:
- debug_path = self._path_to_driver('Debug')
- release_path = self._path_to_driver('Release')
-
- debug_mtime = os.stat(debug_path).st_mtime
- release_mtime = os.stat(release_path).st_mtime
-
- if (debug_mtime > release_mtime and configuration == 'Release' or
- release_mtime > debug_mtime and configuration == 'Debug'):
- _log.warning('You are not running the most '
- 'recent DumpRenderTree binary. You need to '
- 'pass --debug or not to select between '
- 'Debug and Release.')
- _log.warning('')
- # This will fail if we don't have both a debug and release binary.
- # That's fine because, in this case, we must already be running the
- # most up-to-date one.
- except OSError:
- pass
- return True
-
- def _chromium_baseline_path(self, platform):
- if platform is None:
- platform = self.name()
- return self.path_from_webkit_base('LayoutTests', 'platform', platform)
-
- def _convert_path(self, path):
- """Handles filename conversion for subprocess command line args."""
- # See note above in diff_image() for why we need this.
- if sys.platform == 'cygwin':
- return cygpath(path)
- return path
-
- def _path_to_image_diff(self):
- binary_name = 'image_diff'
- if self.get_option('use_drt'):
- binary_name = 'ImageDiff'
- return self._build_path(self.get_option('configuration'), binary_name)
-
-
-class ChromiumDriver(base.Driver):
- """Abstract interface for test_shell."""
-
- def __init__(self, port, worker_number):
- self._port = port
- self._worker_number = worker_number
- self._image_path = None
- if self._port.get_option('pixel_tests'):
- self._image_path = os.path.join(
- self._port.get_option('results_directory'),
- 'png_result%s.png' % self._worker_number)
-
- def cmd_line(self):
- cmd = self._command_wrapper(self._port.get_option('wrapper'))
- cmd.append(self._port._path_to_driver())
- if self._port.get_option('pixel_tests'):
- # See note above in diff_image() for why we need _convert_path().
- cmd.append("--pixel-tests=" +
- self._port._convert_path(self._image_path))
-
- if self._port.get_option('use_drt'):
- cmd.append('--test-shell')
- else:
- cmd.append('--layout-tests')
-
- if self._port.get_option('startup_dialog'):
- cmd.append('--testshell-startup-dialog')
-
- if self._port.get_option('gp_fault_error_box'):
- cmd.append('--gp-fault-error-box')
-
- if self._port.get_option('js_flags') is not None:
- cmd.append('--js-flags="' + self._port.get_option('js_flags') + '"')
-
- if self._port.get_option('multiple_loads') > 0:
- cmd.append('--multiple-loads=' + str(self._port.get_option('multiple_loads')))
-
- if self._port.get_option('accelerated_compositing'):
- cmd.append('--enable-accelerated-compositing')
-
- if self._port.get_option('accelerated_2d_canvas'):
- cmd.append('--enable-accelerated-2d-canvas')
- return cmd
-
- def start(self):
- # FIXME: Should be an error to call this method twice.
- cmd = self.cmd_line()
-
- # We need to pass close_fds=True to work around Python bug #2320
- # (otherwise we can hang when we kill DumpRenderTree when we are running
- # multiple threads). See http://bugs.python.org/issue2320 .
- # Note that close_fds isn't supported on Windows, but this bug only
- # shows up on Mac and Linux.
- close_flag = sys.platform not in ('win32', 'cygwin')
- self._proc = subprocess.Popen(cmd, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- close_fds=close_flag)
-
- def poll(self):
- # poll() is not threadsafe and can throw OSError due to:
- # http://bugs.python.org/issue1731717
- return self._proc.poll()
-
- def _write_command_and_read_line(self, input=None):
- """Returns a tuple: (line, did_crash)"""
- try:
- if input:
- if isinstance(input, unicode):
- # TestShell expects utf-8
- input = input.encode("utf-8")
- self._proc.stdin.write(input)
- # DumpRenderTree text output is always UTF-8. However some tests
- # (e.g. webarchive) may spit out binary data instead of text so we
- # don't bother to decode the output (for either DRT or test_shell).
- line = self._proc.stdout.readline()
- # We could assert() here that line correctly decodes as UTF-8.
- return (line, False)
- except IOError, e:
- _log.error("IOError communicating w/ test_shell: " + str(e))
- return (None, True)
-
- def _test_shell_command(self, uri, timeoutms, checksum):
- cmd = uri
- if timeoutms:
- cmd += ' ' + str(timeoutms)
- if checksum:
- cmd += ' ' + checksum
- cmd += "\n"
- return cmd
-
- def _output_image(self):
- """Returns the image output which driver generated."""
- png_path = self._image_path
- if png_path and os.path.isfile(png_path):
- with open(png_path, 'rb') as image_file:
- return image_file.read()
- else:
- return None
-
- def _output_image_with_retry(self):
- # Retry a few more times because open() sometimes fails on Windows,
- # raising "IOError: [Errno 13] Permission denied:"
- retry_num = 50
- timeout_seconds = 5.0
- for i in range(retry_num):
- try:
- return self._output_image()
- except IOError, e:
- if e.errno == errno.EACCES:
- time.sleep(timeout_seconds / retry_num)
- else:
- raise e
- return self._output_image()
-
- def run_test(self, test_input):
- output = []
- error = []
- crash = False
- timeout = False
- actual_uri = None
- actual_checksum = None
-
- start_time = time.time()
-
- uri = self._port.filename_to_uri(test_input.filename)
- cmd = self._test_shell_command(uri, test_input.timeout,
- test_input.image_hash)
- (line, crash) = self._write_command_and_read_line(input=cmd)
-
- while not crash and line.rstrip() != "#EOF":
- # Make sure we haven't crashed.
- if line == '' and self.poll() is not None:
- # This is hex code 0xc000001d, which is used for abrupt
- # termination. This happens if we hit ctrl+c from the prompt
- # and we happen to be waiting on test_shell.
- # sdoyon: Not sure for which OS and in what circumstances the
- # above code is valid. What works for me under Linux to detect
- # ctrl+c is for the subprocess returncode to be negative
- # SIGINT. And that agrees with the subprocess documentation.
- if (-1073741510 == self._proc.returncode or
- - signal.SIGINT == self._proc.returncode):
- raise KeyboardInterrupt
- crash = True
- break
-
- # Don't include #URL lines in our output
- if line.startswith("#URL:"):
- actual_uri = line.rstrip()[5:]
- if uri != actual_uri:
- # GURL capitalizes the drive letter of a file URL.
- if (not re.search("^file:///[a-z]:", uri) or
- uri.lower() != actual_uri.lower()):
- _log.fatal("Test got out of sync:\n|%s|\n|%s|" %
- (uri, actual_uri))
- raise AssertionError("test out of sync")
- elif line.startswith("#MD5:"):
- actual_checksum = line.rstrip()[5:]
- elif line.startswith("#TEST_TIMED_OUT"):
- timeout = True
- # Test timed out, but we still need to read until #EOF.
- elif actual_uri:
- output.append(line)
- else:
- error.append(line)
-
- (line, crash) = self._write_command_and_read_line(input=None)
-
- run_time = time.time() - start_time
- return test_output.TestOutput(
- ''.join(output), self._output_image_with_retry(), actual_checksum,
- crash, run_time, timeout, ''.join(error))
-
- def stop(self):
- if self._proc:
- self._proc.stdin.close()
- self._proc.stdout.close()
- if self._proc.stderr:
- self._proc.stderr.close()
- if sys.platform not in ('win32', 'cygwin'):
- # Closing stdin/stdout/stderr hangs sometimes on OS X,
- # (see __init__(), above), and anyway we don't want to hang
- # the harness if test_shell is buggy, so we wait a couple
- # seconds to give test_shell a chance to clean up, but then
- # force-kill the process if necessary.
- KILL_TIMEOUT = 3.0
- timeout = time.time() + KILL_TIMEOUT
- # poll() is not threadsafe and can throw OSError due to:
- # http://bugs.python.org/issue1731717
- while self._proc.poll() is None and time.time() < timeout:
- time.sleep(0.1)
- # poll() is not threadsafe and can throw OSError due to:
- # http://bugs.python.org/issue1731717
- if self._proc.poll() is None:
- _log.warning('stopping test driver timed out, '
- 'killing it')
- self._port._executive.kill_process(self._proc.pid)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu.py
deleted file mode 100644
index 54a0fee..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu.py
+++ /dev/null
@@ -1,154 +0,0 @@
-#!/usr/bin/env 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.
-
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import with_statement
-
-import codecs
-import os
-import sys
-
-import chromium_linux
-import chromium_mac
-import chromium_win
-
-
-def get(**kwargs):
- """Some tests have slightly different results when run while using
- hardware acceleration. In those cases, we prepend an additional directory
- to the baseline paths."""
- port_name = kwargs.get('port_name', None)
- if port_name == 'chromium-gpu':
- if sys.platform in ('cygwin', 'win32'):
- port_name = 'chromium-gpu-win'
- elif sys.platform == 'linux2':
- port_name = 'chromium-gpu-linux'
- elif sys.platform == 'darwin':
- port_name = 'chromium-gpu-mac'
- else:
- raise NotImplementedError('unsupported platform: %s' %
- sys.platform)
-
- if port_name == 'chromium-gpu-linux':
- return ChromiumGpuLinuxPort(**kwargs)
-
- if port_name.startswith('chromium-gpu-mac'):
- return ChromiumGpuMacPort(**kwargs)
-
- if port_name.startswith('chromium-gpu-win'):
- return ChromiumGpuWinPort(**kwargs)
-
- raise NotImplementedError('unsupported port: %s' % port_name)
-
-
-def _set_gpu_options(options):
- if options:
- if options.accelerated_compositing is None:
- options.accelerated_compositing = True
- if options.accelerated_2d_canvas is None:
- options.accelerated_2d_canvas = True
- if options.use_drt is None:
- options.use_drt = True
-
- # FIXME: Remove this after http://codereview.chromium.org/5133001/ is enabled
- # on the bots.
- if options.builder_name is not None and not ' - GPU' in options.builder_name:
- options.builder_name = options.builder_name + ' - GPU'
-
-
-def _gpu_overrides(port):
- try:
- overrides_path = port.path_from_chromium_base('webkit', 'tools',
- 'layout_tests', 'test_expectations_gpu.txt')
- except AssertionError:
- return None
- if not os.path.exists(overrides_path):
- return None
- with codecs.open(overrides_path, "r", "utf-8") as file:
- return file.read()
-
-
-class ChromiumGpuLinuxPort(chromium_linux.ChromiumLinuxPort):
- def __init__(self, **kwargs):
- kwargs.setdefault('port_name', 'chromium-gpu-linux')
- _set_gpu_options(kwargs.get('options'))
- chromium_linux.ChromiumLinuxPort.__init__(self, **kwargs)
-
- def baseline_search_path(self):
- # Mimic the Linux -> Win expectations fallback in the ordinary Chromium port.
- return (map(self._webkit_baseline_path, ['chromium-gpu-linux', 'chromium-gpu-win', 'chromium-gpu']) +
- chromium_linux.ChromiumLinuxPort.baseline_search_path(self))
-
- def default_child_processes(self):
- return 1
-
- def path_to_test_expectations_file(self):
- return self.path_from_webkit_base('LayoutTests', 'platform',
- 'chromium-gpu', 'test_expectations.txt')
-
- def test_expectations_overrides(self):
- return _gpu_overrides(self)
-
-
-class ChromiumGpuMacPort(chromium_mac.ChromiumMacPort):
- def __init__(self, **kwargs):
- kwargs.setdefault('port_name', 'chromium-gpu-mac')
- _set_gpu_options(kwargs.get('options'))
- chromium_mac.ChromiumMacPort.__init__(self, **kwargs)
-
- def baseline_search_path(self):
- return (map(self._webkit_baseline_path, ['chromium-gpu-mac', 'chromium-gpu']) +
- chromium_mac.ChromiumMacPort.baseline_search_path(self))
-
- def default_child_processes(self):
- return 1
-
- def path_to_test_expectations_file(self):
- return self.path_from_webkit_base('LayoutTests', 'platform',
- 'chromium-gpu', 'test_expectations.txt')
-
- def test_expectations_overrides(self):
- return _gpu_overrides(self)
-
-
-class ChromiumGpuWinPort(chromium_win.ChromiumWinPort):
- def __init__(self, **kwargs):
- kwargs.setdefault('port_name', 'chromium-gpu-win' + self.version())
- _set_gpu_options(kwargs.get('options'))
- chromium_win.ChromiumWinPort.__init__(self, **kwargs)
-
- def baseline_search_path(self):
- return (map(self._webkit_baseline_path, ['chromium-gpu-win', 'chromium-gpu']) +
- chromium_win.ChromiumWinPort.baseline_search_path(self))
-
- def default_child_processes(self):
- return 1
-
- def path_to_test_expectations_file(self):
- return self.path_from_webkit_base('LayoutTests', 'platform',
- 'chromium-gpu', 'test_expectations.txt')
-
- def test_expectations_overrides(self):
- return _gpu_overrides(self)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu_unittest.py
deleted file mode 100644
index 03bc98a..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu_unittest.py
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/env 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.
-
-# 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 os
-import unittest
-
-from webkitpy.tool import mocktool
-import chromium_gpu
-
-
-class ChromiumGpuTest(unittest.TestCase):
- def test_get_chromium_gpu_linux(self):
- self.assertOverridesWorked('chromium-gpu-linux')
-
- def test_get_chromium_gpu_mac(self):
- self.assertOverridesWorked('chromium-gpu-mac')
-
- def test_get_chromium_gpu_win(self):
- self.assertOverridesWorked('chromium-gpu-win')
-
- def assertOverridesWorked(self, port_name):
- # test that we got the right port
- mock_options = mocktool.MockOptions(accelerated_compositing=None,
- accelerated_2d_canvas=None,
- builder_name='foo',
- use_drt=None,
- child_processes=None)
- port = chromium_gpu.get(port_name=port_name, options=mock_options)
- self.assertTrue(port._options.accelerated_compositing)
- self.assertTrue(port._options.accelerated_2d_canvas)
- self.assertTrue(port._options.use_drt)
- self.assertEqual(port.default_child_processes(), 1)
- self.assertEqual(port._options.builder_name, 'foo - GPU')
-
- # we use startswith() instead of Equal to gloss over platform versions.
- self.assertTrue(port.name().startswith(port_name))
-
- # test that it has the right directories in front of the search path.
- paths = port.baseline_search_path()
- self.assertEqual(port._webkit_baseline_path(port_name), paths[0])
- if port_name == 'chromium-gpu-linux':
- self.assertEqual(port._webkit_baseline_path('chromium-gpu-win'), paths[1])
- self.assertEqual(port._webkit_baseline_path('chromium-gpu'), paths[2])
- else:
- self.assertEqual(port._webkit_baseline_path('chromium-gpu'), paths[1])
-
- # Test that we have the right expectations file.
- self.assertTrue('chromium-gpu' in
- port.path_to_test_expectations_file())
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py
deleted file mode 100644
index b26a6b5..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py
+++ /dev/null
@@ -1,190 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Chromium Linux implementation of the Port interface."""
-
-import logging
-import os
-import signal
-
-import chromium
-
-_log = logging.getLogger("webkitpy.layout_tests.port.chromium_linux")
-
-
-class ChromiumLinuxPort(chromium.ChromiumPort):
- """Chromium Linux implementation of the Port class."""
-
- def __init__(self, **kwargs):
- kwargs.setdefault('port_name', 'chromium-linux')
- chromium.ChromiumPort.__init__(self, **kwargs)
-
- def baseline_search_path(self):
- 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)
- if needs_http:
- if self.get_option('use_apache'):
- result = self._check_apache_install() and result
- else:
- result = self._check_lighttpd_install() and result
- result = self._check_wdiff_install() and result
-
- if not result:
- _log.error('For complete Linux build requirements, please see:')
- _log.error('')
- _log.error(' http://code.google.com/p/chromium/wiki/'
- 'LinuxBuildInstructions')
- return result
-
- def test_platform_name(self):
- # We use 'linux' instead of 'chromium-linux' in test_expectations.txt.
- return 'linux'
-
- def version(self):
- # We don't have different versions on linux.
- return ''
-
- #
- # PROTECTED METHODS
- #
-
- def _build_path(self, *comps):
- base = self.path_from_chromium_base()
- if os.path.exists(os.path.join(base, 'sconsbuild')):
- return os.path.join(base, 'sconsbuild', *comps)
- if os.path.exists(os.path.join(base, 'out', *comps)) or not self.get_option('use_drt'):
- return os.path.join(base, 'out', *comps)
- base = self.path_from_webkit_base()
- if os.path.exists(os.path.join(base, 'sconsbuild')):
- return os.path.join(base, 'sconsbuild', *comps)
- return os.path.join(base, 'out', *comps)
-
- def _check_apache_install(self):
- result = chromium.check_file_exists(self._path_to_apache(),
- "apache2")
- result = chromium.check_file_exists(self._path_to_apache_config_file(),
- "apache2 config file") and result
- if not result:
- _log.error(' Please install using: "sudo apt-get install '
- 'apache2 libapache2-mod-php5"')
- _log.error('')
- return result
-
- def _check_lighttpd_install(self):
- result = chromium.check_file_exists(
- self._path_to_lighttpd(), "LigHTTPd executable")
- result = chromium.check_file_exists(self._path_to_lighttpd_php(),
- "PHP CGI executable") and result
- result = chromium.check_file_exists(self._path_to_lighttpd_modules(),
- "LigHTTPd modules") and result
- if not result:
- _log.error(' Please install using: "sudo apt-get install '
- 'lighttpd php5-cgi"')
- _log.error('')
- return result
-
- def _check_wdiff_install(self):
- result = chromium.check_file_exists(self._path_to_wdiff(), 'wdiff')
- if not result:
- _log.error(' Please install using: "sudo apt-get install '
- 'wdiff"')
- _log.error('')
- # FIXME: The ChromiumMac port always returns True.
- return result
-
- def _path_to_apache(self):
- if self._is_redhat_based():
- return '/usr/sbin/httpd'
- else:
- return '/usr/sbin/apache2'
-
- def _path_to_apache_config_file(self):
- if self._is_redhat_based():
- config_name = 'fedora-httpd.conf'
- else:
- config_name = 'apache2-debian-httpd.conf'
-
- return os.path.join(self.layout_tests_dir(), 'http', 'conf',
- config_name)
-
- def _path_to_lighttpd(self):
- return "/usr/sbin/lighttpd"
-
- def _path_to_lighttpd_modules(self):
- return "/usr/lib/lighttpd"
-
- def _path_to_lighttpd_php(self):
- return "/usr/bin/php-cgi"
-
- def _path_to_driver(self, configuration=None):
- if not configuration:
- configuration = self.get_option('configuration')
- binary_name = 'test_shell'
- if self.get_option('use_drt'):
- binary_name = 'DumpRenderTree'
- return self._build_path(configuration, binary_name)
-
- def _path_to_helper(self):
- return None
-
- def _path_to_wdiff(self):
- if self._is_redhat_based():
- return '/usr/bin/dwdiff'
- else:
- return '/usr/bin/wdiff'
-
- def _is_redhat_based(self):
- return os.path.exists(os.path.join('/etc', 'redhat-release'))
-
- def _shut_down_http_server(self, server_pid):
- """Shut down the lighttpd web server. Blocks until it's fully
- shut down.
-
- Args:
- server_pid: The process ID of the running server.
- """
- # server_pid is not set when "http_server.py stop" is run manually.
- if server_pid is None:
- # TODO(mmoss) This isn't ideal, since it could conflict with
- # lighttpd processes not started by http_server.py,
- # but good enough for now.
- self._executive.kill_all("lighttpd")
- self._executive.kill_all("apache2")
- else:
- try:
- os.kill(server_pid, signal.SIGTERM)
- # TODO(mmoss) Maybe throw in a SIGKILL just to be sure?
- except OSError:
- # Sometimes we get a bad PID (e.g. from a stale httpd.pid
- # file), so if kill fails on the given PID, just try to
- # 'killall' web servers.
- self._shut_down_http_server(None)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py
deleted file mode 100644
index d1c383c..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py
+++ /dev/null
@@ -1,176 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Chromium Mac implementation of the Port interface."""
-
-import logging
-import os
-import platform
-import signal
-
-import chromium
-
-from webkitpy.common.system.executive import Executive
-
-_log = logging.getLogger("webkitpy.layout_tests.port.chromium_mac")
-
-
-class ChromiumMacPort(chromium.ChromiumPort):
- """Chromium Mac implementation of the Port class."""
-
- def __init__(self, **kwargs):
- kwargs.setdefault('port_name', 'chromium-mac')
- chromium.ChromiumPort.__init__(self, **kwargs)
-
- def baseline_search_path(self):
- 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)
- result = self._check_wdiff_install() and result
- if not result:
- _log.error('For complete Mac build requirements, please see:')
- _log.error('')
- _log.error(' http://code.google.com/p/chromium/wiki/'
- 'MacBuildInstructions')
- return result
-
- def default_child_processes(self):
- # FIXME: we need to run single-threaded for now. See
- # https://bugs.webkit.org/show_bug.cgi?id=38553. Unfortunately this
- # routine is called right before the logger is configured, so if we
- # try to _log.warning(), it gets thrown away.
- import sys
- sys.stderr.write("Defaulting to one child - see https://bugs.webkit.org/show_bug.cgi?id=38553\n")
- return 1
-
- def driver_name(self):
- """name for this port's equivalent of DumpRenderTree."""
- if self.get_option('use_drt'):
- return "DumpRenderTree"
- return "TestShell"
-
- def test_platform_name(self):
- # We use 'mac' instead of 'chromium-mac'
- 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'
- release_version = int(os_version_string.split('.')[1])
- # we don't support 'tiger' or earlier releases
- if release_version == 5:
- return '-leopard'
- elif release_version == 6:
- return '-snowleopard'
- return ''
-
- #
- # PROTECTED METHODS
- #
-
- def _build_path(self, *comps):
- path = self.path_from_chromium_base('xcodebuild', *comps)
- if os.path.exists(path) or not self.get_option('use_drt'):
- return path
- return self.path_from_webkit_base('WebKit', 'chromium', 'xcodebuild',
- *comps)
-
- def _check_wdiff_install(self):
- try:
- # We're ignoring the return and always returning True
- self._executive.run_command([self._path_to_wdiff()], error_handler=Executive.ignore_error)
- except OSError:
- _log.warning('wdiff not found. Install using MacPorts or some '
- 'other means')
- return True
-
- def _lighttpd_path(self, *comps):
- return self.path_from_chromium_base('third_party', 'lighttpd',
- 'mac', *comps)
-
- def _path_to_apache(self):
- return '/usr/sbin/httpd'
-
- def _path_to_apache_config_file(self):
- return os.path.join(self.layout_tests_dir(), 'http', 'conf',
- 'apache2-httpd.conf')
-
- def _path_to_lighttpd(self):
- return self._lighttpd_path('bin', 'lighttpd')
-
- def _path_to_lighttpd_modules(self):
- return self._lighttpd_path('lib')
-
- def _path_to_lighttpd_php(self):
- return self._lighttpd_path('bin', 'php-cgi')
-
- def _path_to_driver(self, configuration=None):
- # FIXME: make |configuration| happy with case-sensitive file
- # systems.
- if not configuration:
- configuration = self.get_option('configuration')
- return self._build_path(configuration, self.driver_name() + '.app',
- 'Contents', 'MacOS', self.driver_name())
-
- def _path_to_helper(self):
- binary_name = 'layout_test_helper'
- if self.get_option('use_drt'):
- binary_name = 'LayoutTestHelper'
- return self._build_path(self.get_option('configuration'), binary_name)
-
- def _path_to_wdiff(self):
- return 'wdiff'
-
- def _shut_down_http_server(self, server_pid):
- """Shut down the lighttpd web server. Blocks until it's fully
- shut down.
-
- Args:
- server_pid: The process ID of the running server.
- """
- # server_pid is not set when "http_server.py stop" is run manually.
- if server_pid is None:
- # TODO(mmoss) This isn't ideal, since it could conflict with
- # lighttpd processes not started by http_server.py,
- # but good enough for now.
- self._executive.kill_all('lighttpd')
- self._executive.kill_all('httpd')
- else:
- try:
- os.kill(server_pid, signal.SIGTERM)
- # TODO(mmoss) Maybe throw in a SIGKILL just to be sure?
- except OSError:
- # Sometimes we get a bad PID (e.g. from a stale httpd.pid
- # file), so if kill fails on the given PID, just try to
- # 'killall' web servers.
- self._shut_down_http_server(None)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac_unittest.py
deleted file mode 100644
index d63faa0..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac_unittest.py
+++ /dev/null
@@ -1,40 +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.
-
-import chromium_mac
-import unittest
-
-from webkitpy.thirdparty.mock import Mock
-
-
-class ChromiumMacPortTest(unittest.TestCase):
-
- def test_check_wdiff_install(self):
- port = chromium_mac.ChromiumMacPort()
- # Currently is always true, just logs if missing.
- self.assertTrue(port._check_wdiff_install())
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py
deleted file mode 100644
index 5396522..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py
+++ /dev/null
@@ -1,186 +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.
-
-import os
-import unittest
-import StringIO
-
-from webkitpy.tool import mocktool
-from webkitpy.thirdparty.mock import Mock
-
-import chromium
-import chromium_linux
-import chromium_mac
-import chromium_win
-
-class ChromiumDriverTest(unittest.TestCase):
-
- def setUp(self):
- mock_port = Mock()
- mock_port.get_option = lambda option_name: ''
- self.driver = chromium.ChromiumDriver(mock_port, worker_number=0)
-
- def test_test_shell_command(self):
- expected_command = "test.html 2 checksum\n"
- self.assertEqual(self.driver._test_shell_command("test.html", 2, "checksum"), expected_command)
-
- def _assert_write_command_and_read_line(self, input=None, expected_line=None, expected_stdin=None, expected_crash=False):
- if not expected_stdin:
- if input:
- expected_stdin = input
- else:
- # We reset stdin, so we should expect stdin.getValue = ""
- expected_stdin = ""
- self.driver._proc.stdin = StringIO.StringIO()
- line, did_crash = self.driver._write_command_and_read_line(input)
- self.assertEqual(self.driver._proc.stdin.getvalue(), expected_stdin)
- self.assertEqual(line, expected_line)
- self.assertEqual(did_crash, expected_crash)
-
- def test_write_command_and_read_line(self):
- self.driver._proc = Mock()
- # Set up to read 3 lines before we get an IOError
- self.driver._proc.stdout = StringIO.StringIO("first\nsecond\nthird\n")
-
- unicode_input = u"I \u2661 Unicode"
- utf8_input = unicode_input.encode("utf-8")
- # Test unicode input conversion to utf-8
- self._assert_write_command_and_read_line(input=unicode_input, expected_stdin=utf8_input, expected_line="first\n")
- # Test str() input.
- self._assert_write_command_and_read_line(input="foo", expected_line="second\n")
- # Test input=None
- self._assert_write_command_and_read_line(expected_line="third\n")
- # Test reading from a closed/empty stream.
- # reading from a StringIO does not raise IOError like a real file would, so raise IOError manually.
- def mock_readline():
- raise IOError
- self.driver._proc.stdout.readline = mock_readline
- self._assert_write_command_and_read_line(expected_crash=True)
-
-
-class ChromiumPortTest(unittest.TestCase):
- class TestMacPort(chromium_mac.ChromiumMacPort):
- def __init__(self, options):
- chromium_mac.ChromiumMacPort.__init__(self,
- port_name='test-port',
- options=options)
-
- def default_configuration(self):
- self.default_configuration_called = True
- return 'default'
-
- class TestLinuxPort(chromium_linux.ChromiumLinuxPort):
- def __init__(self, options):
- chromium_linux.ChromiumLinuxPort.__init__(self,
- port_name='test-port',
- options=options)
-
- def default_configuration(self):
- self.default_configuration_called = True
- return 'default'
-
- def test_path_to_image_diff(self):
- mock_options = mocktool.MockOptions(use_drt=True)
- port = ChromiumPortTest.TestLinuxPort(options=mock_options)
- self.assertTrue(port._path_to_image_diff().endswith(
- '/out/default/ImageDiff'), msg=port._path_to_image_diff())
- port = ChromiumPortTest.TestMacPort(options=mock_options)
- self.assertTrue(port._path_to_image_diff().endswith(
- '/xcodebuild/default/ImageDiff'))
- # FIXME: Figure out how this is going to work on Windows.
- #port = chromium_win.ChromiumWinPort('test-port', options=MockOptions())
-
- def test_skipped_layout_tests(self):
- mock_options = mocktool.MockOptions(use_drt=True)
- port = ChromiumPortTest.TestLinuxPort(options=mock_options)
-
- fake_test = os.path.join(port.layout_tests_dir(), "fast/js/not-good.js")
-
- port.test_expectations = lambda: """BUG_TEST SKIP : fast/js/not-good.js = TEXT
-LINUX WIN : fast/js/very-good.js = TIMEOUT PASS"""
- port.test_expectations_overrides = lambda: ''
- port.tests = lambda paths: set()
- port.path_exists = lambda test: True
-
- skipped_tests = port.skipped_layout_tests(extra_test_files=[fake_test, ])
- self.assertTrue("fast/js/not-good.js" in skipped_tests)
-
- def test_default_configuration(self):
- mock_options = mocktool.MockOptions()
- port = ChromiumPortTest.TestLinuxPort(options=mock_options)
- self.assertEquals(mock_options.configuration, 'default')
- self.assertTrue(port.default_configuration_called)
-
- mock_options = mocktool.MockOptions(configuration=None)
- port = ChromiumPortTest.TestLinuxPort(mock_options)
- self.assertEquals(mock_options.configuration, 'default')
- self.assertTrue(port.default_configuration_called)
-
- def test_diff_image(self):
- class TestPort(ChromiumPortTest.TestLinuxPort):
- def _path_to_image_diff(self):
- return "/path/to/image_diff"
-
- class MockExecute:
- def __init__(self, result):
- self._result = result
-
- def run_command(self,
- args,
- cwd=None,
- input=None,
- error_handler=None,
- return_exit_code=False,
- return_stderr=True,
- decode_output=False):
- if return_exit_code:
- return self._result
- return ''
-
- mock_options = mocktool.MockOptions(use_drt=False)
- port = ChromiumPortTest.TestLinuxPort(mock_options)
-
- # Images are different.
- port._executive = MockExecute(0)
- self.assertEquals(False, port.diff_image("EXPECTED", "ACTUAL"))
-
- # Images are the same.
- port._executive = MockExecute(1)
- self.assertEquals(True, port.diff_image("EXPECTED", "ACTUAL"))
-
- # There was some error running image_diff.
- port._executive = MockExecute(2)
- exception_raised = False
- try:
- port.diff_image("EXPECTED", "ACTUAL")
- except ValueError, e:
- exception_raised = True
- self.assertFalse(exception_raised)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py
deleted file mode 100644
index 69b529a..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py
+++ /dev/null
@@ -1,174 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Chromium Win implementation of the Port interface."""
-
-import logging
-import os
-import sys
-
-import chromium
-
-_log = logging.getLogger("webkitpy.layout_tests.port.chromium_win")
-
-
-class ChromiumWinPort(chromium.ChromiumPort):
- """Chromium Win implementation of the Port class."""
-
- def __init__(self, **kwargs):
- kwargs.setdefault('port_name', 'chromium-win' + self.version())
- chromium.ChromiumPort.__init__(self, **kwargs)
-
- 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.get_option('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):
- port_names = []
- if self._name.endswith('-win-xp'):
- port_names.append("chromium-win-xp")
- if self._name.endswith('-win-xp') or self._name.endswith('-win-vista'):
- 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)
- if not result:
- _log.error('For complete Windows build requirements, please '
- 'see:')
- _log.error('')
- _log.error(' http://dev.chromium.org/developers/how-tos/'
- 'build-instructions-windows')
- return result
-
- def relative_test_filename(self, filename):
- path = filename[len(self.layout_tests_dir()) + 1:]
- return path.replace('\\', '/')
-
- def test_platform_name(self):
- # We return 'win-xp', not 'chromium-win-xp' here, for convenience.
- return 'win' + self.version()
-
- def version(self):
- if not hasattr(sys, 'getwindowsversion'):
- return ''
- winver = sys.getwindowsversion()
- if winver[0] == 6 and (winver[1] == 1):
- return '-7'
- if winver[0] == 6 and (winver[1] == 0):
- return '-vista'
- if winver[0] == 5 and (winver[1] == 1 or winver[1] == 2):
- return '-xp'
- return ''
-
- #
- # PROTECTED ROUTINES
- #
-
- def _build_path(self, *comps):
- p = self.path_from_chromium_base('webkit', *comps)
- if os.path.exists(p):
- return p
- p = self.path_from_chromium_base('chrome', *comps)
- if os.path.exists(p) or not self.get_option('use_drt'):
- return p
- return os.path.join(self.path_from_webkit_base(), 'WebKit', 'chromium',
- *comps)
-
- def _lighttpd_path(self, *comps):
- return self.path_from_chromium_base('third_party', 'lighttpd', 'win',
- *comps)
-
- def _path_to_apache(self):
- return self.path_from_chromium_base('third_party', 'cygwin', 'usr',
- 'sbin', 'httpd')
-
- def _path_to_apache_config_file(self):
- return os.path.join(self.layout_tests_dir(), 'http', 'conf',
- 'cygwin-httpd.conf')
-
- def _path_to_lighttpd(self):
- return self._lighttpd_path('LightTPD.exe')
-
- def _path_to_lighttpd_modules(self):
- return self._lighttpd_path('lib')
-
- def _path_to_lighttpd_php(self):
- return self._lighttpd_path('php5', 'php-cgi.exe')
-
- def _path_to_driver(self, configuration=None):
- if not configuration:
- configuration = self.get_option('configuration')
- binary_name = 'test_shell.exe'
- if self.get_option('use_drt'):
- binary_name = 'DumpRenderTree.exe'
- return self._build_path(configuration, binary_name)
-
- def _path_to_helper(self):
- binary_name = 'layout_test_helper.exe'
- if self.get_option('use_drt'):
- binary_name = 'LayoutTestHelper.exe'
- return self._build_path(self.get_option('configuration'), binary_name)
-
- def _path_to_image_diff(self):
- binary_name = 'image_diff.exe'
- if self.get_option('use_drt'):
- binary_name = 'ImageDiff.exe'
- return self._build_path(self.get_option('configuration'), binary_name)
-
- def _path_to_wdiff(self):
- return self.path_from_chromium_base('third_party', 'cygwin', 'bin',
- 'wdiff.exe')
-
- def _shut_down_http_server(self, server_pid):
- """Shut down the lighttpd web server. Blocks until it's fully
- shut down.
-
- Args:
- server_pid: The process ID of the running server.
- """
- # FIXME: Why are we ignoring server_pid and calling
- # _kill_all instead of Executive.kill_process(pid)?
- self._executive.kill_all("LightTPD.exe")
- self._executive.kill_all("httpd.exe")
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win_unittest.py
deleted file mode 100644
index 36f3c6b..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win_unittest.py
+++ /dev/null
@@ -1,74 +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.
-
-import os
-import sys
-import unittest
-import chromium_win
-from webkitpy.common.system import outputcapture
-from webkitpy.tool import mocktool
-
-
-class ChromiumWinTest(unittest.TestCase):
-
- class RegisterCygwinOption(object):
- def __init__(self):
- self.register_cygwin = True
-
- def setUp(self):
- self.orig_platform = sys.platform
-
- def tearDown(self):
- sys.platform = self.orig_platform
-
- def _mock_path_from_chromium_base(self, *comps):
- return os.path.join("/chromium/src", *comps)
-
- def test_setup_environ_for_server(self):
- port = chromium_win.ChromiumWinPort()
- port._executive = mocktool.MockExecutive(should_log=True)
- port.path_from_chromium_base = self._mock_path_from_chromium_base
- output = outputcapture.OutputCapture()
- orig_environ = os.environ.copy()
- env = output.assert_outputs(self, port.setup_environ_for_server)
- self.assertEqual(orig_environ["PATH"], os.environ["PATH"])
- self.assertNotEqual(env["PATH"], os.environ["PATH"])
-
- def test_setup_environ_for_server_register_cygwin(self):
- sys.platform = "win32"
- port = chromium_win.ChromiumWinPort(
- options=ChromiumWinTest.RegisterCygwinOption())
- port._executive = mocktool.MockExecutive(should_log=True)
- port.path_from_chromium_base = self._mock_path_from_chromium_base
- setup_mount = self._mock_path_from_chromium_base("third_party",
- "cygwin",
- "setup_mount.bat")
- expected_stderr = "MOCK run_command: %s\n" % [setup_mount]
- output = outputcapture.OutputCapture()
- output.assert_outputs(self, port.setup_environ_for_server,
- expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/config.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/config.py
deleted file mode 100644
index 9aec637..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/config.py
+++ /dev/null
@@ -1,169 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Wrapper objects for WebKit-specific utility routines."""
-
-# FIXME: This file needs to be unified with common/checkout/scm.py and
-# common/config/ports.py .
-
-import os
-
-from webkitpy.common.system import logutils
-from webkitpy.common.system import executive
-
-
-_log = logutils.get_logger(__file__)
-
-#
-# FIXME: This is used to record if we've already hit the filesystem to look
-# for a default configuration. We cache this to speed up the unit tests,
-# but this can be reset with clear_cached_configuration(). This should be
-# replaced with us consistently using MockConfigs() for tests that don't
-# hit the filesystem at all and provide a reliable value.
-#
-_have_determined_configuration = False
-_configuration = "Release"
-
-
-def clear_cached_configuration():
- global _have_determined_configuration, _configuration
- _have_determined_configuration = False
- _configuration = "Release"
-
-
-class Config(object):
- _FLAGS_FROM_CONFIGURATIONS = {
- "Debug": "--debug",
- "Release": "--release",
- }
-
- def __init__(self, executive, filesystem):
- self._executive = executive
- self._filesystem = filesystem
- self._webkit_base_dir = None
- self._default_configuration = None
- self._build_directories = {}
-
- def build_directory(self, configuration):
- """Returns the path to the build directory for the configuration."""
- if configuration:
- flags = ["--configuration",
- self._FLAGS_FROM_CONFIGURATIONS[configuration]]
- else:
- configuration = ""
- flags = ["--top-level"]
-
- if not self._build_directories.get(configuration):
- args = ["perl", self._script_path("webkit-build-directory")] + flags
- self._build_directories[configuration] = (
- self._executive.run_command(args).rstrip())
-
- return self._build_directories[configuration]
-
- def build_dumprendertree(self, configuration):
- """Builds DRT in the given configuration.
-
- Returns True if the build was successful and up-to-date."""
- flag = self._FLAGS_FROM_CONFIGURATIONS[configuration]
- exit_code = self._executive.run_command([
- self._script_path("build-dumprendertree"), flag],
- return_exit_code=True)
- if exit_code != 0:
- _log.error("Failed to build DumpRenderTree")
- return False
- return True
-
- def default_configuration(self):
- """Returns the default configuration for the user.
-
- Returns the value set by 'set-webkit-configuration', or "Release"
- if that has not been set. This mirrors the logic in webkitdirs.pm."""
- if not self._default_configuration:
- self._default_configuration = self._determine_configuration()
- if not self._default_configuration:
- self._default_configuration = 'Release'
- if self._default_configuration not in self._FLAGS_FROM_CONFIGURATIONS:
- _log.warn("Configuration \"%s\" is not a recognized value.\n" %
- self._default_configuration)
- _log.warn("Scripts may fail. "
- "See 'set-webkit-configuration --help'.")
- return self._default_configuration
-
- def path_from_webkit_base(self, *comps):
- return self._filesystem.join(self.webkit_base_dir(), *comps)
-
- def webkit_base_dir(self):
- """Returns the absolute path to the top of the WebKit tree.
-
- Raises an AssertionError if the top dir can't be determined."""
- # Note: this code somewhat duplicates the code in
- # scm.find_checkout_root(). However, that code only works if the top
- # of the SCM repository also matches the top of the WebKit tree. The
- # Chromium ports, for example, only check out subdirectories like
- # WebKitTools/Scripts, and so we still have to do additional work
- # to find the top of the tree.
- #
- # This code will also work if there is no SCM system at all.
- if not self._webkit_base_dir:
- abspath = os.path.abspath(__file__)
- self._webkit_base_dir = abspath[0:abspath.find('WebKitTools')]
- return self._webkit_base_dir
-
- def _script_path(self, script_name):
- return self._filesystem.join(self.webkit_base_dir(), "WebKitTools",
- "Scripts", script_name)
-
- def _determine_configuration(self):
- # This mirrors the logic in webkitdirs.pm:determineConfiguration().
- #
- # FIXME: See the comment at the top of the file regarding unit tests
- # and our use of global mutable static variables.
- global _have_determined_configuration, _configuration
- if not _have_determined_configuration:
- contents = self._read_configuration()
- if not contents:
- contents = "Release"
- if contents == "Deployment":
- contents = "Release"
- if contents == "Development":
- contents = "Debug"
- _configuration = contents
- _have_determined_configuration = True
- return _configuration
-
- def _read_configuration(self):
- try:
- configuration_path = self._filesystem.join(self.build_directory(None),
- "Configuration")
- if not self._filesystem.exists(configuration_path):
- return None
- except (OSError, executive.ScriptError):
- return None
-
- return self._filesystem.read_text_file(configuration_path).rstrip()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/config_mock.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/config_mock.py
deleted file mode 100644
index af71fa3..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/config_mock.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Wrapper objects for WebKit-specific utility routines."""
-
-
-class MockConfig(object):
- def __init__(self, default_configuration='Release'):
- self._default_configuration = default_configuration
-
- def build_directory(self, configuration):
- return "/build"
-
- def build_dumprendertree(self, configuration):
- return True
-
- def default_configuration(self):
- return self._default_configuration
-
- def path_from_webkit_base(self, *comps):
- return "/" + "/".join(list(comps))
-
- def webkit_base_dir(self):
- return "/"
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/config_standalone.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/config_standalone.py
deleted file mode 100644
index 3dec3b9..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/config_standalone.py
+++ /dev/null
@@ -1,70 +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.
-
-"""FIXME: This script is used by
-config_unittest.test_default_configuration__standalone() to read the
-default configuration to work around any possible caching / reset bugs. See
-https://bugs.webkit.org/show_bug?id=49360 for the motivation. We can remove
-this test when we remove the global configuration cache in config.py."""
-
-import os
-import unittest
-import sys
-
-
-# Ensure that webkitpy is in PYTHONPATH.
-this_dir = os.path.abspath(sys.path[0])
-up = os.path.dirname
-script_dir = up(up(up(this_dir)))
-if script_dir not in sys.path:
- sys.path.append(script_dir)
-
-from webkitpy.common.system import executive
-from webkitpy.common.system import executive_mock
-from webkitpy.common.system import filesystem
-from webkitpy.common.system import filesystem_mock
-
-import config
-
-
-def main(argv=None):
- if not argv:
- argv = sys.argv
-
- if len(argv) == 3 and argv[1] == '--mock':
- e = executive_mock.MockExecutive2(output='foo')
- fs = filesystem_mock.MockFileSystem({'foo/Configuration': argv[2]})
- else:
- e = executive.Executive()
- fs = filesystem.FileSystem()
-
- c = config.Config(e, fs)
- print c.default_configuration()
-
-if __name__ == '__main__':
- main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/config_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/config_unittest.py
deleted file mode 100644
index 2d23691..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/config_unittest.py
+++ /dev/null
@@ -1,201 +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.
-
-import os
-import sys
-import unittest
-
-from webkitpy.common.system import executive
-from webkitpy.common.system import executive_mock
-from webkitpy.common.system import filesystem
-from webkitpy.common.system import filesystem_mock
-from webkitpy.common.system import outputcapture
-
-import config
-
-
-def mock_run_command(arg_list):
- # Set this to True to test actual output (where possible).
- integration_test = False
- if integration_test:
- return executive.Executive().run_command(arg_list)
-
- if 'webkit-build-directory' in arg_list[1]:
- return mock_webkit_build_directory(arg_list[2:])
- return 'Error'
-
-
-def mock_webkit_build_directory(arg_list):
- if arg_list == ['--top-level']:
- return '/WebKitBuild'
- elif arg_list == ['--configuration', '--debug']:
- return '/WebKitBuild/Debug'
- elif arg_list == ['--configuration', '--release']:
- return '/WebKitBuild/Release'
- return 'Error'
-
-
-class ConfigTest(unittest.TestCase):
- def tearDown(self):
- config.clear_cached_configuration()
-
- def make_config(self, output='', files={}, exit_code=0, exception=None,
- run_command_fn=None):
- e = executive_mock.MockExecutive2(output=output, exit_code=exit_code,
- exception=exception,
- run_command_fn=run_command_fn)
- fs = filesystem_mock.MockFileSystem(files)
- return config.Config(e, fs)
-
- def assert_configuration(self, contents, expected):
- # This tests that a configuration file containing
- # _contents_ ends up being interpreted as _expected_.
- c = self.make_config('foo', {'foo/Configuration': contents})
- self.assertEqual(c.default_configuration(), expected)
-
- def test_build_directory(self):
- # --top-level
- c = self.make_config(run_command_fn=mock_run_command)
- self.assertTrue(c.build_directory(None).endswith('WebKitBuild'))
-
- # Test again to check caching
- self.assertTrue(c.build_directory(None).endswith('WebKitBuild'))
-
- # Test other values
- self.assertTrue(c.build_directory('Release').endswith('/Release'))
- self.assertTrue(c.build_directory('Debug').endswith('/Debug'))
- self.assertRaises(KeyError, c.build_directory, 'Unknown')
-
- def test_build_dumprendertree__success(self):
- c = self.make_config(exit_code=0)
- self.assertTrue(c.build_dumprendertree("Debug"))
- self.assertTrue(c.build_dumprendertree("Release"))
- self.assertRaises(KeyError, c.build_dumprendertree, "Unknown")
-
- def test_build_dumprendertree__failure(self):
- c = self.make_config(exit_code=-1)
-
- # FIXME: Build failures should log errors. However, the message we
- # get depends on how we're being called; as a standalone test,
- # we'll get the "no handlers found" message. As part of
- # test-webkitpy, we get the actual message. Really, we need
- # outputcapture to install its own handler.
- oc = outputcapture.OutputCapture()
- oc.capture_output()
- self.assertFalse(c.build_dumprendertree('Debug'))
- oc.restore_output()
-
- oc.capture_output()
- self.assertFalse(c.build_dumprendertree('Release'))
- oc.restore_output()
-
- def test_default_configuration__release(self):
- self.assert_configuration('Release', 'Release')
-
- def test_default_configuration__debug(self):
- self.assert_configuration('Debug', 'Debug')
-
- def test_default_configuration__deployment(self):
- self.assert_configuration('Deployment', 'Release')
-
- def test_default_configuration__development(self):
- self.assert_configuration('Development', 'Debug')
-
- def test_default_configuration__notfound(self):
- # This tests what happens if the default configuration file
- # doesn't exist.
- c = self.make_config(output='foo', files={'foo/Configuration': None})
- self.assertEqual(c.default_configuration(), "Release")
-
- def test_default_configuration__unknown(self):
- # Ignore the warning about an unknown configuration value.
- oc = outputcapture.OutputCapture()
- oc.capture_output()
- self.assert_configuration('Unknown', 'Unknown')
- oc.restore_output()
-
- def test_default_configuration__standalone(self):
- # FIXME: This test runs a standalone python script to test
- # reading the default configuration to work around any possible
- # caching / reset bugs. See https://bugs.webkit.org/show_bug?id=49360
- # for the motivation. We can remove this test when we remove the
- # global configuration cache in config.py.
- e = executive.Executive()
- fs = filesystem.FileSystem()
- c = config.Config(e, fs)
- script = c.path_from_webkit_base('WebKitTools', 'Scripts',
- 'webkitpy', 'layout_tests', 'port', 'config_standalone.py')
-
- # Note: don't use 'Release' here, since that's the normal default.
- expected = 'Debug'
-
- args = [sys.executable, script, '--mock', expected]
- actual = e.run_command(args).rstrip()
- self.assertEqual(actual, expected)
-
- def test_default_configuration__no_perl(self):
- # We need perl to run webkit-build-directory to find out where the
- # default configuration file is. See what happens if perl isn't
- # installed. (We should get the default value, 'Release').
- c = self.make_config(exception=OSError)
- actual = c.default_configuration()
- self.assertEqual(actual, 'Release')
-
- def test_default_configuration__scripterror(self):
- # We run webkit-build-directory to find out where the default
- # configuration file is. See what happens if that script fails.
- # (We should get the default value, 'Release').
- c = self.make_config(exception=executive.ScriptError())
- actual = c.default_configuration()
- self.assertEqual(actual, 'Release')
-
- def test_path_from_webkit_base(self):
- # FIXME: We use a real filesystem here. Should this move to a
- # mocked one?
- c = config.Config(executive.Executive(), filesystem.FileSystem())
- self.assertTrue(c.path_from_webkit_base('foo'))
-
- def test_webkit_base_dir(self):
- # FIXME: We use a real filesystem here. Should this move to a
- # mocked one?
- c = config.Config(executive.Executive(), filesystem.FileSystem())
- base_dir = c.webkit_base_dir()
- self.assertTrue(base_dir)
-
- orig_cwd = os.getcwd()
- os.chdir(os.environ['HOME'])
- c = config.Config(executive.Executive(), filesystem.FileSystem())
- try:
- base_dir_2 = c.webkit_base_dir()
- self.assertEqual(base_dir, base_dir_2)
- finally:
- os.chdir(orig_cwd)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py
deleted file mode 100644
index 4ed34e6..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py
+++ /dev/null
@@ -1,132 +0,0 @@
-#!/usr/bin/env 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 Google name 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.
-
-"""This is a test implementation of the Port interface that generates the
- correct output for every test. It can be used for perf testing, because
- it is pretty much a lower limit on how fast a port can possibly run.
-
- This implementation acts as a wrapper around a real port (the real port
- is held as a delegate object). To specify which port, use the port name
- 'dryrun-XXX' (e.g., 'dryrun-chromium-mac-leopard'). If you use just
- 'dryrun', it uses the default port.
-
- Note that because this is really acting as a wrapper around the underlying
- port, you must be able to run the underlying port as well
- (check_build() and check_sys_deps() must pass and auxiliary binaries
- like layout_test_helper and httpd must work).
-
- This implementation also modifies the test expectations so that all
- tests are either SKIPPED or expected to PASS."""
-
-from __future__ import with_statement
-
-import os
-import sys
-import time
-
-from webkitpy.layout_tests.layout_package import test_output
-
-import base
-import factory
-
-
-class DryRunPort(object):
- """DryRun implementation of the Port interface."""
-
- def __init__(self, **kwargs):
- pfx = 'dryrun-'
- if 'port_name' in kwargs:
- if kwargs['port_name'].startswith(pfx):
- kwargs['port_name'] = kwargs['port_name'][len(pfx):]
- else:
- kwargs['port_name'] = None
- self.__delegate = factory.get(**kwargs)
-
- def __getattr__(self, name):
- return getattr(self.__delegate, name)
-
- def check_build(self, needs_http):
- return True
-
- def check_sys_deps(self, needs_http):
- return True
-
- def start_helper(self):
- pass
-
- def start_http_server(self):
- pass
-
- def start_websocket_server(self):
- pass
-
- def stop_helper(self):
- pass
-
- def stop_http_server(self):
- pass
-
- def stop_websocket_server(self):
- pass
-
- def create_driver(self, worker_number):
- return DryrunDriver(self, worker_number)
-
-
-class DryrunDriver(base.Driver):
- """Dryrun implementation of the DumpRenderTree / Driver interface."""
-
- def __init__(self, port, worker_number):
- self._port = port
- self._worker_number = worker_number
-
- def cmd_line(self):
- return ['None']
-
- def poll(self):
- return None
-
- def run_test(self, test_input):
- start_time = time.time()
- text_output = self._port.expected_text(test_input.filename)
-
- if test_input.image_hash is not None:
- image = self._port.expected_image(test_input.filename)
- hash = self._port.expected_checksum(test_input.filename)
- else:
- image = None
- hash = None
- return test_output.TestOutput(text_output, image, hash, False,
- time.time() - start_time, False, None)
-
- def start(self):
- pass
-
- def stop(self):
- pass
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/factory.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/factory.py
deleted file mode 100644
index 6935744..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/factory.py
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Factory method to retrieve the appropriate port implementation."""
-
-
-import sys
-
-ALL_PORT_NAMES = ['test', 'dryrun', 'mac', 'win', 'gtk', 'qt', 'chromium-mac',
- 'chromium-linux', 'chromium-win', 'google-chrome-win',
- 'google-chrome-mac', 'google-chrome-linux32', 'google-chrome-linux64']
-
-
-def get(port_name=None, options=None, **kwargs):
- """Returns an object implementing the Port interface. If
- port_name is None, this routine attempts to guess at the most
- appropriate port on this platform."""
- # Wrapped for backwards-compatibility
- if port_name:
- kwargs['port_name'] = port_name
- if options:
- kwargs['options'] = options
- return _get_kwargs(**kwargs)
-
-
-def _get_kwargs(**kwargs):
- port_to_use = kwargs.get('port_name', None)
- options = kwargs.get('options', None)
- if port_to_use is None:
- if sys.platform == 'win32' or sys.platform == 'cygwin':
- if options and hasattr(options, 'chromium') and options.chromium:
- port_to_use = 'chromium-win'
- else:
- port_to_use = 'win'
- elif sys.platform == 'linux2':
- port_to_use = 'chromium-linux'
- elif sys.platform == 'darwin':
- if options and hasattr(options, 'chromium') and options.chromium:
- port_to_use = 'chromium-mac'
- else:
- port_to_use = 'mac'
-
- if port_to_use is None:
- raise NotImplementedError('unknown port; sys.platform = "%s"' %
- sys.platform)
-
- if port_to_use == 'test':
- import test
- maker = test.TestPort
- elif port_to_use.startswith('dryrun'):
- import dryrun
- maker = dryrun.DryRunPort
- elif port_to_use.startswith('mac'):
- import mac
- maker = mac.MacPort
- elif port_to_use.startswith('win'):
- import win
- maker = win.WinPort
- elif port_to_use.startswith('gtk'):
- import gtk
- maker = gtk.GtkPort
- elif port_to_use.startswith('qt'):
- import qt
- maker = qt.QtPort
- elif port_to_use.startswith('chromium-gpu'):
- import chromium_gpu
- maker = chromium_gpu.get
- elif port_to_use.startswith('chromium-mac'):
- import chromium_mac
- maker = chromium_mac.ChromiumMacPort
- elif port_to_use.startswith('chromium-linux'):
- import chromium_linux
- maker = chromium_linux.ChromiumLinuxPort
- elif port_to_use.startswith('chromium-win'):
- import chromium_win
- maker = chromium_win.ChromiumWinPort
- elif port_to_use.startswith('google-chrome'):
- import google_chrome
- maker = google_chrome.GetGoogleChromePort
- else:
- raise NotImplementedError('unsupported port: %s' % port_to_use)
- return maker(**kwargs)
-
-def get_all(options=None):
- """Returns all the objects implementing the Port interface."""
- return dict([(port_name, get(port_name, options=options))
- for port_name in ALL_PORT_NAMES])
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py
deleted file mode 100644
index 978a557..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py
+++ /dev/null
@@ -1,188 +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.
-
-import sys
-import unittest
-
-from webkitpy.tool import mocktool
-
-import chromium_gpu
-import chromium_linux
-import chromium_mac
-import chromium_win
-import dryrun
-import factory
-import google_chrome
-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".
-
- def setUp(self):
- self.real_sys_platform = sys.platform
- self.webkit_options = mocktool.MockOptions(pixel_tests=False)
- self.chromium_options = mocktool.MockOptions(pixel_tests=False,
- chromium=True)
-
- def tearDown(self):
- sys.platform = self.real_sys_platform
-
- def assert_port(self, port_name, expected_port, port_obj=None):
- """Helper assert for port_name.
-
- Args:
- port_name: port name to get port object.
- expected_port: class of expected port object.
- port_obj: optional port object
- """
- port_obj = port_obj or factory.get(port_name=port_name)
- self.assertTrue(isinstance(port_obj, 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_google_chrome(self):
- # The actual Chrome class names aren't available so we test that the
- # objects we get are at least subclasses of the Chromium versions.
- self.assert_port("google-chrome-linux32",
- chromium_linux.ChromiumLinuxPort)
- self.assert_port("google-chrome-linux64",
- chromium_linux.ChromiumLinuxPort)
- self.assert_port("google-chrome-win",
- chromium_win.ChromiumWinPort)
- self.assert_port("google-chrome-mac",
- chromium_mac.ChromiumMacPort)
-
- def test_gtk(self):
- self.assert_port("gtk", gtk.GtkPort)
-
- def test_qt(self):
- self.assert_port("qt", qt.QtPort)
-
- def test_chromium_gpu_linux(self):
- self.assert_port("chromium-gpu-linux", chromium_gpu.ChromiumGpuLinuxPort)
-
- def test_chromium_gpu_mac(self):
- self.assert_port("chromium-gpu-mac", chromium_gpu.ChromiumGpuMacPort)
-
- def test_chromium_gpu_win(self):
- self.assert_port("chromium-gpu-win", chromium_gpu.ChromiumGpuWinPort)
-
- 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)
-
- def test_get_all_ports(self):
- ports = factory.get_all()
- for name in factory.ALL_PORT_NAMES:
- self.assertTrue(name in ports.keys())
- self.assert_port("test", test.TestPort, ports["test"])
- self.assert_port("dryrun-test", dryrun.DryRunPort, ports["dryrun"])
- self.assert_port("dryrun-mac", dryrun.DryRunPort, ports["dryrun"])
- self.assert_port("mac", mac.MacPort, ports["mac"])
- self.assert_port("win", win.WinPort, ports["win"])
- self.assert_port("gtk", gtk.GtkPort, ports["gtk"])
- self.assert_port("qt", qt.QtPort, ports["qt"])
- self.assert_port("chromium-mac", chromium_mac.ChromiumMacPort,
- ports["chromium-mac"])
- self.assert_port("chromium-linux", chromium_linux.ChromiumLinuxPort,
- ports["chromium-linux"])
- self.assert_port("chromium-win", chromium_win.ChromiumWinPort,
- ports["chromium-win"])
-
- def test_unknown_specified(self):
- # Test what happens when you specify an unknown port.
- orig_platform = sys.platform
- self.assertRaises(NotImplementedError, factory.get,
- port_name='unknown')
-
- def test_unknown_default(self):
- # Test what happens when you're running on an unknown platform.
- orig_platform = sys.platform
- sys.platform = 'unknown'
- self.assertRaises(NotImplementedError, factory.get)
- sys.platform = orig_platform
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome.py
deleted file mode 100644
index 8d94bb5..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome.py
+++ /dev/null
@@ -1,122 +0,0 @@
-#!/usr/bin/env 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.
-
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import with_statement
-
-import codecs
-import os
-
-
-def _test_expectations_overrides(port, super):
- # The chrome ports use the regular overrides plus anything in the
- # official test_expectations as well. Hopefully we don't get collisions.
- chromium_overrides = super.test_expectations_overrides(port)
-
- # FIXME: It used to be that AssertionError would get raised by
- # path_from_chromium_base() if we weren't in a Chromium checkout, but
- # this changed in r60427. This should probably be changed back.
- overrides_path = port.path_from_chromium_base('webkit', 'tools',
- 'layout_tests', 'test_expectations_chrome.txt')
- if not os.path.exists(overrides_path):
- return chromium_overrides
-
- with codecs.open(overrides_path, "r", "utf-8") as file:
- if chromium_overrides:
- return chromium_overrides + file.read()
- else:
- return file.read()
-
-def GetGoogleChromePort(**kwargs):
- """Some tests have slightly different results when compiled as Google
- Chrome vs Chromium. In those cases, we prepend an additional directory to
- to the baseline paths."""
- port_name = kwargs['port_name']
- del kwargs['port_name']
- if port_name == 'google-chrome-linux32':
- import chromium_linux
-
- class GoogleChromeLinux32Port(chromium_linux.ChromiumLinuxPort):
- def baseline_search_path(self):
- paths = chromium_linux.ChromiumLinuxPort.baseline_search_path(
- self)
- paths.insert(0, self._webkit_baseline_path(
- 'google-chrome-linux32'))
- return paths
-
- def test_expectations_overrides(self):
- return _test_expectations_overrides(self,
- chromium_linux.ChromiumLinuxPort)
-
- return GoogleChromeLinux32Port(**kwargs)
- elif port_name == 'google-chrome-linux64':
- import chromium_linux
-
- class GoogleChromeLinux64Port(chromium_linux.ChromiumLinuxPort):
- def baseline_search_path(self):
- paths = chromium_linux.ChromiumLinuxPort.baseline_search_path(
- self)
- paths.insert(0, self._webkit_baseline_path(
- 'google-chrome-linux64'))
- return paths
-
- def test_expectations_overrides(self):
- return _test_expectations_overrides(self,
- chromium_linux.ChromiumLinuxPort)
-
- return GoogleChromeLinux64Port(**kwargs)
- elif port_name.startswith('google-chrome-mac'):
- import chromium_mac
-
- class GoogleChromeMacPort(chromium_mac.ChromiumMacPort):
- def baseline_search_path(self):
- paths = chromium_mac.ChromiumMacPort.baseline_search_path(
- self)
- paths.insert(0, self._webkit_baseline_path(
- 'google-chrome-mac'))
- return paths
-
- def test_expectations_overrides(self):
- return _test_expectations_overrides(self,
- chromium_mac.ChromiumMacPort)
-
- return GoogleChromeMacPort(**kwargs)
- elif port_name.startswith('google-chrome-win'):
- import chromium_win
-
- class GoogleChromeWinPort(chromium_win.ChromiumWinPort):
- def baseline_search_path(self):
- paths = chromium_win.ChromiumWinPort.baseline_search_path(
- self)
- paths.insert(0, self._webkit_baseline_path(
- 'google-chrome-win'))
- return paths
-
- def test_expectations_overrides(self):
- return _test_expectations_overrides(self,
- chromium_win.ChromiumWinPort)
-
- return GoogleChromeWinPort(**kwargs)
- raise NotImplementedError('unsupported port: %s' % port_name)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome_unittest.py
deleted file mode 100644
index e60c274..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome_unittest.py
+++ /dev/null
@@ -1,103 +0,0 @@
-#!/usr/bin/env 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.
-
-# 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 codecs
-import os
-import unittest
-
-from webkitpy.common import newstringio
-
-import factory
-import google_chrome
-
-
-class GetGoogleChromePortTest(unittest.TestCase):
- def test_get_google_chrome_port(self):
- test_ports = ('google-chrome-linux32', 'google-chrome-linux64',
- 'google-chrome-mac', 'google-chrome-win')
- for port in test_ports:
- self._verify_baseline_path(port, port)
- self._verify_expectations_overrides(port)
-
- self._verify_baseline_path('google-chrome-mac', 'google-chrome-mac-leopard')
- self._verify_baseline_path('google-chrome-win', 'google-chrome-win-xp')
- self._verify_baseline_path('google-chrome-win', 'google-chrome-win-vista')
-
- def _verify_baseline_path(self, expected_path, port_name):
- port = google_chrome.GetGoogleChromePort(port_name=port_name,
- options=None)
- path = port.baseline_search_path()[0]
- self.assertEqual(expected_path, os.path.split(path)[1])
-
- def _verify_expectations_overrides(self, port_name):
- # FIXME: make this more robust when we have the Tree() abstraction.
- # we should be able to test for the files existing or not, and
- # be able to control the contents better.
-
- chromium_port = factory.get("chromium-mac")
- chromium_overrides = chromium_port.test_expectations_overrides()
- port = google_chrome.GetGoogleChromePort(port_name=port_name,
- options=None)
-
- orig_exists = os.path.exists
- orig_open = codecs.open
- expected_string = "// hello, world\n"
-
- def mock_exists_chrome_not_found(path):
- if 'test_expectations_chrome.txt' in path:
- return False
- return orig_exists(path)
-
- def mock_exists_chrome_found(path):
- if 'test_expectations_chrome.txt' in path:
- return True
- return orig_exists(path)
-
- def mock_open(path, mode, encoding):
- if 'test_expectations_chrome.txt' in path:
- return newstringio.StringIO(expected_string)
- return orig_open(path, mode, encoding)
-
- try:
- os.path.exists = mock_exists_chrome_not_found
- chrome_overrides = port.test_expectations_overrides()
- self.assertEqual(chromium_overrides, chrome_overrides)
-
- os.path.exists = mock_exists_chrome_found
- codecs.open = mock_open
- chrome_overrides = port.test_expectations_overrides()
- if chromium_overrides:
- self.assertEqual(chrome_overrides,
- chromium_overrides + expected_string)
- else:
- self.assertEqual(chrome_overrides, expected_string)
- finally:
- os.path.exists = orig_exists
- codecs.open = orig_open
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/gtk.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/gtk.py
deleted file mode 100644
index c60909e..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/gtk.py
+++ /dev/null
@@ -1,83 +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 Google name 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.
-
-"""WebKit Gtk implementation of the Port interface."""
-
-import logging
-import os
-
-from webkitpy.layout_tests.port.webkit import WebKitPort
-
-_log = logging.getLogger("webkitpy.layout_tests.port.gtk")
-
-
-class GtkPort(WebKitPort):
- """WebKit Gtk implementation of the Port class."""
-
- def __init__(self, **kwargs):
- kwargs.setdefault('port_name', 'gtk')
- WebKitPort.__init__(self, **kwargs)
-
- def _tests_for_other_platforms(self):
- # FIXME: This list could be dynamic based on platform name and
- # pushed into base.Port.
- # This really need to be automated.
- return [
- "platform/chromium",
- "platform/win",
- "platform/qt",
- "platform/mac",
- ]
-
- def _path_to_apache_config_file(self):
- # FIXME: This needs to detect the distribution and change config files.
- return os.path.join(self.layout_tests_dir(), 'http', 'conf',
- 'apache2-debian-httpd.conf')
-
- def _shut_down_http_server(self, server_pid):
- """Shut down the httpd web server. Blocks until it's fully
- shut down.
-
- Args:
- server_pid: The process ID of the running server.
- """
- # server_pid is not set when "http_server.py stop" is run manually.
- if server_pid is None:
- # FIXME: This isn't ideal, since it could conflict with
- # lighttpd processes not started by http_server.py,
- # but good enough for now.
- self._executive.kill_all('apache2')
- else:
- try:
- os.kill(server_pid, signal.SIGTERM)
- # TODO(mmoss) Maybe throw in a SIGKILL just to be sure?
- except OSError:
- # Sometimes we get a bad PID (e.g. from a stale httpd.pid
- # file), so if kill fails on the given PID, just try to
- # 'killall' web servers.
- self._shut_down_http_server(None)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock.py
deleted file mode 100644
index 8995b21..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock.py
+++ /dev/null
@@ -1,132 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Szeged
-# Copyright (C) 2010 Andras Becsi (abecsi@inf.u-szeged.hu), University of Szeged
-#
-# 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.
-#
-# THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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.
-
-"""This class helps to block NRWT threads when more NRWTs run
-http and websocket tests in a same time."""
-
-import glob
-import logging
-import os
-import sys
-import tempfile
-import time
-
-from webkitpy.common.system.executive import Executive
-from webkitpy.common.system.file_lock import FileLock
-
-
-_log = logging.getLogger("webkitpy.layout_tests.port.http_lock")
-
-
-class HttpLock(object):
-
- def __init__(self, lock_path, lock_file_prefix="WebKitHttpd.lock.",
- guard_lock="WebKit.lock"):
- self._lock_path = lock_path
- if not self._lock_path:
- self._lock_path = tempfile.gettempdir()
- self._lock_file_prefix = lock_file_prefix
- self._lock_file_path_prefix = os.path.join(self._lock_path,
- self._lock_file_prefix)
- self._guard_lock_file = os.path.join(self._lock_path, guard_lock)
- self._guard_lock = FileLock(self._guard_lock_file)
- self._process_lock_file_name = ""
- self._executive = Executive()
-
- def cleanup_http_lock(self):
- """Delete the lock file if exists."""
- if os.path.exists(self._process_lock_file_name):
- _log.debug("Removing lock file: %s" % self._process_lock_file_name)
- os.unlink(self._process_lock_file_name)
-
- def _extract_lock_number(self, lock_file_name):
- """Return the lock number from lock file."""
- prefix_length = len(self._lock_file_path_prefix)
- return int(lock_file_name[prefix_length:])
-
- def _lock_file_list(self):
- """Return the list of lock files sequentially."""
- lock_list = glob.glob(self._lock_file_path_prefix + '*')
- lock_list.sort(key=self._extract_lock_number)
- return lock_list
-
- def _next_lock_number(self):
- """Return the next available lock number."""
- lock_list = self._lock_file_list()
- if not lock_list:
- return 0
- return self._extract_lock_number(lock_list[-1]) + 1
-
- def _curent_lock_pid(self):
- """Return with the current lock pid. If the lock is not valid
- it deletes the lock file."""
- lock_list = self._lock_file_list()
- if not lock_list:
- return
- try:
- current_lock_file = open(lock_list[0], 'r')
- current_pid = current_lock_file.readline()
- current_lock_file.close()
- if not (current_pid and self._executive.check_running_pid(int(current_pid))):
- _log.debug("Removing stuck lock file: %s" % lock_list[0])
- os.unlink(lock_list[0])
- return
- except (IOError, OSError):
- return
- return int(current_pid)
-
- def _create_lock_file(self):
- """The lock files are used to schedule the running test sessions in first
- come first served order. The guard lock ensures that the lock numbers are
- sequential."""
- if not os.path.exists(self._lock_path):
- _log.debug("Lock directory does not exist: %s" % self._lock_path)
- return False
-
- if not self._guard_lock.acquire_lock():
- _log.debug("Guard lock timed out!")
- return False
-
- self._process_lock_file_name = (self._lock_file_path_prefix +
- str(self._next_lock_number()))
- _log.debug("Creating lock file: %s" % self._process_lock_file_name)
- lock_file = open(self._process_lock_file_name, 'w')
- lock_file.write(str(os.getpid()))
- lock_file.close()
- self._guard_lock.release_lock()
- return True
-
-
- def wait_for_httpd_lock(self):
- """Create a lock file and wait until it's turn comes. If something goes wrong
- it wont do any locking."""
- if not self._create_lock_file():
- _log.debug("Warning, http locking failed!")
- return
-
- while self._curent_lock_pid() != os.getpid():
- time.sleep(1)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock_unittest.py
deleted file mode 100644
index 85c760a..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock_unittest.py
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Szeged
-#
-# 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.
-#
-# THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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 glob
-import http_lock
-import os
-import unittest
-
-
-class HttpLockTest(unittest.TestCase):
-
- def __init__(self, testFunc):
- self.http_lock_obj = http_lock.HttpLock(None, "WebKitTestHttpd.lock.", "WebKitTest.lock")
- self.lock_file_path_prefix = os.path.join(self.http_lock_obj._lock_path,
- self.http_lock_obj._lock_file_prefix)
- self.lock_file_name = self.lock_file_path_prefix + "0"
- self.guard_lock_file = self.http_lock_obj._guard_lock_file
- self.clean_all_lockfile()
- unittest.TestCase.__init__(self, testFunc)
-
- def clean_all_lockfile(self):
- if os.path.exists(self.guard_lock_file):
- os.unlink(self.guard_lock_file)
- lock_list = glob.glob(self.lock_file_path_prefix + '*')
- for file_name in lock_list:
- os.unlink(file_name)
-
- def assertEqual(self, first, second):
- if first != second:
- self.clean_all_lockfile()
- unittest.TestCase.assertEqual(self, first, second)
-
- def _check_lock_file(self):
- if os.path.exists(self.lock_file_name):
- pid = os.getpid()
- lock_file = open(self.lock_file_name, 'r')
- lock_file_pid = lock_file.readline()
- lock_file.close()
- self.assertEqual(pid, int(lock_file_pid))
- return True
- return False
-
- def test_lock_lifecycle(self):
- self.http_lock_obj._create_lock_file()
-
- self.assertEqual(True, self._check_lock_file())
- self.assertEqual(1, self.http_lock_obj._next_lock_number())
-
- self.http_lock_obj.cleanup_http_lock()
-
- self.assertEqual(False, self._check_lock_file())
- self.assertEqual(0, self.http_lock_obj._next_lock_number())
-
- def test_extract_lock_number(self,):
- lock_file_list = (
- self.lock_file_path_prefix + "00",
- self.lock_file_path_prefix + "9",
- self.lock_file_path_prefix + "001",
- self.lock_file_path_prefix + "021",
- )
-
- expected_number_list = (0, 9, 1, 21)
-
- for lock_file, expected in zip(lock_file_list, expected_number_list):
- self.assertEqual(self.http_lock_obj._extract_lock_number(lock_file), expected)
-
- def test_lock_file_list(self):
- lock_file_list = [
- self.lock_file_path_prefix + "6",
- self.lock_file_path_prefix + "1",
- self.lock_file_path_prefix + "4",
- self.lock_file_path_prefix + "3",
- ]
-
- expected_file_list = [
- self.lock_file_path_prefix + "1",
- self.lock_file_path_prefix + "3",
- self.lock_file_path_prefix + "4",
- self.lock_file_path_prefix + "6",
- ]
-
- for file_name in lock_file_list:
- open(file_name, 'w')
-
- self.assertEqual(self.http_lock_obj._lock_file_list(), expected_file_list)
-
- for file_name in lock_file_list:
- os.unlink(file_name)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server.py
deleted file mode 100755
index 0f8a21e..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server.py
+++ /dev/null
@@ -1,233 +0,0 @@
-#!/usr/bin/env 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.
-
-"""A class to help start/stop the lighttpd server used by layout tests."""
-
-from __future__ import with_statement
-
-import codecs
-import logging
-import optparse
-import os
-import shutil
-import subprocess
-import sys
-import tempfile
-import time
-import urllib
-
-import factory
-import http_server_base
-
-_log = logging.getLogger("webkitpy.layout_tests.port.http_server")
-
-
-class HttpdNotStarted(Exception):
- pass
-
-
-class Lighttpd(http_server_base.HttpServerBase):
-
- def __init__(self, port_obj, output_dir, background=False, port=None,
- root=None, run_background=None):
- """Args:
- output_dir: the absolute path to the layout test result directory
- """
- # Webkit tests
- http_server_base.HttpServerBase.__init__(self, port_obj)
- self._output_dir = output_dir
- self._process = None
- self._port = port
- self._root = root
- self._run_background = run_background
- if self._port:
- self._port = int(self._port)
-
- try:
- self._webkit_tests = os.path.join(
- self._port_obj.layout_tests_dir(), 'http', 'tests')
- self._js_test_resource = os.path.join(
- self._port_obj.layout_tests_dir(), 'fast', 'js', 'resources')
- except:
- self._webkit_tests = None
- self._js_test_resource = None
-
- # Self generated certificate for SSL server (for client cert get
- # <base-path>\chrome\test\data\ssl\certs\root_ca_cert.crt)
- self._pem_file = os.path.join(
- os.path.dirname(os.path.abspath(__file__)), 'httpd2.pem')
-
- # One mapping where we can get to everything
- self.VIRTUALCONFIG = []
-
- if self._webkit_tests:
- self.VIRTUALCONFIG.extend(
- # Three mappings (one with SSL) for LayoutTests http tests
- [{'port': 8000, 'docroot': self._webkit_tests},
- {'port': 8080, 'docroot': self._webkit_tests},
- {'port': 8443, 'docroot': self._webkit_tests,
- 'sslcert': self._pem_file}])
-
- def is_running(self):
- return self._process != None
-
- def start(self):
- if self.is_running():
- raise 'Lighttpd already running'
-
- base_conf_file = self._port_obj.path_from_webkit_base('WebKitTools',
- 'Scripts', 'webkitpy', 'layout_tests', 'port', 'lighttpd.conf')
- out_conf_file = os.path.join(self._output_dir, 'lighttpd.conf')
- time_str = time.strftime("%d%b%Y-%H%M%S")
- access_file_name = "access.log-" + time_str + ".txt"
- access_log = os.path.join(self._output_dir, access_file_name)
- log_file_name = "error.log-" + time_str + ".txt"
- error_log = os.path.join(self._output_dir, log_file_name)
-
- # Remove old log files. We only need to keep the last ones.
- self.remove_log_files(self._output_dir, "access.log-")
- self.remove_log_files(self._output_dir, "error.log-")
-
- # Write out the config
- with codecs.open(base_conf_file, "r", "utf-8") as file:
- base_conf = file.read()
-
- # FIXME: This should be re-worked so that this block can
- # use with open() instead of a manual file.close() call.
- # lighttpd.conf files seem to be UTF-8 without BOM:
- # http://redmine.lighttpd.net/issues/992
- f = codecs.open(out_conf_file, "w", "utf-8")
- f.write(base_conf)
-
- # Write out our cgi handlers. Run perl through env so that it
- # processes the #! line and runs perl with the proper command
- # line arguments. Emulate apache's mod_asis with a cat cgi handler.
- f.write(('cgi.assign = ( ".cgi" => "/usr/bin/env",\n'
- ' ".pl" => "/usr/bin/env",\n'
- ' ".asis" => "/bin/cat",\n'
- ' ".php" => "%s" )\n\n') %
- self._port_obj._path_to_lighttpd_php())
-
- # Setup log files
- f.write(('server.errorlog = "%s"\n'
- 'accesslog.filename = "%s"\n\n') % (error_log, access_log))
-
- # Setup upload folders. Upload folder is to hold temporary upload files
- # and also POST data. This is used to support XHR layout tests that
- # does POST.
- f.write(('server.upload-dirs = ( "%s" )\n\n') % (self._output_dir))
-
- # Setup a link to where the js test templates are stored
- f.write(('alias.url = ( "/js-test-resources" => "%s" )\n\n') %
- (self._js_test_resource))
-
- # dump out of virtual host config at the bottom.
- if self._root:
- if self._port:
- # Have both port and root dir.
- mappings = [{'port': self._port, 'docroot': self._root}]
- else:
- # Have only a root dir - set the ports as for LayoutTests.
- # This is used in ui_tests to run http tests against a browser.
-
- # default set of ports as for LayoutTests but with a
- # specified root.
- mappings = [{'port': 8000, 'docroot': self._root},
- {'port': 8080, 'docroot': self._root},
- {'port': 8443, 'docroot': self._root,
- 'sslcert': self._pem_file}]
- else:
- mappings = self.VIRTUALCONFIG
- for mapping in mappings:
- ssl_setup = ''
- if 'sslcert' in mapping:
- ssl_setup = (' ssl.engine = "enable"\n'
- ' ssl.pemfile = "%s"\n' % mapping['sslcert'])
-
- f.write(('$SERVER["socket"] == "127.0.0.1:%d" {\n'
- ' server.document-root = "%s"\n' +
- ssl_setup +
- '}\n\n') % (mapping['port'], mapping['docroot']))
- f.close()
-
- executable = self._port_obj._path_to_lighttpd()
- module_path = self._port_obj._path_to_lighttpd_modules()
- start_cmd = [executable,
- # Newly written config file
- '-f', os.path.join(self._output_dir, 'lighttpd.conf'),
- # Where it can find its module dynamic libraries
- '-m', module_path]
-
- if not self._run_background:
- start_cmd.append(# Don't background
- '-D')
-
- # Copy liblightcomp.dylib to /tmp/lighttpd/lib to work around the
- # bug that mod_alias.so loads it from the hard coded path.
- if sys.platform == 'darwin':
- tmp_module_path = '/tmp/lighttpd/lib'
- if not os.path.exists(tmp_module_path):
- os.makedirs(tmp_module_path)
- lib_file = 'liblightcomp.dylib'
- shutil.copyfile(os.path.join(module_path, lib_file),
- os.path.join(tmp_module_path, lib_file))
-
- 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)
-
- # Wait for server to start.
- self.mappings = mappings
- server_started = self.wait_for_action(
- self.is_server_running_on_all_ports)
-
- # Our process terminated already
- if not server_started or self._process.returncode != None:
- raise google.httpd_utils.HttpdNotStarted('Failed to start httpd.')
-
- _log.debug("Server successfully started")
-
- # TODO(deanm): Find a nicer way to shutdown cleanly. Our log files are
- # probably not being flushed, etc... why doesn't our python have os.kill ?
-
- def stop(self, force=False):
- if not force and not self.is_running():
- return
-
- httpd_pid = None
- if self._process:
- httpd_pid = self._process.pid
- self._port_obj._shut_down_http_server(httpd_pid)
-
- if self._process:
- # wait() is not threadsafe and can throw OSError due to:
- # http://bugs.python.org/issue1731717
- self._process.wait()
- self._process = None
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server_base.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server_base.py
deleted file mode 100644
index 2745cce..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_server_base.py
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Base class with common routines between the Apache and Lighttpd servers."""
-
-import logging
-import os
-import time
-import urllib
-
-_log = logging.getLogger("webkitpy.layout_tests.port.http_server_base")
-
-
-class HttpServerBase(object):
-
- def __init__(self, port_obj):
- self._port_obj = port_obj
-
- def wait_for_action(self, action):
- """Repeat the action for 20 seconds or until it succeeds. Returns
- whether it succeeded."""
- start_time = time.time()
- while time.time() - start_time < 20:
- if action():
- return True
- _log.debug("Waiting for action: %s" % action)
- time.sleep(1)
-
- return False
-
- def is_server_running_on_all_ports(self):
- """Returns whether the server is running on all the desired ports."""
- for mapping in self.mappings:
- if 'sslcert' in mapping:
- http_suffix = 's'
- else:
- http_suffix = ''
-
- url = 'http%s://127.0.0.1:%d/' % (http_suffix, mapping['port'])
-
- try:
- response = urllib.urlopen(url)
- _log.debug("Server running at %s" % url)
- except IOError, e:
- _log.debug("Server NOT running at %s: %s" % (url, e))
- return False
-
- return True
-
- def remove_log_files(self, folder, starts_with):
- files = os.listdir(folder)
- for file in files:
- if file.startswith(starts_with):
- full_path = os.path.join(folder, file)
- os.remove(full_path)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/httpd2.pem b/WebKitTools/Scripts/webkitpy/layout_tests/port/httpd2.pem
deleted file mode 100644
index 6349b78..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/httpd2.pem
+++ /dev/null
@@ -1,41 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEZDCCAkygAwIBAgIBATANBgkqhkiG9w0BAQUFADBgMRAwDgYDVQQDEwdUZXN0
-IENBMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
-TW91bnRhaW4gVmlldzESMBAGA1UEChMJQ2VydCBUZXN0MB4XDTA4MDcyODIyMzIy
-OFoXDTEzMDcyNzIyMzIyOFowSjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlm
-b3JuaWExEjAQBgNVBAoTCUNlcnQgVGVzdDESMBAGA1UEAxMJMTI3LjAuMC4xMIGf
-MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQj2tPWPUgbuI4H3/3dnttqVbndwU3
-3BdRCd67DFM44GRrsjDSH4bY/EbFyX9D52d/iy6ZaAmDePcCz5k/fgP3DMujykYG
-qgNiV2ywxTlMj7NlN2C7SRt68fQMZr5iI7rypdxuaZt9lSMD3ENBffYtuLTyZd9a
-3JPJe1TaIab5GwIDAQABo4HCMIG/MAkGA1UdEwQCMAAwHQYDVR0OBBYEFCYLBv5K
-x5sLNVlpLh5FwTwhdDl7MIGSBgNVHSMEgYowgYeAFF3Of5nj1BlBMU/Gz7El9Vqv
-45cxoWSkYjBgMRAwDgYDVQQDEwdUZXN0IENBMQswCQYDVQQGEwJVUzETMBEGA1UE
-CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzESMBAGA1UEChMJ
-Q2VydCBUZXN0ggkA1FGT1D/e2U4wDQYJKoZIhvcNAQEFBQADggIBAEtkVmLObUgk
-b2cIA2S+QDtifq1UgVfBbytvR2lFmnADOR55mo0gHQG3HHqq4g034LmoVXDHhUk8
-Gb6aFiv4QubmVhLXcUelTRXwiNvGzkW7pC6Jrq105hdPjzXMKTcmiLaopm5Fqfc7
-hj5Cn1Sjspc8pdeQjrbeMdvca7KlFrGP8YkwCU2xOOX9PiN9G0966BWfjnr/fZZp
-+OQVuUFHdiAZwthEMuDpAAXHqYXIsermgdOpgJaA53cf8NqBV2QGhtFgtsJCRoiu
-7DKqhyRWBGyz19VIH2b7y+6qvQVxuHk19kKRM0nftw/yNcJnm7gtttespMUPsOMa
-a2SD1G0hm0TND6vxaBhgR3cVqpl/qIpAdFi00Tm7hTyYE7I43zPW03t+/DpCt3Um
-EMRZsQ90co5q+bcx/vQ7YAtwUh30uMb0wpibeyCwDp8cqNmSiRkEuc/FjTYes5t8
-5gR//WX1l0+qjrjusO9NmoLnq2Yk6UcioX+z+q6Z/dudGfqhLfeWD2Q0LWYA242C
-d7km5Y3KAt1PJdVsof/aiVhVdddY/OIEKTRQhWEdDbosy2eh16BCKXT2FFvhNDg1
-AYFvn6I8nj9IldMJiIc3DdhacEAEzRMeRgPdzAa1griKUGknxsyTyRii8ru0WS6w
-DCNrlDOVXdzYGEZooBI76BDVY0W0akjV
------END CERTIFICATE-----
------BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQDQj2tPWPUgbuI4H3/3dnttqVbndwU33BdRCd67DFM44GRrsjDS
-H4bY/EbFyX9D52d/iy6ZaAmDePcCz5k/fgP3DMujykYGqgNiV2ywxTlMj7NlN2C7
-SRt68fQMZr5iI7rypdxuaZt9lSMD3ENBffYtuLTyZd9a3JPJe1TaIab5GwIDAQAB
-AoGANHXu8z2YIzlhE+bwhGm8MGBpKL3qhRuKjeriqMA36tWezOw8lY4ymEAU+Ulv
-BsCdaxqydQoTYou57m4TyUHEcxq9pq3H0zB0qL709DdHi/t4zbV9XIoAzC5v0/hG
-9+Ca29TwC02FCw+qLkNrtwCpwOcQmc+bPxqvFu1iMiahURECQQD2I/Hi2413CMZz
-TBjl8fMiVO9GhA2J0sc8Qi+YcgJakaLD9xcbaiLkTzPZDlA389C1b6Ia+poAr4YA
-Ve0FFbxpAkEA2OobayyHE/QtPEqoy6NLR57jirmVBNmSWWd4lAyL5UIHIYVttJZg
-8CLvbzaU/iDGwR+wKsM664rKPHEmtlyo4wJBAMeSqYO5ZOCJGu9NWjrHjM3fdAsG
-8zs2zhiLya+fcU0iHIksBW5TBmt71Jw/wMc9R5J1K0kYvFml98653O5si1ECQBCk
-RV4/mE1rmlzZzYFyEcB47DQkcM5ictvxGEsje0gnfKyRtAz6zI0f4QbDRUMJ+LWw
-XK+rMsYHa+SfOb0b9skCQQCLdeonsIpFDv/Uv+flHISy0WA+AFkLXrRkBKh6G/OD
-dMHaNevkJgUnpceVEnkrdenp5CcEoFTI17pd+nBgDm/B
------END RSA PRIVATE KEY-----
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf b/WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf
deleted file mode 100644
index 26ca22f..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/lighttpd.conf
+++ /dev/null
@@ -1,90 +0,0 @@
-server.tag = "LightTPD/1.4.19 (Win32)"
-server.modules = ( "mod_accesslog",
- "mod_alias",
- "mod_cgi",
- "mod_rewrite" )
-
-# default document root required
-server.document-root = "."
-
-# files to check for if .../ is requested
-index-file.names = ( "index.php", "index.pl", "index.cgi",
- "index.html", "index.htm", "default.htm" )
-# mimetype mapping
-mimetype.assign = (
- ".gif" => "image/gif",
- ".jpg" => "image/jpeg",
- ".jpeg" => "image/jpeg",
- ".png" => "image/png",
- ".svg" => "image/svg+xml",
- ".css" => "text/css",
- ".html" => "text/html",
- ".htm" => "text/html",
- ".xhtml" => "application/xhtml+xml",
- ".xhtmlmp" => "application/vnd.wap.xhtml+xml",
- ".js" => "application/x-javascript",
- ".log" => "text/plain",
- ".conf" => "text/plain",
- ".text" => "text/plain",
- ".txt" => "text/plain",
- ".dtd" => "text/xml",
- ".xml" => "text/xml",
- ".manifest" => "text/cache-manifest",
- )
-
-# Use the "Content-Type" extended attribute to obtain mime type if possible
-mimetype.use-xattr = "enable"
-
-##
-# which extensions should not be handle via static-file transfer
-#
-# .php, .pl, .fcgi are most often handled by mod_fastcgi or mod_cgi
-static-file.exclude-extensions = ( ".php", ".pl", ".cgi" )
-
-server.bind = "localhost"
-server.port = 8001
-
-## virtual directory listings
-dir-listing.activate = "enable"
-#dir-listing.encoding = "iso-8859-2"
-#dir-listing.external-css = "style/oldstyle.css"
-
-## enable debugging
-#debug.log-request-header = "enable"
-#debug.log-response-header = "enable"
-#debug.log-request-handling = "enable"
-#debug.log-file-not-found = "enable"
-
-#### SSL engine
-#ssl.engine = "enable"
-#ssl.pemfile = "server.pem"
-
-# Rewrite rule for utf-8 path test (LayoutTests/http/tests/uri/utf8-path.html)
-# See the apache rewrite rule at LayoutTests/http/tests/uri/intercept/.htaccess
-# Rewrite rule for LayoutTests/http/tests/appcache/cyrillic-uri.html.
-# See the apache rewrite rule at
-# LayoutTests/http/tests/appcache/resources/intercept/.htaccess
-url.rewrite-once = (
- "^/uri/intercept/(.*)" => "/uri/resources/print-uri.php",
- "^/appcache/resources/intercept/(.*)" => "/appcache/resources/print-uri.php"
-)
-
-# LayoutTests/http/tests/xmlhttprequest/response-encoding.html uses an htaccess
-# to override charset for reply2.txt, reply2.xml, and reply4.txt.
-$HTTP["url"] =~ "^/xmlhttprequest/resources/reply2.(txt|xml)" {
- mimetype.assign = (
- ".txt" => "text/plain; charset=windows-1251",
- ".xml" => "text/xml; charset=windows-1251"
- )
-}
-$HTTP["url"] =~ "^/xmlhttprequest/resources/reply4.txt" {
- mimetype.assign = ( ".txt" => "text/plain; charset=koi8-r" )
-}
-
-# LayoutTests/http/tests/appcache/wrong-content-type.html uses an htaccess
-# to override mime type for wrong-content-type.manifest.
-$HTTP["url"] =~ "^/appcache/resources/wrong-content-type.manifest" {
- mimetype.assign = ( ".manifest" => "text/plain" )
-}
-
-# Autogenerated test-specific config follows.
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/mac.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/mac.py
deleted file mode 100644
index 696e339..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/mac.py
+++ /dev/null
@@ -1,152 +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 Google name 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.
-
-"""WebKit Mac implementation of the Port interface."""
-
-import logging
-import os
-import platform
-import signal
-
-import webkitpy.common.system.ospath as ospath
-import webkitpy.layout_tests.port.server_process as server_process
-from webkitpy.layout_tests.port.webkit import WebKitPort, WebKitDriver
-
-_log = logging.getLogger("webkitpy.layout_tests.port.mac")
-
-
-class MacPort(WebKitPort):
- """WebKit Mac implementation of the Port class."""
-
- def __init__(self, **kwargs):
- kwargs.setdefault('port_name', 'mac' + self.version())
- WebKitPort.__init__(self, **kwargs)
-
- def default_child_processes(self):
- # FIXME: new-run-webkit-tests is unstable on Mac running more than
- # four threads in parallel.
- # See https://bugs.webkit.org/show_bug.cgi?id=36622
- child_processes = WebKitPort.default_child_processes(self)
- if child_processes > 4:
- return 4
- return child_processes
-
- def baseline_search_path(self):
- port_names = []
- if self._name == 'mac-tiger':
- port_names.append("mac-tiger")
- if self._name in ('mac-tiger', 'mac-leopard'):
- port_names.append("mac-leopard")
- if self._name in ('mac-tiger', 'mac-leopard', 'mac-snowleopard'):
- 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',
- 'mac', 'test_expectations.txt')
-
- def _skipped_file_paths(self):
- # FIXME: This method will need to be made work for non-mac
- # platforms and moved into base.Port.
- skipped_files = []
- if self._name in ('mac-tiger', 'mac-leopard', 'mac-snowleopard'):
- skipped_files.append(os.path.join(
- self._webkit_baseline_path(self._name), 'Skipped'))
- skipped_files.append(os.path.join(self._webkit_baseline_path('mac'),
- 'Skipped'))
- return skipped_files
-
- def test_platform_name(self):
- return 'mac' + self.version()
-
- def version(self):
- os_version_string = platform.mac_ver()[0] # e.g. "10.5.6"
- if not os_version_string:
- return '-leopard'
- release_version = int(os_version_string.split('.')[1])
- if release_version == 4:
- return '-tiger'
- elif release_version == 5:
- return '-leopard'
- elif release_version == 6:
- return '-snowleopard'
- return ''
-
- def _build_java_test_support(self):
- java_tests_path = os.path.join(self.layout_tests_dir(), "java")
- build_java = ["/usr/bin/make", "-C", java_tests_path]
- if self._executive.run_command(build_java, return_exit_code=True):
- _log.error("Failed to build Java support files: %s" % build_java)
- return False
- return True
-
- def _check_port_build(self):
- return self._build_java_test_support()
-
- def _tests_for_other_platforms(self):
- # The original run-webkit-tests builds up a "whitelist" of tests to
- # run, and passes that to DumpRenderTree. new-run-webkit-tests assumes
- # we run *all* tests and test_expectations.txt functions as a
- # blacklist.
- # FIXME: This list could be dynamic based on platform name and
- # pushed into base.Port.
- return [
- "platform/chromium",
- "platform/gtk",
- "platform/qt",
- "platform/win",
- ]
-
- def _path_to_apache_config_file(self):
- return os.path.join(self.layout_tests_dir(), 'http', 'conf',
- 'apache2-httpd.conf')
-
- # FIXME: This doesn't have anything to do with WebKit.
- def _shut_down_http_server(self, server_pid):
- """Shut down the lighttpd web server. Blocks until it's fully
- shut down.
-
- Args:
- server_pid: The process ID of the running server.
- """
- # server_pid is not set when "http_server.py stop" is run manually.
- if server_pid is None:
- # FIXME: This isn't ideal, since it could conflict with
- # lighttpd processes not started by http_server.py,
- # but good enough for now.
- self._executive.kill_all('httpd')
- else:
- try:
- os.kill(server_pid, signal.SIGTERM)
- # FIXME: Maybe throw in a SIGKILL just to be sure?
- except OSError:
- # Sometimes we get a bad PID (e.g. from a stale httpd.pid
- # file), so if kill fails on the given PID, just try to
- # 'killall' web servers.
- self._shut_down_http_server(None)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/mac_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/mac_unittest.py
deleted file mode 100644
index d383a4c..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/mac_unittest.py
+++ /dev/null
@@ -1,81 +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.
-
-import StringIO
-import sys
-import unittest
-
-import mac
-import port_testcase
-
-
-class MacTest(port_testcase.PortTestCase):
- def make_port(self, options=port_testcase.mock_options):
- if sys.platform != 'darwin':
- return None
- port_obj = mac.MacPort(options=options)
- port_obj._options.results_directory = port_obj.results_directory()
- port_obj._options.configuration = 'Release'
- return port_obj
-
- def test_skipped_file_paths(self):
- port = self.make_port()
- if not port:
- return
- skipped_paths = port._skipped_file_paths()
- # FIXME: _skipped_file_paths should return WebKit-relative paths.
- # So to make it unit testable, we strip the WebKit directory from the path.
- relative_paths = [path[len(port.path_from_webkit_base()):] for path in skipped_paths]
- self.assertEqual(relative_paths, ['LayoutTests/platform/mac-leopard/Skipped', 'LayoutTests/platform/mac/Skipped'])
-
- example_skipped_file = u"""
-# <rdar://problem/5647952> fast/events/mouseout-on-window.html needs mac DRT to issue mouse out events
-fast/events/mouseout-on-window.html
-
-# <rdar://problem/5643675> window.scrollTo scrolls a window with no scrollbars
-fast/events/attempt-scroll-with-no-scrollbars.html
-
-# see bug <rdar://problem/5646437> REGRESSION (r28015): svg/batik/text/smallFonts fails
-svg/batik/text/smallFonts.svg
-"""
- example_skipped_tests = [
- "fast/events/mouseout-on-window.html",
- "fast/events/attempt-scroll-with-no-scrollbars.html",
- "svg/batik/text/smallFonts.svg",
- ]
-
- def test_skipped_file_paths(self):
- port = self.make_port()
- if not port:
- return
- skipped_file = StringIO.StringIO(self.example_skipped_file)
- self.assertEqual(port._tests_from_skipped_file(skipped_file), self.example_skipped_tests)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/port_testcase.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/port_testcase.py
deleted file mode 100644
index c4b36ac..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/port_testcase.py
+++ /dev/null
@@ -1,97 +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.
-
-"""Unit testing base class for Port implementations."""
-
-import os
-import tempfile
-import unittest
-
-from webkitpy.tool import mocktool
-mock_options = mocktool.MockOptions(results_directory='layout-test-results',
- use_apache=True,
- configuration='Release')
-
-# FIXME: This should be used for all ports, not just WebKit Mac. See
-# https://bugs.webkit.org/show_bug.cgi?id=50043 .
-
-class PortTestCase(unittest.TestCase):
- """Tests the WebKit port implementation."""
- def make_port(self, options=mock_options):
- """Override in subclass."""
- raise NotImplementedError()
-
- def test_driver_cmd_line(self):
- port = self.make_port()
- if not port:
- return
- self.assertTrue(len(port.driver_cmd_line()))
-
- def test_http_server(self):
- port = self.make_port()
- if not port:
- return
- port.start_http_server()
- port.stop_http_server()
-
- def test_image_diff(self):
- port = self.make_port()
- if not port:
- return
-
- # FIXME: not sure why this shouldn't always be True
- #self.assertTrue(port.check_image_diff())
- if not port.check_image_diff():
- return
-
- dir = port.layout_tests_dir()
- file1 = os.path.join(dir, 'fast', 'css', 'button_center.png')
- fh1 = file(file1)
- contents1 = fh1.read()
- file2 = os.path.join(dir, 'fast', 'css',
- 'remove-shorthand-expected.png')
- fh2 = file(file2)
- contents2 = fh2.read()
- tmpfile = tempfile.mktemp()
-
- self.assertFalse(port.diff_image(contents1, contents1))
- self.assertTrue(port.diff_image(contents1, contents2))
-
- self.assertTrue(port.diff_image(contents1, contents2, tmpfile))
- fh1.close()
- fh2.close()
- # FIXME: this may not be being written?
- # self.assertTrue(os.path.exists(tmpfile))
- # os.remove(tmpfile)
-
- def test_websocket_server(self):
- port = self.make_port()
- if not port:
- return
- port.start_websocket_server()
- port.stop_websocket_server()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/qt.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/qt.py
deleted file mode 100644
index af94acc..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/qt.py
+++ /dev/null
@@ -1,119 +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 Google name 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.
-
-"""QtWebKit implementation of the Port interface."""
-
-import logging
-import os
-import signal
-import sys
-
-import webkit
-
-from webkitpy.layout_tests.port.webkit import WebKitPort
-
-_log = logging.getLogger("webkitpy.layout_tests.port.qt")
-
-
-class QtPort(WebKitPort):
- """QtWebKit implementation of the Port class."""
-
- def __init__(self, **kwargs):
- kwargs.setdefault('port_name', 'qt')
- WebKitPort.__init__(self, **kwargs)
-
- def baseline_search_path(self):
- port_names = []
- if sys.platform == 'linux2':
- port_names.append("qt-linux")
- elif sys.platform in ('win32', 'cygwin'):
- port_names.append("qt-win")
- elif sys.platform == 'darwin':
- port_names.append("qt-mac")
- port_names.append("qt")
- 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.
- # This really need to be automated.
- return [
- "platform/chromium",
- "platform/win",
- "platform/gtk",
- "platform/mac",
- ]
-
- def _path_to_apache_config_file(self):
- # FIXME: This needs to detect the distribution and change config files.
- return os.path.join(self.layout_tests_dir(), 'http', 'conf',
- 'apache2-debian-httpd.conf')
-
- def _shut_down_http_server(self, server_pid):
- """Shut down the httpd web server. Blocks until it's fully
- shut down.
-
- Args:
- server_pid: The process ID of the running server.
- """
- # server_pid is not set when "http_server.py stop" is run manually.
- if server_pid is None:
- # FIXME: This isn't ideal, since it could conflict with
- # lighttpd processes not started by http_server.py,
- # but good enough for now.
- self._executive.kill_all('apache2')
- else:
- try:
- os.kill(server_pid, signal.SIGTERM)
- # TODO(mmoss) Maybe throw in a SIGKILL just to be sure?
- except OSError:
- # Sometimes we get a bad PID (e.g. from a stale httpd.pid
- # file), so if kill fails on the given PID, just try to
- # 'killall' web servers.
- self._shut_down_http_server(None)
-
- def _build_driver(self):
- # The Qt port builds DRT as part of the main build step
- return True
-
- def _path_to_driver(self):
- return self._build_path('bin/DumpRenderTree')
-
- def _path_to_image_diff(self):
- return self._build_path('bin/ImageDiff')
-
- def _path_to_webcore_library(self):
- return self._build_path('lib/libQtWebKit.so')
-
- def _runtime_feature_list(self):
- return None
-
- def setup_environ_for_server(self):
- env = webkit.WebKitPort.setup_environ_for_server(self)
- env['QTWEBKIT_PLUGIN_PATH'] = self._build_path('lib/plugins')
- return env
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/server_process.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/server_process.py
deleted file mode 100644
index 5a0a40c..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/server_process.py
+++ /dev/null
@@ -1,225 +0,0 @@
-#!/usr/bin/env 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 Google name 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.
-
-"""Package that implements the ServerProcess wrapper class"""
-
-import logging
-import os
-import select
-import signal
-import subprocess
-import sys
-import time
-if sys.platform != 'win32':
- import fcntl
-
-from webkitpy.common.system.executive import Executive
-
-_log = logging.getLogger("webkitpy.layout_tests.port.server_process")
-
-
-class ServerProcess:
- """This class provides a wrapper around a subprocess that
- implements a simple request/response usage model. The primary benefit
- is that reading responses takes a timeout, so that we don't ever block
- indefinitely. The class also handles transparently restarting processes
- as necessary to keep issuing commands."""
-
- def __init__(self, port_obj, name, cmd, env=None, executive=Executive()):
- self._port = port_obj
- self._name = name
- self._cmd = cmd
- self._env = env
- self._reset()
- self._executive = executive
-
- def _reset(self):
- self._proc = None
- self._output = ''
- self.crashed = False
- self.timed_out = False
- self.error = ''
-
- def _start(self):
- if self._proc:
- raise ValueError("%s already running" % self._name)
- self._reset()
- # close_fds is a workaround for http://bugs.python.org/issue2320
- close_fds = sys.platform not in ('win32', 'cygwin')
- self._proc = subprocess.Popen(self._cmd, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- close_fds=close_fds,
- env=self._env)
- fd = self._proc.stdout.fileno()
- fl = fcntl.fcntl(fd, fcntl.F_GETFL)
- fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
- fd = self._proc.stderr.fileno()
- fl = fcntl.fcntl(fd, fcntl.F_GETFL)
- fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
-
- def handle_interrupt(self):
- """This routine checks to see if the process crashed or exited
- because of a keyboard interrupt and raises KeyboardInterrupt
- accordingly."""
- if self.crashed:
- # This is hex code 0xc000001d, which is used for abrupt
- # termination. This happens if we hit ctrl+c from the prompt
- # and we happen to be waiting on the DumpRenderTree.
- # sdoyon: Not sure for which OS and in what circumstances the
- # above code is valid. What works for me under Linux to detect
- # ctrl+c is for the subprocess returncode to be negative
- # SIGINT. And that agrees with the subprocess documentation.
- if (-1073741510 == self._proc.returncode or
- - signal.SIGINT == self._proc.returncode):
- raise KeyboardInterrupt
- return
-
- def poll(self):
- """Check to see if the underlying process is running; returns None
- if it still is (wrapper around subprocess.poll)."""
- if self._proc:
- # poll() is not threadsafe and can throw OSError due to:
- # http://bugs.python.org/issue1731717
- return self._proc.poll()
- return None
-
- def write(self, input):
- """Write a request to the subprocess. The subprocess is (re-)start()'ed
- if is not already running."""
- if not self._proc:
- self._start()
- self._proc.stdin.write(input)
-
- def read_line(self, timeout):
- """Read a single line from the subprocess, waiting until the deadline.
- If the deadline passes, the call times out. Note that even if the
- subprocess has crashed or the deadline has passed, if there is output
- pending, it will be returned.
-
- Args:
- timeout: floating-point number of seconds the call is allowed
- to block for. A zero or negative number will attempt to read
- any existing data, but will not block. There is no way to
- block indefinitely.
- Returns:
- output: data returned, if any. If no data is available and the
- call times out or crashes, an empty string is returned. Note
- that the returned string includes the newline ('\n')."""
- return self._read(timeout, size=0)
-
- def read(self, timeout, size):
- """Attempts to read size characters from the subprocess, waiting until
- the deadline passes. If the deadline passes, any available data will be
- returned. Note that even if the deadline has passed or if the
- subprocess has crashed, any available data will still be returned.
-
- Args:
- timeout: floating-point number of seconds the call is allowed
- to block for. A zero or negative number will attempt to read
- any existing data, but will not block. There is no way to
- block indefinitely.
- size: amount of data to read. Must be a postive integer.
- Returns:
- output: data returned, if any. If no data is available, an empty
- string is returned.
- """
- if size <= 0:
- raise ValueError('ServerProcess.read() called with a '
- 'non-positive size: %d ' % size)
- return self._read(timeout, size)
-
- def _read(self, timeout, size):
- """Internal routine that actually does the read."""
- index = -1
- out_fd = self._proc.stdout.fileno()
- err_fd = self._proc.stderr.fileno()
- select_fds = (out_fd, err_fd)
- deadline = time.time() + timeout
- while not self.timed_out and not self.crashed:
- # poll() is not threadsafe and can throw OSError due to:
- # http://bugs.python.org/issue1731717
- if self._proc.poll() != None:
- self.crashed = True
- self.handle_interrupt()
-
- now = time.time()
- if now > deadline:
- self.timed_out = True
-
- # Check to see if we have any output we can return.
- if size and len(self._output) >= size:
- index = size
- elif size == 0:
- index = self._output.find('\n') + 1
-
- if index > 0 or self.crashed or self.timed_out:
- output = self._output[0:index]
- self._output = self._output[index:]
- return output
-
- # Nope - wait for more data.
- (read_fds, write_fds, err_fds) = select.select(select_fds, [],
- select_fds,
- deadline - now)
- try:
- if out_fd in read_fds:
- self._output += self._proc.stdout.read()
- if err_fd in read_fds:
- self.error += self._proc.stderr.read()
- except IOError, e:
- pass
-
- def stop(self):
- """Stop (shut down) the subprocess), if it is running."""
- pid = self._proc.pid
- self._proc.stdin.close()
- self._proc.stdout.close()
- if self._proc.stderr:
- self._proc.stderr.close()
- if sys.platform not in ('win32', 'cygwin'):
- # Closing stdin/stdout/stderr hangs sometimes on OS X,
- # (see restart(), above), and anyway we don't want to hang
- # the harness if DumpRenderTree is buggy, so we wait a couple
- # seconds to give DumpRenderTree a chance to clean up, but then
- # force-kill the process if necessary.
- KILL_TIMEOUT = 3.0
- timeout = time.time() + KILL_TIMEOUT
- # poll() is not threadsafe and can throw OSError due to:
- # http://bugs.python.org/issue1731717
- while self._proc.poll() is None and time.time() < timeout:
- time.sleep(0.1)
- # poll() is not threadsafe and can throw OSError due to:
- # http://bugs.python.org/issue1731717
- if self._proc.poll() is None:
- _log.warning('stopping %s timed out, killing it' %
- self._name)
- self._executive.kill_process(self._proc.pid)
- _log.warning('killed')
- self._reset()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py
deleted file mode 100644
index 8e27f35..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py
+++ /dev/null
@@ -1,312 +0,0 @@
-#!/usr/bin/env 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 Google name 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.
-
-"""Dummy Port implementation used for testing."""
-from __future__ import with_statement
-
-import codecs
-import fnmatch
-import os
-import sys
-import time
-
-from webkitpy.layout_tests.layout_package import test_output
-
-import base
-
-
-# This sets basic expectations for a test. Each individual expectation
-# can be overridden by a keyword argument in TestList.add().
-class TestInstance:
- def __init__(self, name):
- self.name = name
- self.base = name[(name.rfind("/") + 1):name.rfind(".html")]
- self.crash = False
- self.exception = False
- self.hang = False
- self.keyboard = False
- self.error = ''
- self.timeout = False
- self.actual_text = self.base + '-txt\n'
- self.actual_checksum = self.base + '-checksum\n'
- self.actual_image = self.base + '-png\n'
- self.expected_text = self.actual_text
- self.expected_checksum = self.actual_checksum
- self.expected_image = self.actual_image
-
-
-# This is an in-memory list of tests, what we want them to produce, and
-# what we want to claim are the expected results.
-class TestList:
- def __init__(self, port):
- self.port = port
- self.tests = {}
-
- def add(self, name, **kwargs):
- test = TestInstance(name)
- for key, value in kwargs.items():
- test.__dict__[key] = value
- self.tests[name] = test
-
- def keys(self):
- return self.tests.keys()
-
- def __contains__(self, item):
- return item in self.tests
-
- def __getitem__(self, item):
- return self.tests[item]
-
-
-class TestPort(base.Port):
- """Test implementation of the Port interface."""
-
- def __init__(self, **kwargs):
- base.Port.__init__(self, **kwargs)
- tests = TestList(self)
- tests.add('passes/image.html')
- tests.add('passes/text.html')
- tests.add('failures/expected/checksum.html',
- actual_checksum='checksum_fail-checksum')
- tests.add('failures/expected/crash.html', crash=True)
- tests.add('failures/expected/exception.html', exception=True)
- tests.add('failures/expected/timeout.html', timeout=True)
- tests.add('failures/expected/hang.html', hang=True)
- tests.add('failures/expected/missing_text.html',
- expected_text=None)
- tests.add('failures/expected/image.html',
- actual_image='image_fail-png',
- expected_image='image-png')
- tests.add('failures/expected/image_checksum.html',
- actual_checksum='image_checksum_fail-checksum',
- actual_image='image_checksum_fail-png')
- tests.add('failures/expected/keyboard.html',
- keyboard=True)
- tests.add('failures/expected/missing_check.html',
- expected_checksum=None)
- tests.add('failures/expected/missing_image.html',
- expected_image=None)
- tests.add('failures/expected/missing_text.html',
- expected_text=None)
- tests.add('failures/expected/text.html',
- actual_text='text_fail-png')
- tests.add('failures/unexpected/text-image-checksum.html',
- actual_text='text-image-checksum_fail-txt',
- actual_checksum='text-image-checksum_fail-checksum')
- tests.add('http/tests/passes/text.html')
- tests.add('http/tests/ssl/text.html')
- tests.add('passes/error.html', error='stuff going to stderr')
- tests.add('passes/image.html')
- tests.add('passes/platform_image.html')
- tests.add('passes/text.html')
- tests.add('websocket/tests/passes/text.html')
- self._tests = tests
-
- def baseline_path(self):
- return os.path.join(self.layout_tests_dir(), 'platform',
- self.name() + self.version())
-
- def baseline_search_path(self):
- return [self.baseline_path()]
-
- def check_build(self, needs_http):
- return True
-
- def diff_image(self, expected_contents, actual_contents,
- diff_filename=None):
- diffed = actual_contents != expected_contents
- if diffed and diff_filename:
- with codecs.open(diff_filename, "w", "utf-8") as diff_fh:
- diff_fh.write("< %s\n---\n> %s\n" %
- (expected_contents, actual_contents))
- return diffed
-
- def expected_checksum(self, test):
- test = self.relative_test_filename(test)
- return self._tests[test].expected_checksum
-
- def expected_image(self, test):
- test = self.relative_test_filename(test)
- return self._tests[test].expected_image
-
- def expected_text(self, test):
- test = self.relative_test_filename(test)
- text = self._tests[test].expected_text
- if not text:
- text = ''
- return text
-
- def tests(self, paths):
- # Test the idea of port-specific overrides for test lists. Also
- # keep in memory to speed up the test harness.
- if not paths:
- paths = ['*']
-
- matched_tests = []
- for p in paths:
- if self.path_isdir(p):
- matched_tests.extend(fnmatch.filter(self._tests.keys(), p + '*'))
- else:
- matched_tests.extend(fnmatch.filter(self._tests.keys(), p))
- layout_tests_dir = self.layout_tests_dir()
- return set([os.path.join(layout_tests_dir, p) for p in matched_tests])
-
- def path_exists(self, path):
- # used by test_expectations.py and printing.py
- rpath = self.relative_test_filename(path)
- if rpath in self._tests:
- return True
- if self.path_isdir(rpath):
- return True
- if rpath.endswith('-expected.txt'):
- test = rpath.replace('-expected.txt', '.html')
- return (test in self._tests and
- self._tests[test].expected_text)
- if rpath.endswith('-expected.checksum'):
- test = rpath.replace('-expected.checksum', '.html')
- return (test in self._tests and
- self._tests[test].expected_checksum)
- if rpath.endswith('-expected.png'):
- test = rpath.replace('-expected.png', '.html')
- return (test in self._tests and
- self._tests[test].expected_image)
- return False
-
- def layout_tests_dir(self):
- return self.path_from_webkit_base('WebKitTools', 'Scripts',
- 'webkitpy', 'layout_tests', 'data')
-
- def path_isdir(self, path):
- # Used by test_expectations.py
- #
- # We assume that a path is a directory if we have any tests that
- # whose prefix matches the path plus a directory modifier.
- if path[-1] != '/':
- path += '/'
- return any([t.startswith(path) for t in self._tests.keys()])
-
- def test_dirs(self):
- return ['passes', 'failures']
-
- def name(self):
- return self._name
-
- def _path_to_wdiff(self):
- return None
-
- def results_directory(self):
- return '/tmp/' + self.get_option('results_directory')
-
- def setup_test_run(self):
- pass
-
- def create_driver(self, worker_number):
- return TestDriver(self, worker_number)
-
- def start_http_server(self):
- pass
-
- def start_websocket_server(self):
- pass
-
- def stop_http_server(self):
- pass
-
- def stop_websocket_server(self):
- pass
-
- def test_expectations(self):
- """Returns the test expectations for this port.
-
- Basically this string should contain the equivalent of a
- test_expectations file. See test_expectations.py for more details."""
- return """
-WONTFIX : failures/expected/checksum.html = IMAGE
-WONTFIX : failures/expected/crash.html = CRASH
-// This one actually passes because the checksums will match.
-WONTFIX : failures/expected/image.html = PASS
-WONTFIX : failures/expected/image_checksum.html = IMAGE
-WONTFIX : failures/expected/missing_check.html = MISSING PASS
-WONTFIX : failures/expected/missing_image.html = MISSING PASS
-WONTFIX : failures/expected/missing_text.html = MISSING PASS
-WONTFIX : failures/expected/text.html = TEXT
-WONTFIX : failures/expected/timeout.html = TIMEOUT
-WONTFIX SKIP : failures/expected/hang.html = TIMEOUT
-WONTFIX SKIP : failures/expected/keyboard.html = CRASH
-WONTFIX SKIP : failures/expected/exception.html = CRASH
-"""
-
- def test_base_platform_names(self):
- return ('mac', 'win')
-
- def test_platform_name(self):
- return 'mac'
-
- def test_platform_names(self):
- return self.test_base_platform_names()
-
- def test_platform_name_to_name(self, test_platform_name):
- return test_platform_name
-
- def version(self):
- return ''
-
-
-class TestDriver(base.Driver):
- """Test/Dummy implementation of the DumpRenderTree interface."""
-
- def __init__(self, port, worker_number):
- self._port = port
-
- def cmd_line(self):
- return ['None']
-
- def poll(self):
- return True
-
- def run_test(self, test_input):
- start_time = time.time()
- test_name = self._port.relative_test_filename(test_input.filename)
- test = self._port._tests[test_name]
- if test.keyboard:
- raise KeyboardInterrupt
- if test.exception:
- raise ValueError('exception from ' + test_name)
- if test.hang:
- time.sleep((float(test_input.timeout) * 4) / 1000.0)
- return test_output.TestOutput(test.actual_text, test.actual_image,
- test.actual_checksum, test.crash,
- time.time() - start_time, test.timeout,
- test.error)
-
- def start(self):
- pass
-
- def stop(self):
- pass
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/test_files.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/test_files.py
deleted file mode 100644
index 2c0a7b6..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/test_files.py
+++ /dev/null
@@ -1,128 +0,0 @@
-#!/usr/bin/env 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.
-
-"""This module is used to find all of the layout test files used by
-run-webkit-tests. It exposes one public function - find() -
-which takes an optional list of paths. If a list is passed in, the returned
-list of test files is constrained to those found under the paths passed in,
-i.e. calling find(["LayoutTests/fast"]) will only return files
-under that directory."""
-
-import glob
-import os
-import time
-
-from webkitpy.common.system import logutils
-
-
-_log = logutils.get_logger(__file__)
-
-
-# When collecting test cases, we include any file with these extensions.
-_supported_file_extensions = set(['.html', '.shtml', '.xml', '.xhtml', '.xhtmlmp', '.pl',
- '.php', '.svg'])
-# When collecting test cases, skip these directories
-_skipped_directories = set(['.svn', '_svn', 'resources', 'script-tests'])
-
-
-def find(port, paths):
- """Finds the set of tests under port.layout_tests_dir().
-
- Args:
- paths: a list of command line paths relative to the layout_tests_dir()
- to limit the search to. glob patterns are ok.
- """
- gather_start_time = time.time()
- paths_to_walk = set()
- # if paths is empty, provide a pre-defined list.
- if paths:
- _log.debug("Gathering tests from: %s relative to %s" % (paths, port.layout_tests_dir()))
- for path in paths:
- # If there's an * in the name, assume it's a glob pattern.
- path = os.path.join(port.layout_tests_dir(), path)
- if path.find('*') > -1:
- filenames = glob.glob(path)
- paths_to_walk.update(filenames)
- else:
- paths_to_walk.add(path)
- else:
- _log.debug("Gathering tests from: %s" % port.layout_tests_dir())
- paths_to_walk.add(port.layout_tests_dir())
-
- # Now walk all the paths passed in on the command line and get filenames
- test_files = set()
- for path in paths_to_walk:
- if os.path.isfile(path) and _is_test_file(path):
- test_files.add(os.path.normpath(path))
- continue
-
- for root, dirs, files in os.walk(path):
- # Don't walk skipped directories or their sub-directories.
- if os.path.basename(root) in _skipped_directories:
- del dirs[:]
- continue
- # This copy and for-in is slightly inefficient, but
- # the extra walk avoidance consistently shaves .5 seconds
- # off of total walk() time on my MacBook Pro.
- for directory in dirs[:]:
- if directory in _skipped_directories:
- dirs.remove(directory)
-
- for filename in files:
- if _is_test_file(filename):
- filename = os.path.join(root, filename)
- filename = os.path.normpath(filename)
- test_files.add(filename)
-
- gather_time = time.time() - gather_start_time
- _log.debug("Test gathering took %f seconds" % gather_time)
-
- return test_files
-
-
-def _has_supported_extension(filename):
- """Return true if filename is one of the file extensions we want to run a
- test on."""
- extension = os.path.splitext(filename)[1]
- return extension in _supported_file_extensions
-
-
-def _is_reference_html_file(filename):
- """Return true if the filename points to a reference HTML file."""
- if (filename.endswith('-expected.html') or
- filename.endswith('-expected-mismatch.html')):
- _log.warn("Reftests are not supported - ignoring %s" % filename)
- return True
- return False
-
-
-def _is_test_file(filename):
- """Return true if the filename points to a test file."""
- return (_has_supported_extension(filename) and
- not _is_reference_html_file(filename))
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/test_files_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/test_files_unittest.py
deleted file mode 100644
index 83525c8..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/test_files_unittest.py
+++ /dev/null
@@ -1,75 +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.
-
-import os
-import unittest
-
-import base
-import test_files
-
-
-class TestFilesTest(unittest.TestCase):
- def test_find_no_paths_specified(self):
- port = base.Port()
- layout_tests_dir = port.layout_tests_dir()
- port.layout_tests_dir = lambda: os.path.join(layout_tests_dir,
- 'fast', 'html')
- tests = test_files.find(port, [])
- self.assertNotEqual(tests, 0)
-
- def test_find_one_test(self):
- port = base.Port()
- # This is just a test picked at random but known to exist.
- tests = test_files.find(port, ['fast/html/keygen.html'])
- self.assertEqual(len(tests), 1)
-
- def test_find_glob(self):
- port = base.Port()
- tests = test_files.find(port, ['fast/html/key*'])
- self.assertEqual(len(tests), 1)
-
- def test_find_with_skipped_directories(self):
- port = base.Port()
- tests = port.tests('userscripts')
- self.assertTrue('userscripts/resources/frame1.html' not in tests)
-
- def test_find_with_skipped_directories_2(self):
- port = base.Port()
- tests = test_files.find(port, ['userscripts/resources'])
- self.assertEqual(tests, set([]))
-
- def test_is_test_file(self):
- self.assertTrue(test_files._is_test_file('foo.html'))
- self.assertTrue(test_files._is_test_file('foo.shtml'))
- self.assertFalse(test_files._is_test_file('foo.png'))
- self.assertFalse(test_files._is_test_file('foo-expected.html'))
- self.assertFalse(test_files._is_test_file('foo-expected-mismatch.html'))
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py
deleted file mode 100644
index 09be833..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py
+++ /dev/null
@@ -1,504 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2010 Google Inc. All rights reserved.
-# Copyright (C) 2010 Gabor Rapcsanyi <rgabor@inf.u-szeged.hu>, University of Szeged
-#
-# 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 Google name 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.
-
-"""WebKit implementations of the Port interface."""
-
-
-from __future__ import with_statement
-
-import codecs
-import logging
-import os
-import re
-import shutil
-import signal
-import sys
-import time
-import webbrowser
-import operator
-import tempfile
-import shutil
-
-import webkitpy.common.system.ospath as ospath
-import webkitpy.layout_tests.layout_package.test_output as test_output
-import webkitpy.layout_tests.port.base as base
-import webkitpy.layout_tests.port.server_process as server_process
-
-_log = logging.getLogger("webkitpy.layout_tests.port.webkit")
-
-
-class WebKitPort(base.Port):
- """WebKit implementation of the Port class."""
-
- def __init__(self, **kwargs):
- base.Port.__init__(self, **kwargs)
- self._cached_apache_path = None
-
- # FIXME: disable pixel tests until they are run by default on the
- # build machines.
- self.set_option_default('pixel_tests', False)
-
- def baseline_path(self):
- return self._webkit_baseline_path(self._name)
-
- def baseline_search_path(self):
- return [self._webkit_baseline_path(self._name)]
-
- def path_to_test_expectations_file(self):
- return os.path.join(self._webkit_baseline_path(self._name),
- 'test_expectations.txt')
-
- # Only needed by ports which maintain versioned test expectations (like mac-tiger vs. mac-leopard)
- def version(self):
- return ''
-
- def _build_driver(self):
- configuration = self.get_option('configuration')
- return self._config.build_dumprendertree(configuration)
-
- def _check_driver(self):
- driver_path = self._path_to_driver()
- if not os.path.exists(driver_path):
- _log.error("DumpRenderTree was not found at %s" % driver_path)
- return False
- return True
-
- def check_build(self, needs_http):
- if self.get_option('build') and not self._build_driver():
- return False
- if not self._check_driver():
- return False
- if self.get_option('pixel_tests'):
- if not self.check_image_diff():
- return False
- if not self._check_port_build():
- return False
- return True
-
- def _check_port_build(self):
- # Ports can override this method to do additional checks.
- return True
-
- def check_image_diff(self, override_step=None, logging=True):
- image_diff_path = self._path_to_image_diff()
- if not os.path.exists(image_diff_path):
- _log.error("ImageDiff was not found at %s" % image_diff_path)
- return False
- return True
-
- def diff_image(self, expected_contents, actual_contents,
- diff_filename=None):
- """Return True if the two files are different. Also write a delta
- image of the two images into |diff_filename| if it is not None."""
-
- # Handle the case where the test didn't actually generate an image.
- if not actual_contents:
- return True
-
- sp = self._diff_image_request(expected_contents, actual_contents)
- return self._diff_image_reply(sp, diff_filename)
-
- def _diff_image_request(self, expected_contents, actual_contents):
- # FIXME: use self.get_option('tolerance') and
- # self.set_option_default('tolerance', 0.1) once that behaves correctly
- # with default values.
- if self.get_option('tolerance') is not None:
- tolerance = self.get_option('tolerance')
- else:
- tolerance = 0.1
- command = [self._path_to_image_diff(), '--tolerance', str(tolerance)]
- sp = server_process.ServerProcess(self, 'ImageDiff', command)
-
- sp.write('Content-Length: %d\n%sContent-Length: %d\n%s' %
- (len(actual_contents), actual_contents,
- len(expected_contents), expected_contents))
-
- return sp
-
- def _diff_image_reply(self, sp, diff_filename):
- timeout = 2.0
- deadline = time.time() + timeout
- output = sp.read_line(timeout)
- while not sp.timed_out and not sp.crashed and output:
- if output.startswith('Content-Length'):
- m = re.match('Content-Length: (\d+)', output)
- content_length = int(m.group(1))
- timeout = deadline - time.time()
- output = sp.read(timeout, content_length)
- break
- elif output.startswith('diff'):
- break
- else:
- timeout = deadline - time.time()
- output = sp.read_line(deadline)
-
- result = True
- if output.startswith('diff'):
- m = re.match('diff: (.+)% (passed|failed)', output)
- if m.group(2) == 'passed':
- result = False
- elif output and diff_filename:
- with open(diff_filename, 'w') as file:
- file.write(output)
- elif sp.timed_out:
- _log.error("ImageDiff timed out")
- elif sp.crashed:
- _log.error("ImageDiff crashed")
- sp.stop()
- return result
-
- def results_directory(self):
- # Results are store relative to the built products to make it easy
- # to have multiple copies of webkit checked out and built.
- return self._build_path(self.get_option('results_directory'))
-
- def setup_test_run(self):
- # This port doesn't require any specific configuration.
- pass
-
- def create_driver(self, worker_number):
- return WebKitDriver(self, worker_number)
-
- def test_base_platform_names(self):
- # At the moment we don't use test platform names, but we have
- # to return something.
- return ('mac', 'win')
-
- def _tests_for_other_platforms(self):
- raise NotImplementedError('WebKitPort._tests_for_other_platforms')
- # The original run-webkit-tests builds up a "whitelist" of tests to
- # run, and passes that to DumpRenderTree. new-run-webkit-tests assumes
- # we run *all* tests and test_expectations.txt functions as a
- # blacklist.
- # FIXME: This list could be dynamic based on platform name and
- # pushed into base.Port.
- return [
- "platform/chromium",
- "platform/gtk",
- "platform/qt",
- "platform/win",
- ]
-
- def _runtime_feature_list(self):
- """Return the supported features of DRT. If a port doesn't support
- this DRT switch, it has to override this method to return None"""
- driver_path = self._path_to_driver()
- feature_list = ' '.join(os.popen(driver_path + " --print-supported-features 2>&1").readlines())
- if "SupportedFeatures:" in feature_list:
- return feature_list
- return None
-
- def _supported_symbol_list(self):
- """Return the supported symbols of WebCore."""
- webcore_library_path = self._path_to_webcore_library()
- if not webcore_library_path:
- return None
- symbol_list = ' '.join(os.popen("nm " + webcore_library_path).readlines())
- return symbol_list
-
- def _directories_for_features(self):
- """Return the supported feature dictionary. The keys are the
- features and the values are the directories in lists."""
- directories_for_features = {
- "Accelerated Compositing": ["compositing"],
- "3D Rendering": ["animations/3d", "transforms/3d"],
- }
- return directories_for_features
-
- def _directories_for_symbols(self):
- """Return the supported feature dictionary. The keys are the
- symbols and the values are the directories in lists."""
- directories_for_symbol = {
- "MathMLElement": ["mathml"],
- "GraphicsLayer": ["compositing"],
- "WebCoreHas3DRendering": ["animations/3d", "transforms/3d"],
- "WebGLShader": ["fast/canvas/webgl", "compositing/webgl", "http/tests/canvas/webgl"],
- "WMLElement": ["http/tests/wml", "fast/wml", "wml"],
- "parseWCSSInputProperty": ["fast/wcss"],
- "isXHTMLMPDocument": ["fast/xhtmlmp"],
- }
- return directories_for_symbol
-
- def _skipped_tests_for_unsupported_features(self):
- """Return the directories of unsupported tests. Search for the
- symbols in the symbol_list, if found add the corresponding
- directories to the skipped directory list."""
- feature_list = self._runtime_feature_list()
- directories = self._directories_for_features()
-
- # if DRT feature detection not supported
- if not feature_list:
- feature_list = self._supported_symbol_list()
- directories = self._directories_for_symbols()
-
- if not feature_list:
- return []
-
- skipped_directories = [directories[feature]
- for feature in directories.keys()
- if feature not in feature_list]
- return reduce(operator.add, skipped_directories)
-
- def _tests_for_disabled_features(self):
- # FIXME: This should use the feature detection from
- # webkitperl/features.pm to match run-webkit-tests.
- # For now we hard-code a list of features known to be disabled on
- # the Mac platform.
- disabled_feature_tests = [
- "fast/xhtmlmp",
- "http/tests/wml",
- "mathml",
- "wml",
- ]
- # FIXME: webarchive tests expect to read-write from
- # -expected.webarchive files instead of .txt files.
- # This script doesn't know how to do that yet, so pretend they're
- # just "disabled".
- webarchive_tests = [
- "webarchive",
- "svg/webarchive",
- "http/tests/webarchive",
- "svg/custom/image-with-prefix-in-webarchive.svg",
- ]
- unsupported_feature_tests = self._skipped_tests_for_unsupported_features()
- return disabled_feature_tests + webarchive_tests + unsupported_feature_tests
-
- def _tests_from_skipped_file(self, skipped_file):
- tests_to_skip = []
- for line in skipped_file.readlines():
- line = line.strip()
- if line.startswith('#') or not len(line):
- continue
- tests_to_skip.append(line)
- return tests_to_skip
-
- def _skipped_file_paths(self):
- return [os.path.join(self._webkit_baseline_path(self._name),
- 'Skipped')]
-
- def _expectations_from_skipped_files(self):
- tests_to_skip = []
- for filename in self._skipped_file_paths():
- if not os.path.exists(filename):
- _log.warn("Failed to open Skipped file: %s" % filename)
- continue
- with codecs.open(filename, "r", "utf-8") as skipped_file:
- tests_to_skip.extend(self._tests_from_skipped_file(skipped_file))
- return tests_to_skip
-
- def test_expectations(self):
- # The WebKit mac port uses a combination of a test_expectations file
- # and 'Skipped' files.
- expectations_path = self.path_to_test_expectations_file()
- with codecs.open(expectations_path, "r", "utf-8") as file:
- return file.read() + self._skips()
-
- def _skips(self):
- # Each Skipped file contains a list of files
- # or directories to be skipped during the test run. The total list
- # of tests to skipped is given by the contents of the generic
- # Skipped file found in platform/X plus a version-specific file
- # found in platform/X-version. Duplicate entries are allowed.
- # This routine reads those files and turns contents into the
- # format expected by test_expectations.
-
- tests_to_skip = self.skipped_layout_tests()
- skip_lines = map(lambda test_path: "BUG_SKIPPED SKIP : %s = FAIL" %
- test_path, tests_to_skip)
- return "\n".join(skip_lines)
-
- def skipped_layout_tests(self):
- # Use a set to allow duplicates
- tests_to_skip = set(self._expectations_from_skipped_files())
- tests_to_skip.update(self._tests_for_other_platforms())
- tests_to_skip.update(self._tests_for_disabled_features())
- return tests_to_skip
-
- def test_platform_name(self):
- return self._name + self.version()
-
- def test_platform_names(self):
- return self.test_base_platform_names() + (
- 'mac-tiger', 'mac-leopard', 'mac-snowleopard')
-
- def _build_path(self, *comps):
- return self._filesystem.join(self._config.build_directory(
- self.get_option('configuration')), *comps)
-
- def _path_to_driver(self):
- return self._build_path('DumpRenderTree')
-
- def _path_to_webcore_library(self):
- return None
-
- def _path_to_helper(self):
- return None
-
- def _path_to_image_diff(self):
- return self._build_path('ImageDiff')
-
- def _path_to_wdiff(self):
- # FIXME: This does not exist on a default Mac OS X Leopard install.
- return 'wdiff'
-
- def _path_to_apache(self):
- if not self._cached_apache_path:
- # The Apache binary path can vary depending on OS and distribution
- # See http://wiki.apache.org/httpd/DistrosDefaultLayout
- for path in ["/usr/sbin/httpd", "/usr/sbin/apache2"]:
- if os.path.exists(path):
- self._cached_apache_path = path
- break
-
- if not self._cached_apache_path:
- _log.error("Could not find apache. Not installed or unknown path.")
-
- return self._cached_apache_path
-
-
-class WebKitDriver(base.Driver):
- """WebKit implementation of the DumpRenderTree interface."""
-
- def __init__(self, port, worker_number):
- self._worker_number = worker_number
- self._port = port
- self._driver_tempdir = tempfile.mkdtemp(prefix='DumpRenderTree-')
-
- def __del__(self):
- shutil.rmtree(self._driver_tempdir)
-
- def cmd_line(self):
- cmd = self._command_wrapper(self._port.get_option('wrapper'))
- cmd += [self._port._path_to_driver(), '-']
-
- if self._port.get_option('pixel_tests'):
- cmd.append('--pixel-tests')
-
- if self._port.get_option('use_drt'):
- if self._port.get_option('accelerated_compositing'):
- cmd.append('--enable-accelerated-compositing')
-
- if self._port.get_option('accelerated_2d_canvas'):
- cmd.append('--enable-accelerated-2d-canvas')
-
- return cmd
-
- def start(self):
- environment = self._port.setup_environ_for_server()
- environment['DYLD_FRAMEWORK_PATH'] = self._port._build_path()
- environment['DUMPRENDERTREE_TEMP'] = self._driver_tempdir
- self._server_process = server_process.ServerProcess(self._port,
- "DumpRenderTree", self.cmd_line(), environment)
-
- def poll(self):
- return self._server_process.poll()
-
- def restart(self):
- self._server_process.stop()
- self._server_process.start()
- return
-
- # FIXME: This function is huge.
- def run_test(self, test_input):
- uri = self._port.filename_to_uri(test_input.filename)
- if uri.startswith("file:///"):
- command = uri[7:]
- else:
- command = uri
-
- if test_input.image_hash:
- command += "'" + test_input.image_hash
- command += "\n"
-
- start_time = time.time()
- self._server_process.write(command)
-
- have_seen_content_type = False
- actual_image_hash = None
- output = str() # Use a byte array for output, even though it should be UTF-8.
- image = str()
-
- timeout = int(test_input.timeout) / 1000.0
- deadline = time.time() + timeout
- line = self._server_process.read_line(timeout)
- while (not self._server_process.timed_out
- and not self._server_process.crashed
- and line.rstrip() != "#EOF"):
- if (line.startswith('Content-Type:') and not
- have_seen_content_type):
- have_seen_content_type = True
- else:
- # Note: Text output from DumpRenderTree is always UTF-8.
- # However, some tests (e.g. webarchives) spit out binary
- # data instead of text. So to make things simple, we
- # always treat the output as binary.
- output += line
- line = self._server_process.read_line(timeout)
- timeout = deadline - time.time()
-
- # Now read a second block of text for the optional image data
- remaining_length = -1
- HASH_HEADER = 'ActualHash: '
- LENGTH_HEADER = 'Content-Length: '
- line = self._server_process.read_line(timeout)
- while (not self._server_process.timed_out
- and not self._server_process.crashed
- and line.rstrip() != "#EOF"):
- if line.startswith(HASH_HEADER):
- actual_image_hash = line[len(HASH_HEADER):].strip()
- elif line.startswith('Content-Type:'):
- pass
- elif line.startswith(LENGTH_HEADER):
- timeout = deadline - time.time()
- content_length = int(line[len(LENGTH_HEADER):])
- image = self._server_process.read(timeout, content_length)
- timeout = deadline - time.time()
- line = self._server_process.read_line(timeout)
-
- error_lines = self._server_process.error.splitlines()
- # FIXME: This is a hack. It is unclear why sometimes
- # we do not get any error lines from the server_process
- # probably we are not flushing stderr.
- if error_lines and error_lines[-1] == "#EOF":
- error_lines.pop() # Remove the expected "#EOF"
- error = "\n".join(error_lines)
- # FIXME: This seems like the wrong section of code to be doing
- # this reset in.
- self._server_process.error = ""
- return test_output.TestOutput(output, image, actual_image_hash,
- self._server_process.crashed,
- time.time() - start_time,
- self._server_process.timed_out,
- error)
-
- def stop(self):
- if self._server_process:
- self._server_process.stop()
- self._server_process = None
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit_unittest.py
deleted file mode 100644
index 7b68310..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit_unittest.py
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2010 Gabor Rapcsanyi <rgabor@inf.u-szeged.hu>, University of Szeged
-#
-# 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.
-#
-# THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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 unittest
-
-from webkitpy.layout_tests.port.webkit import WebKitPort
-
-
-class TestWebKitPort(WebKitPort):
- def __init__(self, symbol_list=None, feature_list=None):
- self.symbol_list = symbol_list
- self.feature_list = feature_list
-
- def _runtime_feature_list(self):
- return self.feature_list
-
- def _supported_symbol_list(self):
- return self.symbol_list
-
- def _tests_for_other_platforms(self):
- return ["media", ]
-
- def _tests_for_disabled_features(self):
- return ["accessibility", ]
-
- def _skipped_file_paths(self):
- return []
-
-class WebKitPortTest(unittest.TestCase):
-
- def test_skipped_directories_for_symbols(self):
- supported_symbols = ["GraphicsLayer", "WebCoreHas3DRendering", "isXHTMLMPDocument", "fooSymbol"]
- expected_directories = set(["mathml", "fast/canvas/webgl", "compositing/webgl", "http/tests/canvas/webgl", "http/tests/wml", "fast/wml", "wml", "fast/wcss"])
- result_directories = set(TestWebKitPort(supported_symbols, None)._skipped_tests_for_unsupported_features())
- self.assertEqual(result_directories, expected_directories)
-
- def test_skipped_directories_for_features(self):
- supported_features = ["Accelerated Compositing", "Foo Feature"]
- expected_directories = set(["animations/3d", "transforms/3d"])
- result_directories = set(TestWebKitPort(None, supported_features)._skipped_tests_for_unsupported_features())
- self.assertEqual(result_directories, expected_directories)
-
- def test_skipped_layout_tests(self):
- self.assertEqual(TestWebKitPort(None, None).skipped_layout_tests(),
- set(["media", "accessibility"]))
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py
deleted file mode 100644
index 926bc04..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py
+++ /dev/null
@@ -1,257 +0,0 @@
-#!/usr/bin/env 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.
-
-"""A class to help start/stop the PyWebSocket server used by layout tests."""
-
-
-from __future__ import with_statement
-
-import codecs
-import logging
-import optparse
-import os
-import subprocess
-import sys
-import tempfile
-import time
-import urllib
-
-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")
-
-_WS_LOG_PREFIX = 'pywebsocket.ws.log-'
-_WSS_LOG_PREFIX = 'pywebsocket.wss.log-'
-
-_DEFAULT_WS_PORT = 8880
-_DEFAULT_WSS_PORT = 9323
-
-
-def url_is_alive(url):
- """Checks to see if we get an http response from |url|.
- We poll the url 20 times with a 0.5 second delay. If we don't
- get a reply in that time, we give up and assume the httpd
- didn't start properly.
-
- Args:
- url: The URL to check.
- Return:
- True if the url is alive.
- """
- sleep_time = 0.5
- wait_time = 10
- while wait_time > 0:
- try:
- response = urllib.urlopen(url)
- # Server is up and responding.
- return True
- except IOError:
- pass
- # Wait for sleep_time before trying again.
- wait_time -= sleep_time
- time.sleep(sleep_time)
-
- return False
-
-
-class PyWebSocketNotStarted(Exception):
- pass
-
-
-class PyWebSocketNotFound(Exception):
- pass
-
-
-class PyWebSocket(http_server.Lighttpd):
-
- def __init__(self, port_obj, output_dir, port=_DEFAULT_WS_PORT,
- root=None, use_tls=False,
- 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)
- self._output_dir = output_dir
- self._process = None
- self._port = port
- self._root = root
- self._use_tls = use_tls
- self._private_key = self._pem_file
- self._certificate = self._pem_file
- if self._port:
- self._port = int(self._port)
- if self._use_tls:
- self._server_name = 'PyWebSocket(Secure)'
- else:
- self._server_name = 'PyWebSocket'
- self._pidfile = pidfile
- self._wsout = None
-
- # Webkit tests
- if self._root:
- self._layout_tests = os.path.abspath(self._root)
- self._web_socket_tests = os.path.abspath(
- os.path.join(self._root, 'http', 'tests',
- 'websocket', 'tests'))
- else:
- try:
- self._layout_tests = self._port_obj.layout_tests_dir()
- self._web_socket_tests = os.path.join(self._layout_tests,
- 'http', 'tests', 'websocket', 'tests')
- except:
- self._web_socket_tests = None
-
- def start(self):
- if not self._web_socket_tests:
- _log.info('No need to start %s server.' % self._server_name)
- return
- if self.is_running():
- raise PyWebSocketNotStarted('%s is already running.' %
- self._server_name)
-
- time_str = time.strftime('%d%b%Y-%H%M%S')
- if self._use_tls:
- log_prefix = _WSS_LOG_PREFIX
- else:
- log_prefix = _WS_LOG_PREFIX
- log_file_name = log_prefix + time_str
-
- # Remove old log files. We only need to keep the last ones.
- self.remove_log_files(self._output_dir, log_prefix)
-
- error_log = os.path.join(self._output_dir, log_file_name + "-err.txt")
-
- output_log = os.path.join(self._output_dir, log_file_name + "-out.txt")
- self._wsout = codecs.open(output_log, "w", "utf-8")
-
- python_interp = sys.executable
- pywebsocket_base = os.path.join(
- os.path.dirname(os.path.dirname(os.path.dirname(
- os.path.abspath(__file__)))), 'thirdparty',
- 'autoinstalled', 'pywebsocket')
- pywebsocket_script = os.path.join(pywebsocket_base, 'mod_pywebsocket',
- 'standalone.py')
- start_cmd = [
- python_interp, '-u', pywebsocket_script,
- '--server-host', '127.0.0.1',
- '--port', str(self._port),
- '--document-root', os.path.join(self._layout_tests, 'http', 'tests'),
- '--scan-dir', self._web_socket_tests,
- '--cgi-paths', '/websocket/tests',
- '--log-file', error_log,
- ]
-
- handler_map_file = os.path.join(self._web_socket_tests,
- 'handler_map.txt')
- if os.path.exists(handler_map_file):
- _log.debug('Using handler_map_file: %s' % handler_map_file)
- start_cmd.append('--websock-handlers-map-file')
- start_cmd.append(handler_map_file)
- else:
- _log.warning('No handler_map_file found')
-
- if self._use_tls:
- start_cmd.extend(['-t', '-k', self._private_key,
- '-c', self._certificate])
-
- env = self._port_obj.setup_environ_for_server()
- env['PYTHONPATH'] = (pywebsocket_base + os.path.pathsep +
- env.get('PYTHONPATH', ''))
-
- _log.debug('Starting %s server on %d.' % (
- self._server_name, self._port))
- _log.debug('cmdline: %s' % ' '.join(start_cmd))
- # FIXME: We should direct this call through Executive for testing.
- # Note: Not thread safe: http://bugs.python.org/issue2320
- self._process = subprocess.Popen(start_cmd,
- stdin=open(os.devnull, 'r'),
- stdout=self._wsout,
- stderr=subprocess.STDOUT,
- env=env)
-
- if self._use_tls:
- url = 'https'
- else:
- url = 'http'
- url = url + '://127.0.0.1:%d/' % self._port
- if not url_is_alive(url):
- if self._process.returncode == None:
- # FIXME: We should use a non-static Executive for easier
- # testing.
- Executive().kill_process(self._process.pid)
- with codecs.open(output_log, "r", "utf-8") as fp:
- for line in fp:
- _log.error(line)
- raise PyWebSocketNotStarted(
- 'Failed to start %s server on port %s.' %
- (self._server_name, self._port))
-
- # Our process terminated already
- if self._process.returncode != None:
- raise PyWebSocketNotStarted(
- 'Failed to start %s server.' % self._server_name)
- if self._pidfile:
- with codecs.open(self._pidfile, "w", "ascii") as file:
- file.write("%d" % self._process.pid)
-
- def stop(self, force=False):
- if not force and not self.is_running():
- return
-
- pid = None
- if self._process:
- pid = self._process.pid
- elif self._pidfile:
- with codecs.open(self._pidfile, "r", "ascii") as file:
- pid = int(file.read().strip())
-
- if not pid:
- raise PyWebSocketNotFound(
- 'Failed to find %s server pid.' % self._server_name)
-
- _log.debug('Shutting down %s server %d.' % (self._server_name, pid))
- # FIXME: We should use a non-static Executive for easier testing.
- Executive().kill_process(pid)
-
- if self._process:
- # wait() is not threadsafe and can throw OSError due to:
- # http://bugs.python.org/issue1731717
- self._process.wait()
- self._process = None
-
- if self._wsout:
- self._wsout.close()
- self._wsout = None
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/win.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/win.py
deleted file mode 100644
index 9e30155..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/win.py
+++ /dev/null
@@ -1,75 +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 Google name 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.
-
-"""WebKit Win implementation of the Port interface."""
-
-import logging
-import os
-
-from webkitpy.layout_tests.port.webkit import WebKitPort
-
-_log = logging.getLogger("webkitpy.layout_tests.port.win")
-
-
-class WinPort(WebKitPort):
- """WebKit Win implementation of the Port class."""
-
- def __init__(self, **kwargs):
- kwargs.setdefault('port_name', 'win')
- WebKitPort.__init__(self, **kwargs)
-
- 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.
- # This really need to be automated.
- return [
- "platform/chromium",
- "platform/gtk",
- "platform/qt",
- "platform/mac",
- ]
-
- def _path_to_apache_config_file(self):
- return os.path.join(self.layout_tests_dir(), 'http', 'conf',
- 'cygwin-httpd.conf')
-
- def _shut_down_http_server(self, server_pid):
- """Shut down the httpd web server. Blocks until it's fully
- shut down.
-
- Args:
- server_pid: The process ID of the running server.
- """
- # Looks like we ignore server_pid.
- # Copy/pasted from chromium-win.
- self._executive.kill_all("httpd.exe")
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py
deleted file mode 100644
index 55c4558..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests.py
+++ /dev/null
@@ -1,966 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Rebaselining tool that automatically produces baselines for all platforms.
-
-The script does the following for each platform specified:
- 1. Compile a list of tests that need rebaselining.
- 2. Download test result archive from buildbot for the platform.
- 3. Extract baselines from the archive file for all identified files.
- 4. Add new baselines to SVN repository.
- 5. For each test that has been rebaselined, remove this platform option from
- the test in test_expectation.txt. If no other platforms remain after
- removal, delete the rebaselined test from the file.
-
-At the end, the script generates a html that compares old and new baselines.
-"""
-
-from __future__ import with_statement
-
-import codecs
-import copy
-import logging
-import optparse
-import os
-import re
-import shutil
-import subprocess
-import sys
-import tempfile
-import time
-import urllib
-import zipfile
-
-from webkitpy.common.system import path
-from webkitpy.common.system import user
-from webkitpy.common.system.executive import Executive, ScriptError
-import webkitpy.common.checkout.scm as scm
-
-import port
-from layout_package import test_expectations
-
-_log = logging.getLogger("webkitpy.layout_tests."
- "rebaseline_chromium_webkit_tests")
-
-BASELINE_SUFFIXES = ['.txt', '.png', '.checksum']
-REBASELINE_PLATFORM_ORDER = ['mac', 'win', 'win-xp', 'win-vista', 'linux']
-ARCHIVE_DIR_NAME_DICT = {'win': 'Webkit_Win',
- 'win-vista': 'webkit-dbg-vista',
- 'win-xp': 'Webkit_Win',
- 'mac': 'Webkit_Mac10_5',
- 'linux': 'webkit-rel-linux64',
- 'win-canary': 'webkit-rel-webkit-org',
- 'win-vista-canary': 'webkit-dbg-vista',
- 'win-xp-canary': 'webkit-rel-webkit-org',
- 'mac-canary': 'webkit-rel-mac-webkit-org',
- 'linux-canary': 'webkit-rel-linux-webkit-org'}
-
-
-def log_dashed_string(text, platform, logging_level=logging.INFO):
- """Log text message with dashes on both sides."""
-
- msg = text
- if platform:
- msg += ': ' + platform
- if len(msg) < 78:
- dashes = '-' * ((78 - len(msg)) / 2)
- msg = '%s %s %s' % (dashes, msg, dashes)
-
- if logging_level == logging.ERROR:
- _log.error(msg)
- elif logging_level == logging.WARNING:
- _log.warn(msg)
- else:
- _log.info(msg)
-
-
-def setup_html_directory(html_directory):
- """Setup the directory to store html results.
-
- All html related files are stored in the "rebaseline_html" subdirectory.
-
- Args:
- html_directory: parent directory that stores the rebaselining results.
- If None, a temp directory is created.
-
- Returns:
- the directory that stores the html related rebaselining results.
- """
-
- if not html_directory:
- html_directory = tempfile.mkdtemp()
- elif not os.path.exists(html_directory):
- os.mkdir(html_directory)
-
- html_directory = os.path.join(html_directory, 'rebaseline_html')
- _log.info('Html directory: "%s"', html_directory)
-
- if os.path.exists(html_directory):
- shutil.rmtree(html_directory, True)
- _log.info('Deleted file at html directory: "%s"', html_directory)
-
- if not os.path.exists(html_directory):
- os.mkdir(html_directory)
- return html_directory
-
-
-def get_result_file_fullpath(html_directory, baseline_filename, platform,
- result_type):
- """Get full path of the baseline result file.
-
- Args:
- html_directory: directory that stores the html related files.
- baseline_filename: name of the baseline file.
- platform: win, linux or mac
- result_type: type of the baseline result: '.txt', '.png'.
-
- Returns:
- Full path of the baseline file for rebaselining result comparison.
- """
-
- base, ext = os.path.splitext(baseline_filename)
- result_filename = '%s-%s-%s%s' % (base, platform, result_type, ext)
- fullpath = os.path.join(html_directory, result_filename)
- _log.debug(' Result file full path: "%s".', fullpath)
- return fullpath
-
-
-class Rebaseliner(object):
- """Class to produce new baselines for a given platform."""
-
- REVISION_REGEX = r'<a href=\"(\d+)/\">'
-
- def __init__(self, running_port, target_port, platform, options):
- """
- Args:
- running_port: the Port the script is running on.
- target_port: the Port the script uses to find port-specific
- configuration information like the test_expectations.txt
- file location and the list of test platforms.
- platform: the test platform to rebaseline
- options: the command-line options object."""
- self._platform = platform
- self._options = options
- self._port = running_port
- self._target_port = target_port
- self._rebaseline_port = port.get(
- self._target_port.test_platform_name_to_name(platform), options)
- self._rebaselining_tests = []
- self._rebaselined_tests = []
-
- # Create tests and expectations helper which is used to:
- # -. compile list of tests that need rebaselining.
- # -. update the tests in test_expectations file after rebaseline
- # is done.
- expectations_str = self._rebaseline_port.test_expectations()
- self._test_expectations = \
- test_expectations.TestExpectations(self._rebaseline_port,
- None,
- expectations_str,
- self._platform,
- False,
- False)
- self._scm = scm.default_scm()
-
- def run(self, backup):
- """Run rebaseline process."""
-
- log_dashed_string('Compiling rebaselining tests', self._platform)
- if not self._compile_rebaselining_tests():
- return True
-
- log_dashed_string('Downloading archive', self._platform)
- archive_file = self._download_buildbot_archive()
- _log.info('')
- if not archive_file:
- _log.error('No archive found.')
- return False
-
- log_dashed_string('Extracting and adding new baselines',
- self._platform)
- if not self._extract_and_add_new_baselines(archive_file):
- return False
-
- log_dashed_string('Updating rebaselined tests in file',
- self._platform)
- self._update_rebaselined_tests_in_file(backup)
- _log.info('')
-
- if len(self._rebaselining_tests) != len(self._rebaselined_tests):
- _log.warning('NOT ALL TESTS THAT NEED REBASELINING HAVE BEEN '
- 'REBASELINED.')
- _log.warning(' Total tests needing rebaselining: %d',
- len(self._rebaselining_tests))
- _log.warning(' Total tests rebaselined: %d',
- len(self._rebaselined_tests))
- return False
-
- _log.warning('All tests needing rebaselining were successfully '
- 'rebaselined.')
-
- return True
-
- def get_rebaselining_tests(self):
- return self._rebaselining_tests
-
- def _compile_rebaselining_tests(self):
- """Compile list of tests that need rebaselining for the platform.
-
- Returns:
- List of tests that need rebaselining or
- None if there is no such test.
- """
-
- self._rebaselining_tests = \
- self._test_expectations.get_rebaselining_failures()
- if not self._rebaselining_tests:
- _log.warn('No tests found that need rebaselining.')
- return None
-
- _log.info('Total number of tests needing rebaselining '
- 'for "%s": "%d"', self._platform,
- len(self._rebaselining_tests))
-
- test_no = 1
- for test in self._rebaselining_tests:
- _log.info(' %d: %s', test_no, test)
- test_no += 1
-
- return self._rebaselining_tests
-
- def _get_latest_revision(self, url):
- """Get the latest layout test revision number from buildbot.
-
- Args:
- url: Url to retrieve layout test revision numbers.
-
- Returns:
- latest revision or
- None on failure.
- """
-
- _log.debug('Url to retrieve revision: "%s"', url)
-
- f = urllib.urlopen(url)
- content = f.read()
- f.close()
-
- revisions = re.findall(self.REVISION_REGEX, content)
- if not revisions:
- _log.error('Failed to find revision, content: "%s"', content)
- return None
-
- revisions.sort(key=int)
- _log.info('Latest revision: "%s"', revisions[len(revisions) - 1])
- return revisions[len(revisions) - 1]
-
- def _get_archive_dir_name(self, platform, webkit_canary):
- """Get name of the layout test archive directory.
-
- Returns:
- Directory name or
- None on failure
- """
-
- if webkit_canary:
- platform += '-canary'
-
- if platform in ARCHIVE_DIR_NAME_DICT:
- return ARCHIVE_DIR_NAME_DICT[platform]
- else:
- _log.error('Cannot find platform key %s in archive '
- 'directory name dictionary', platform)
- return None
-
- def _get_archive_url(self):
- """Generate the url to download latest layout test archive.
-
- Returns:
- Url to download archive or
- None on failure
- """
-
- if self._options.force_archive_url:
- return self._options.force_archive_url
-
- dir_name = self._get_archive_dir_name(self._platform,
- self._options.webkit_canary)
- if not dir_name:
- return None
-
- _log.debug('Buildbot platform dir name: "%s"', dir_name)
-
- url_base = '%s/%s/' % (self._options.archive_url, dir_name)
- latest_revision = self._get_latest_revision(url_base)
- if latest_revision is None or latest_revision <= 0:
- return None
- archive_url = ('%s%s/layout-test-results.zip' % (url_base,
- latest_revision))
- _log.info('Archive url: "%s"', archive_url)
- return archive_url
-
- def _download_buildbot_archive(self):
- """Download layout test archive file from buildbot.
-
- Returns:
- True if download succeeded or
- False otherwise.
- """
-
- url = self._get_archive_url()
- if url is None:
- return None
-
- fn = urllib.urlretrieve(url)[0]
- _log.info('Archive downloaded and saved to file: "%s"', fn)
- return fn
-
- def _extract_and_add_new_baselines(self, archive_file):
- """Extract new baselines from archive and add them to SVN repository.
-
- Args:
- archive_file: full path to the archive file.
-
- Returns:
- List of tests that have been rebaselined or
- None on failure.
- """
-
- zip_file = zipfile.ZipFile(archive_file, 'r')
- zip_namelist = zip_file.namelist()
-
- _log.debug('zip file namelist:')
- for name in zip_namelist:
- _log.debug(' ' + name)
-
- platform = self._rebaseline_port.test_platform_name_to_name(
- self._platform)
- _log.debug('Platform dir: "%s"', platform)
-
- test_no = 1
- self._rebaselined_tests = []
- for test in self._rebaselining_tests:
- _log.info('Test %d: %s', test_no, test)
-
- found = False
- scm_error = False
- test_basename = os.path.splitext(test)[0]
- for suffix in BASELINE_SUFFIXES:
- archive_test_name = ('layout-test-results/%s-actual%s' %
- (test_basename, suffix))
- _log.debug(' Archive test file name: "%s"',
- archive_test_name)
- if not archive_test_name in zip_namelist:
- _log.info(' %s file not in archive.', suffix)
- continue
-
- found = True
- _log.info(' %s file found in archive.', suffix)
-
- # Extract new baseline from archive and save it to a temp file.
- data = zip_file.read(archive_test_name)
- temp_fd, temp_name = tempfile.mkstemp(suffix)
- f = os.fdopen(temp_fd, 'wb')
- f.write(data)
- f.close()
-
- expected_filename = '%s-expected%s' % (test_basename, suffix)
- expected_fullpath = os.path.join(
- self._rebaseline_port.baseline_path(), expected_filename)
- expected_fullpath = os.path.normpath(expected_fullpath)
- _log.debug(' Expected file full path: "%s"',
- expected_fullpath)
-
- # TODO(victorw): for now, the rebaselining tool checks whether
- # or not THIS baseline is duplicate and should be skipped.
- # We could improve the tool to check all baselines in upper
- # and lower
- # levels and remove all duplicated baselines.
- if self._is_dup_baseline(temp_name,
- expected_fullpath,
- test,
- suffix,
- self._platform):
- os.remove(temp_name)
- self._delete_baseline(expected_fullpath)
- continue
-
- # Create the new baseline directory if it doesn't already
- # exist.
- self._port.maybe_make_directory(
- os.path.dirname(expected_fullpath))
-
- shutil.move(temp_name, expected_fullpath)
-
- if 0 != self._scm.add(expected_fullpath, return_exit_code=True):
- # FIXME: print detailed diagnose messages
- scm_error = True
- elif suffix != '.checksum':
- self._create_html_baseline_files(expected_fullpath)
-
- if not found:
- _log.warn(' No new baselines found in archive.')
- else:
- if scm_error:
- _log.warn(' Failed to add baselines to your repository.')
- else:
- _log.info(' Rebaseline succeeded.')
- self._rebaselined_tests.append(test)
-
- test_no += 1
-
- zip_file.close()
- os.remove(archive_file)
-
- return self._rebaselined_tests
-
- def _is_dup_baseline(self, new_baseline, baseline_path, test, suffix,
- platform):
- """Check whether a baseline is duplicate and can fallback to same
- baseline for another platform. For example, if a test has same
- baseline on linux and windows, then we only store windows
- baseline and linux baseline will fallback to the windows version.
-
- Args:
- expected_filename: baseline expectation file name.
- test: test name.
- suffix: file suffix of the expected results, including dot;
- e.g. '.txt' or '.png'.
- platform: baseline platform 'mac', 'win' or 'linux'.
-
- Returns:
- True if the baseline is unnecessary.
- False otherwise.
- """
- test_filepath = os.path.join(self._target_port.layout_tests_dir(),
- test)
- all_baselines = self._rebaseline_port.expected_baselines(
- test_filepath, suffix, True)
- for (fallback_dir, fallback_file) in all_baselines:
- if fallback_dir and fallback_file:
- fallback_fullpath = os.path.normpath(
- os.path.join(fallback_dir, fallback_file))
- if fallback_fullpath.lower() != baseline_path.lower():
- with codecs.open(new_baseline, "r",
- None) as file_handle1:
- new_output = file_handle1.read()
- with codecs.open(fallback_fullpath, "r",
- None) as file_handle2:
- fallback_output = file_handle2.read()
- is_image = baseline_path.lower().endswith('.png')
- if not self._diff_baselines(new_output, fallback_output,
- is_image):
- _log.info(' Found same baseline at %s',
- fallback_fullpath)
- return True
- else:
- return False
-
- return False
-
- def _diff_baselines(self, output1, output2, is_image):
- """Check whether two baselines are different.
-
- Args:
- output1, output2: contents of the baselines to compare.
-
- Returns:
- True if two files are different or have different extensions.
- False otherwise.
- """
-
- if is_image:
- return self._port.diff_image(output1, output2, None)
- else:
- return self._port.compare_text(output1, output2)
-
- def _delete_baseline(self, filename):
- """Remove the file from repository and delete it from disk.
-
- Args:
- filename: full path of the file to delete.
- """
-
- if not filename or not os.path.isfile(filename):
- return
- self._scm.delete(filename)
-
- def _update_rebaselined_tests_in_file(self, backup):
- """Update the rebaselined tests in test expectations file.
-
- Args:
- backup: if True, backup the original test expectations file.
-
- Returns:
- no
- """
-
- if self._rebaselined_tests:
- new_expectations = (
- self._test_expectations.remove_platform_from_expectations(
- self._rebaselined_tests, self._platform))
- path = self._target_port.path_to_test_expectations_file()
- if backup:
- date_suffix = time.strftime('%Y%m%d%H%M%S',
- time.localtime(time.time()))
- backup_file = ('%s.orig.%s' % (path, date_suffix))
- if os.path.exists(backup_file):
- os.remove(backup_file)
- _log.info('Saving original file to "%s"', backup_file)
- os.rename(path, backup_file)
- # FIXME: What encoding are these files?
- # Or is new_expectations always a byte array?
- with open(path, "w") as file:
- file.write(new_expectations)
- # self._scm.add(path)
- else:
- _log.info('No test was rebaselined so nothing to remove.')
-
- def _create_html_baseline_files(self, baseline_fullpath):
- """Create baseline files (old, new and diff) in html directory.
-
- The files are used to compare the rebaselining results.
-
- Args:
- baseline_fullpath: full path of the expected baseline file.
- """
-
- if not baseline_fullpath or not os.path.exists(baseline_fullpath):
- return
-
- # Copy the new baseline to html directory for result comparison.
- baseline_filename = os.path.basename(baseline_fullpath)
- new_file = get_result_file_fullpath(self._options.html_directory,
- baseline_filename, self._platform,
- 'new')
- shutil.copyfile(baseline_fullpath, new_file)
- _log.info(' Html: copied new baseline file from "%s" to "%s".',
- baseline_fullpath, new_file)
-
- # Get the old baseline from the repository and save to the html directory.
- try:
- output = self._scm.show_head(baseline_fullpath)
- except ScriptError, e:
- _log.info(e)
- output = ""
-
- if (not output) or (output.upper().rstrip().endswith(
- 'NO SUCH FILE OR DIRECTORY')):
- _log.info(' No base file: "%s"', baseline_fullpath)
- return
- base_file = get_result_file_fullpath(self._options.html_directory,
- baseline_filename, self._platform,
- 'old')
- # We should be using an explicit encoding here.
- with open(base_file, "wb") as file:
- file.write(output)
- _log.info(' Html: created old baseline file: "%s".',
- base_file)
-
- # Get the diff between old and new baselines and save to the html dir.
- if baseline_filename.upper().endswith('.TXT'):
- output = self._scm.diff_for_file(baseline_fullpath, log=_log)
- if output:
- diff_file = get_result_file_fullpath(
- self._options.html_directory, baseline_filename,
- self._platform, 'diff')
- with open(diff_file, 'wb') as file:
- file.write(output)
- _log.info(' Html: created baseline diff file: "%s".',
- diff_file)
-
-
-class HtmlGenerator(object):
- """Class to generate rebaselining result comparison html."""
-
- HTML_REBASELINE = ('<html>'
- '<head>'
- '<style>'
- 'body {font-family: sans-serif;}'
- '.mainTable {background: #666666;}'
- '.mainTable td , .mainTable th {background: white;}'
- '.detail {margin-left: 10px; margin-top: 3px;}'
- '</style>'
- '<title>Rebaselining Result Comparison (%(time)s)'
- '</title>'
- '</head>'
- '<body>'
- '<h2>Rebaselining Result Comparison (%(time)s)</h2>'
- '%(body)s'
- '</body>'
- '</html>')
- HTML_NO_REBASELINING_TESTS = (
- '<p>No tests found that need rebaselining.</p>')
- HTML_TABLE_TEST = ('<table class="mainTable" cellspacing=1 cellpadding=5>'
- '%s</table><br>')
- HTML_TR_TEST = ('<tr>'
- '<th style="background-color: #CDECDE; border-bottom: '
- '1px solid black; font-size: 18pt; font-weight: bold" '
- 'colspan="5">'
- '<a href="%s">%s</a>'
- '</th>'
- '</tr>')
- HTML_TEST_DETAIL = ('<div class="detail">'
- '<tr>'
- '<th width="100">Baseline</th>'
- '<th width="100">Platform</th>'
- '<th width="200">Old</th>'
- '<th width="200">New</th>'
- '<th width="150">Difference</th>'
- '</tr>'
- '%s'
- '</div>')
- HTML_TD_NOLINK = '<td align=center><a>%s</a></td>'
- HTML_TD_LINK = '<td align=center><a href="%(uri)s">%(name)s</a></td>'
- HTML_TD_LINK_IMG = ('<td><a href="%(uri)s">'
- '<img style="width: 200" src="%(uri)s" /></a></td>')
- HTML_TR = '<tr>%s</tr>'
-
- def __init__(self, target_port, options, platforms, rebaselining_tests,
- executive):
- self._html_directory = options.html_directory
- self._target_port = target_port
- self._platforms = platforms
- self._rebaselining_tests = rebaselining_tests
- self._executive = executive
- self._html_file = os.path.join(options.html_directory,
- 'rebaseline.html')
-
- def abspath_to_uri(self, filename):
- """Converts an absolute path to a file: URI."""
- return path.abspath_to_uri(filename, self._executive)
-
- def generate_html(self):
- """Generate html file for rebaselining result comparison."""
-
- _log.info('Generating html file')
-
- html_body = ''
- if not self._rebaselining_tests:
- html_body += self.HTML_NO_REBASELINING_TESTS
- else:
- tests = list(self._rebaselining_tests)
- tests.sort()
-
- test_no = 1
- for test in tests:
- _log.info('Test %d: %s', test_no, test)
- html_body += self._generate_html_for_one_test(test)
-
- html = self.HTML_REBASELINE % ({'time': time.asctime(),
- 'body': html_body})
- _log.debug(html)
-
- with codecs.open(self._html_file, "w", "utf-8") as file:
- file.write(html)
-
- _log.info('Baseline comparison html generated at "%s"',
- self._html_file)
-
- def show_html(self):
- """Launch the rebaselining html in brwoser."""
-
- _log.info('Launching html: "%s"', self._html_file)
- user.User().open_url(self._html_file)
- _log.info('Html launched.')
-
- def _generate_baseline_links(self, test_basename, suffix, platform):
- """Generate links for baseline results (old, new and diff).
-
- Args:
- test_basename: base filename of the test
- suffix: baseline file suffixes: '.txt', '.png'
- platform: win, linux or mac
-
- Returns:
- html links for showing baseline results (old, new and diff)
- """
-
- baseline_filename = '%s-expected%s' % (test_basename, suffix)
- _log.debug(' baseline filename: "%s"', baseline_filename)
-
- new_file = get_result_file_fullpath(self._html_directory,
- baseline_filename, platform, 'new')
- _log.info(' New baseline file: "%s"', new_file)
- if not os.path.exists(new_file):
- _log.info(' No new baseline file: "%s"', new_file)
- return ''
-
- old_file = get_result_file_fullpath(self._html_directory,
- baseline_filename, platform, 'old')
- _log.info(' Old baseline file: "%s"', old_file)
- if suffix == '.png':
- html_td_link = self.HTML_TD_LINK_IMG
- else:
- html_td_link = self.HTML_TD_LINK
-
- links = ''
- if os.path.exists(old_file):
- links += html_td_link % {
- 'uri': self.abspath_to_uri(old_file),
- 'name': baseline_filename}
- else:
- _log.info(' No old baseline file: "%s"', old_file)
- links += self.HTML_TD_NOLINK % ''
-
- links += html_td_link % {'uri': self.abspath_to_uri(new_file),
- 'name': baseline_filename}
-
- diff_file = get_result_file_fullpath(self._html_directory,
- baseline_filename, platform,
- 'diff')
- _log.info(' Baseline diff file: "%s"', diff_file)
- if os.path.exists(diff_file):
- links += html_td_link % {'uri': self.abspath_to_uri(diff_file),
- 'name': 'Diff'}
- else:
- _log.info(' No baseline diff file: "%s"', diff_file)
- links += self.HTML_TD_NOLINK % ''
-
- return links
-
- def _generate_html_for_one_test(self, test):
- """Generate html for one rebaselining test.
-
- Args:
- test: layout test name
-
- Returns:
- html that compares baseline results for the test.
- """
-
- test_basename = os.path.basename(os.path.splitext(test)[0])
- _log.info(' basename: "%s"', test_basename)
- rows = []
- for suffix in BASELINE_SUFFIXES:
- if suffix == '.checksum':
- continue
-
- _log.info(' Checking %s files', suffix)
- for platform in self._platforms:
- links = self._generate_baseline_links(test_basename, suffix,
- platform)
- if links:
- row = self.HTML_TD_NOLINK % self._get_baseline_result_type(
- suffix)
- row += self.HTML_TD_NOLINK % platform
- row += links
- _log.debug(' html row: %s', row)
-
- rows.append(self.HTML_TR % row)
-
- if rows:
- test_path = os.path.join(self._target_port.layout_tests_dir(),
- test)
- html = self.HTML_TR_TEST % (self.abspath_to_uri(test_path), test)
- html += self.HTML_TEST_DETAIL % ' '.join(rows)
-
- _log.debug(' html for test: %s', html)
- return self.HTML_TABLE_TEST % html
-
- return ''
-
- def _get_baseline_result_type(self, suffix):
- """Name of the baseline result type."""
-
- if suffix == '.png':
- return 'Pixel'
- elif suffix == '.txt':
- return 'Render Tree'
- else:
- 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 parse_options(args):
- """Parse options and return a pair of host options and target options."""
- option_parser = optparse.OptionParser()
- option_parser.add_option('-v', '--verbose',
- action='store_true',
- default=False,
- help='include debug-level logging.')
-
- option_parser.add_option('-q', '--quiet',
- action='store_true',
- help='Suppress result HTML viewing')
-
- option_parser.add_option('-p', '--platforms',
- default='mac,win,win-xp,win-vista,linux',
- help=('Comma delimited list of platforms '
- 'that need rebaselining.'))
-
- option_parser.add_option('-u', '--archive_url',
- default=('http://build.chromium.org/buildbot/'
- 'layout_test_results'),
- help=('Url to find the layout test result archive'
- ' file.'))
- option_parser.add_option('-U', '--force_archive_url',
- help=('Url of result zip file. This option is for debugging '
- 'purposes'))
-
- option_parser.add_option('-w', '--webkit_canary',
- action='store_true',
- default=False,
- help=('If True, pull baselines from webkit.org '
- 'canary bot.'))
-
- option_parser.add_option('-b', '--backup',
- action='store_true',
- default=False,
- help=('Whether or not to backup the original test'
- ' expectations file after rebaseline.'))
-
- option_parser.add_option('-d', '--html_directory',
- default='',
- help=('The directory that stores the results for '
- 'rebaselining comparison.'))
-
- option_parser.add_option('', '--use_drt',
- action='store_true',
- default=False,
- help=('Use ImageDiff from DumpRenderTree instead '
- 'of image_diff for pixel tests.'))
-
- option_parser.add_option('', '--target-platform',
- default='chromium',
- help=('The target platform to rebaseline '
- '("mac", "chromium", "qt", etc.). Defaults '
- 'to "chromium".'))
- options = option_parser.parse_args(args)[0]
-
- target_options = copy.copy(options)
- if options.target_platform == 'chromium':
- target_options.chromium = True
- options.tolerance = 0
-
- return (options, target_options)
-
-
-def main(executive=Executive()):
- """Main function to produce new baselines."""
-
- (options, target_options) = parse_options(sys.argv[1:])
-
- # We need to create three different Port objects over the life of this
- # script. |target_port_obj| is used to determine configuration information:
- # location of the expectations file, names of ports to rebaseline, etc.
- # |port_obj| is used for runtime functionality like actually diffing
- # Then we create a rebaselining port to actual find and manage the
- # baselines.
- target_port_obj = port.get(None, target_options)
-
- # Set up our logging format.
- log_level = logging.INFO
- if options.verbose:
- log_level = logging.DEBUG
- logging.basicConfig(level=log_level,
- format=('%(asctime)s %(filename)s:%(lineno)-3d '
- '%(levelname)s %(message)s'),
- datefmt='%y%m%d %H:%M:%S')
-
- host_port_obj = get_host_port_object(options)
- if not host_port_obj:
- sys.exit(1)
-
- # Verify 'platforms' option is valid.
- if not options.platforms:
- _log.error('Invalid "platforms" option. --platforms must be '
- 'specified in order to rebaseline.')
- sys.exit(1)
- platforms = [p.strip().lower() for p in options.platforms.split(',')]
- for platform in platforms:
- if not platform in REBASELINE_PLATFORM_ORDER:
- _log.error('Invalid platform: "%s"' % (platform))
- sys.exit(1)
-
- # Adjust the platform order so rebaseline tool is running at the order of
- # 'mac', 'win' and 'linux'. This is in same order with layout test baseline
- # search paths. It simplifies how the rebaseline tool detects duplicate
- # baselines. Check _IsDupBaseline method for details.
- rebaseline_platforms = []
- for platform in REBASELINE_PLATFORM_ORDER:
- if platform in platforms:
- rebaseline_platforms.append(platform)
-
- options.html_directory = setup_html_directory(options.html_directory)
-
- rebaselining_tests = set()
- backup = options.backup
- for platform in rebaseline_platforms:
- rebaseliner = Rebaseliner(host_port_obj, target_port_obj,
- platform, options)
-
- _log.info('')
- log_dashed_string('Rebaseline started', platform)
- if rebaseliner.run(backup):
- # Only need to backup one original copy of test expectation file.
- backup = False
- log_dashed_string('Rebaseline done', platform)
- else:
- log_dashed_string('Rebaseline failed', platform, logging.ERROR)
-
- rebaselining_tests |= set(rebaseliner.get_rebaselining_tests())
-
- _log.info('')
- log_dashed_string('Rebaselining result comparison started', None)
- html_generator = HtmlGenerator(target_port_obj,
- options,
- rebaseline_platforms,
- rebaselining_tests,
- executive=executive)
- html_generator.generate_html()
- if not options.quiet:
- html_generator.show_html()
- log_dashed_string('Rebaselining result comparison done', None)
-
- sys.exit(0)
-
-if '__main__' == __name__:
- main()
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
deleted file mode 100644
index 7c55b94..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/rebaseline_chromium_webkit_tests_unittest.py
+++ /dev/null
@@ -1,157 +0,0 @@
-#!/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 os
-import sys
-import unittest
-
-from webkitpy.tool import mocktool
-from webkitpy.layout_tests import port
-from webkitpy.layout_tests import rebaseline_chromium_webkit_tests
-from webkitpy.common.system.executive import Executive, ScriptError
-
-
-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
-
-
-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 = mocktool.MockOptions(configuration=None,
- html_directory=None)
- 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
-
-
-class TestRebaseliner(unittest.TestCase):
- def make_rebaseliner(self):
- options = mocktool.MockOptions(configuration=None,
- html_directory=None)
- host_port_obj = port.get('test', options)
- target_options = options
- target_port_obj = port.get('test', target_options)
- platform = 'test'
- return rebaseline_chromium_webkit_tests.Rebaseliner(
- host_port_obj, target_port_obj, platform, options)
-
- def test_parse_options(self):
- (options, target_options) = rebaseline_chromium_webkit_tests.parse_options([])
- self.assertTrue(target_options.chromium)
- self.assertEqual(options.tolerance, 0)
-
- (options, target_options) = rebaseline_chromium_webkit_tests.parse_options(['--target-platform', 'qt'])
- self.assertFalse(hasattr(target_options, 'chromium'))
- self.assertEqual(options.tolerance, 0)
-
- def test_noop(self):
- # this method tests that was can at least instantiate an object, even
- # if there is nothing to do.
- rebaseliner = self.make_rebaseliner()
- self.assertNotEqual(rebaseliner, None)
-
- def test_diff_baselines_txt(self):
- rebaseliner = self.make_rebaseliner()
- output = rebaseliner._port.expected_text(
- os.path.join(rebaseliner._port.layout_tests_dir(),
- 'passes/text.html'))
- self.assertFalse(rebaseliner._diff_baselines(output, output,
- is_image=False))
-
- def test_diff_baselines_png(self):
- rebaseliner = self.make_rebaseliner()
- image = rebaseliner._port.expected_image(
- os.path.join(rebaseliner._port.layout_tests_dir(),
- 'passes/image.html'))
- self.assertFalse(rebaseliner._diff_baselines(image, image,
- is_image=True))
-
-
-class TestHtmlGenerator(unittest.TestCase):
- def make_generator(self, tests):
- return rebaseline_chromium_webkit_tests.HtmlGenerator(
- target_port=None,
- options=mocktool.MockOptions(configuration=None,
- html_directory='/tmp'),
- platforms=['mac'],
- rebaselining_tests=tests,
- executive=Executive())
-
- def test_generate_baseline_links(self):
- orig_platform = sys.platform
- orig_exists = os.path.exists
-
- try:
- sys.platform = 'darwin'
- os.path.exists = lambda x: True
- generator = self.make_generator(["foo.txt"])
- links = generator._generate_baseline_links("foo", ".txt", "mac")
- expected_links = '<td align=center><a href="file:///tmp/foo-expected-mac-old.txt">foo-expected.txt</a></td><td align=center><a href="file:///tmp/foo-expected-mac-new.txt">foo-expected.txt</a></td><td align=center><a href="file:///tmp/foo-expected-mac-diff.txt">Diff</a></td>'
- self.assertEqual(links, expected_links)
- finally:
- sys.platform = orig_platform
- os.path.exists = orig_exists
-
-
-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
deleted file mode 100755
index 643e204..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
+++ /dev/null
@@ -1,1634 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2010 Google Inc. All rights reserved.
-# Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Szeged
-#
-# 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.
-
-"""Run layout tests.
-
-This is a port of the existing webkit test script run-webkit-tests.
-
-The TestRunner class runs a series of tests (TestType interface) against a set
-of test files. If a test file fails a TestType, it returns a list TestFailure
-objects to the TestRunner. The TestRunner then aggregates the TestFailures to
-create a final report.
-
-This script reads several files, if they exist in the test_lists subdirectory
-next to this script itself. Each should contain a list of paths to individual
-tests or entire subdirectories of tests, relative to the outermost test
-directory. Entire lines starting with '//' (comments) will be ignored.
-
-For details of the files' contents and purposes, see test_lists/README.
-"""
-
-from __future__ import with_statement
-
-import codecs
-import errno
-import glob
-import logging
-import math
-import optparse
-import os
-import platform
-import Queue
-import random
-import re
-import shutil
-import signal
-import sys
-import time
-import traceback
-
-from layout_package import dump_render_tree_thread
-from layout_package import json_layout_results_generator
-from layout_package import message_broker
-from layout_package import printing
-from layout_package import test_expectations
-from layout_package import test_failures
-from layout_package import test_results
-from layout_package import test_results_uploader
-
-from webkitpy.common.system import user
-from webkitpy.thirdparty import simplejson
-from webkitpy.tool import grammar
-
-import port
-
-_log = logging.getLogger("webkitpy.layout_tests.run_webkit_tests")
-
-# Builder base URL where we have the archived test results.
-BUILDER_BASE_URL = "http://build.chromium.org/buildbot/layout_test_results/"
-
-LAYOUT_TESTS_DIRECTORY = "LayoutTests" + os.sep
-
-TestExpectationsFile = test_expectations.TestExpectationsFile
-
-
-class TestInput:
- """Groups information about a test for easy passing of data."""
-
- def __init__(self, filename, timeout):
- """Holds the input parameters for a test.
- Args:
- filename: Full path to the test.
- timeout: Timeout in msecs the driver should use while running the test
- """
- # FIXME: filename should really be test_name as a relative path.
- self.filename = filename
- self.timeout = timeout
- # The image_hash is used to avoid doing an image dump if the
- # checksums match. The image_hash is set later, and only if it is needed
- # for the test.
- self.image_hash = None
-
-
-class ResultSummary(object):
- """A class for partitioning the test results we get into buckets.
-
- This class is basically a glorified struct and it's private to this file
- so we don't bother with any information hiding."""
-
- def __init__(self, expectations, test_files):
- self.total = len(test_files)
- self.remaining = self.total
- self.expectations = expectations
- self.expected = 0
- self.unexpected = 0
- self.tests_by_expectation = {}
- self.tests_by_timeline = {}
- self.results = {}
- self.unexpected_results = {}
- self.failures = {}
- self.tests_by_expectation[test_expectations.SKIP] = set()
- for expectation in TestExpectationsFile.EXPECTATIONS.values():
- self.tests_by_expectation[expectation] = set()
- for timeline in TestExpectationsFile.TIMELINES.values():
- self.tests_by_timeline[timeline] = (
- expectations.get_tests_with_timeline(timeline))
-
- def add(self, result, expected):
- """Add a TestResult into the appropriate bin.
-
- Args:
- result: TestResult from dump_render_tree_thread.
- expected: whether the result was what we expected it to be.
- """
-
- self.tests_by_expectation[result.type].add(result.filename)
- self.results[result.filename] = result
- self.remaining -= 1
- if len(result.failures):
- self.failures[result.filename] = result.failures
- if expected:
- self.expected += 1
- else:
- self.unexpected_results[result.filename] = result.type
- self.unexpected += 1
-
-
-def summarize_unexpected_results(port_obj, expectations, result_summary,
- retry_summary):
- """Summarize any unexpected results as a dict.
-
- FIXME: split this data structure into a separate class?
-
- Args:
- port_obj: interface to port-specific hooks
- expectations: test_expectations.TestExpectations object
- result_summary: summary object from initial test runs
- retry_summary: summary object from final test run of retried tests
- Returns:
- A dictionary containing a summary of the unexpected results from the
- run, with the following fields:
- 'version': a version indicator (1 in this version)
- 'fixable': # of fixable tests (NOW - PASS)
- 'skipped': # of skipped tests (NOW & SKIPPED)
- 'num_regressions': # of non-flaky failures
- 'num_flaky': # of flaky failures
- 'num_passes': # of unexpected passes
- 'tests': a dict of tests -> {'expected': '...', 'actual': '...'}
- """
- results = {}
- results['version'] = 1
-
- tbe = result_summary.tests_by_expectation
- tbt = result_summary.tests_by_timeline
- results['fixable'] = len(tbt[test_expectations.NOW] -
- tbe[test_expectations.PASS])
- results['skipped'] = len(tbt[test_expectations.NOW] &
- tbe[test_expectations.SKIP])
-
- num_passes = 0
- num_flaky = 0
- num_regressions = 0
- keywords = {}
- for k, v in TestExpectationsFile.EXPECTATIONS.iteritems():
- keywords[v] = k.upper()
-
- tests = {}
- for filename, result in result_summary.unexpected_results.iteritems():
- # Note that if a test crashed in the original run, we ignore
- # whether or not it crashed when we retried it (if we retried it),
- # and always consider the result not flaky.
- test = port_obj.relative_test_filename(filename)
- expected = expectations.get_expectations_string(filename)
- actual = [keywords[result]]
-
- if result == test_expectations.PASS:
- num_passes += 1
- elif result == test_expectations.CRASH:
- num_regressions += 1
- else:
- if filename not in retry_summary.unexpected_results:
- actual.extend(expectations.get_expectations_string(
- filename).split(" "))
- num_flaky += 1
- else:
- retry_result = retry_summary.unexpected_results[filename]
- if result != retry_result:
- actual.append(keywords[retry_result])
- num_flaky += 1
- else:
- num_regressions += 1
-
- tests[test] = {}
- tests[test]['expected'] = expected
- tests[test]['actual'] = " ".join(actual)
-
- results['tests'] = tests
- results['num_passes'] = num_passes
- results['num_flaky'] = num_flaky
- results['num_regressions'] = num_regressions
-
- return results
-
-
-class TestRunner:
- """A class for managing running a series of tests on a series of layout
- test files."""
-
- HTTP_SUBDIR = os.sep.join(['', 'http', ''])
- WEBSOCKET_SUBDIR = os.sep.join(['', 'websocket', ''])
-
- # The per-test timeout in milliseconds, if no --time-out-ms option was
- # given to run_webkit_tests. This should correspond to the default timeout
- # in DumpRenderTree.
- DEFAULT_TEST_TIMEOUT_MS = 6 * 1000
-
- def __init__(self, port, options, printer, message_broker):
- """Initialize test runner data structures.
-
- Args:
- port: an object implementing port-specific
- options: a dictionary of command line options
- printer: a Printer object to record updates to.
- message_broker: object used to communicate with workers.
- """
- self._port = port
- self._options = options
- self._printer = printer
- self._message_broker = message_broker
-
- # disable wss server. need to install pyOpenSSL on buildbots.
- # self._websocket_secure_server = websocket_server.PyWebSocket(
- # options.results_directory, use_tls=True, port=9323)
-
- # a set of test files, and the same tests as a list
- self._test_files = set()
- self._test_files_list = None
- self._result_queue = Queue.Queue()
- self._retrying = False
-
- def collect_tests(self, args, last_unexpected_results):
- """Find all the files to test.
-
- Args:
- args: list of test arguments from the command line
- last_unexpected_results: list of unexpected results to retest, if any
-
- """
- paths = [self._strip_test_dir_prefix(arg) for arg in args if arg and arg != '']
- paths += last_unexpected_results
- if self._options.test_list:
- paths += read_test_files(self._options.test_list)
- self._test_files = self._port.tests(paths)
-
- def _strip_test_dir_prefix(self, path):
- if path.startswith(LAYOUT_TESTS_DIRECTORY):
- return path[len(LAYOUT_TESTS_DIRECTORY):]
- return path
-
- def lint(self):
- # Creating the expecations for each platform/configuration pair does
- # all the test list parsing and ensures it's correct syntax (e.g. no
- # dupes).
- for platform_name in self._port.test_platform_names():
- self.parse_expectations(platform_name, is_debug_mode=True)
- self.parse_expectations(platform_name, is_debug_mode=False)
- self._printer.write("")
- _log.info("If there are no fail messages, errors or exceptions, "
- "then the lint succeeded.")
- return 0
-
- def parse_expectations(self, test_platform_name, is_debug_mode):
- """Parse the expectations from the test_list files and return a data
- structure holding them. Throws an error if the test_list files have
- invalid syntax."""
- if self._options.lint_test_files:
- test_files = None
- else:
- test_files = self._test_files
-
- try:
- expectations_str = self._port.test_expectations()
- overrides_str = self._port.test_expectations_overrides()
- self._expectations = test_expectations.TestExpectations(
- self._port, test_files, expectations_str, test_platform_name,
- is_debug_mode, self._options.lint_test_files,
- overrides=overrides_str)
- return self._expectations
- except SyntaxError, err:
- if self._options.lint_test_files:
- print str(err)
- else:
- raise err
-
- def prepare_lists_and_print_output(self):
- """Create appropriate subsets of test lists and returns a
- ResultSummary object. Also prints expected test counts.
- """
-
- # Remove skipped - both fixable and ignored - files from the
- # top-level list of files to test.
- num_all_test_files = len(self._test_files)
- self._printer.print_expected("Found: %d tests" %
- (len(self._test_files)))
- if not num_all_test_files:
- _log.critical('No tests to run.')
- return None
-
- skipped = set()
- if num_all_test_files > 1 and not self._options.force:
- skipped = self._expectations.get_tests_with_result_type(
- test_expectations.SKIP)
- self._test_files -= skipped
-
- # Create a sorted list of test files so the subset chunk,
- # if used, contains alphabetically consecutive tests.
- self._test_files_list = list(self._test_files)
- if self._options.randomize_order:
- random.shuffle(self._test_files_list)
- else:
- self._test_files_list.sort()
-
- # If the user specifies they just want to run a subset of the tests,
- # just grab a subset of the non-skipped tests.
- if self._options.run_chunk or self._options.run_part:
- chunk_value = self._options.run_chunk or self._options.run_part
- test_files = self._test_files_list
- try:
- (chunk_num, chunk_len) = chunk_value.split(":")
- chunk_num = int(chunk_num)
- assert(chunk_num >= 0)
- test_size = int(chunk_len)
- assert(test_size > 0)
- except:
- _log.critical("invalid chunk '%s'" % chunk_value)
- return None
-
- # Get the number of tests
- num_tests = len(test_files)
-
- # Get the start offset of the slice.
- if self._options.run_chunk:
- chunk_len = test_size
- # In this case chunk_num can be really large. We need
- # to make the slave fit in the current number of tests.
- slice_start = (chunk_num * chunk_len) % num_tests
- else:
- # Validate the data.
- assert(test_size <= num_tests)
- assert(chunk_num <= test_size)
-
- # To count the chunk_len, and make sure we don't skip
- # some tests, we round to the next value that fits exactly
- # all the parts.
- rounded_tests = num_tests
- if rounded_tests % test_size != 0:
- rounded_tests = (num_tests + test_size -
- (num_tests % test_size))
-
- chunk_len = rounded_tests / test_size
- slice_start = chunk_len * (chunk_num - 1)
- # It does not mind if we go over test_size.
-
- # Get the end offset of the slice.
- slice_end = min(num_tests, slice_start + chunk_len)
-
- files = test_files[slice_start:slice_end]
-
- tests_run_msg = 'Running: %d tests (chunk slice [%d:%d] of %d)' % (
- (slice_end - slice_start), slice_start, slice_end, num_tests)
- self._printer.print_expected(tests_run_msg)
-
- # If we reached the end and we don't have enough tests, we run some
- # from the beginning.
- if slice_end - slice_start < chunk_len:
- extra = chunk_len - (slice_end - slice_start)
- extra_msg = (' last chunk is partial, appending [0:%d]' %
- extra)
- self._printer.print_expected(extra_msg)
- tests_run_msg += "\n" + extra_msg
- files.extend(test_files[0:extra])
- tests_run_filename = os.path.join(self._options.results_directory,
- "tests_run.txt")
- with codecs.open(tests_run_filename, "w", "utf-8") as file:
- file.write(tests_run_msg + "\n")
-
- len_skip_chunk = int(len(files) * len(skipped) /
- float(len(self._test_files)))
- skip_chunk_list = list(skipped)[0:len_skip_chunk]
- skip_chunk = set(skip_chunk_list)
-
- # Update expectations so that the stats are calculated correctly.
- # We need to pass a list that includes the right # of skipped files
- # to ParseExpectations so that ResultSummary() will get the correct
- # stats. So, we add in the subset of skipped files, and then
- # subtract them back out.
- self._test_files_list = files + skip_chunk_list
- self._test_files = set(self._test_files_list)
-
- self._expectations = self.parse_expectations(
- self._port.test_platform_name(),
- self._options.configuration == 'Debug')
-
- self._test_files = set(files)
- self._test_files_list = files
- else:
- skip_chunk = skipped
-
- result_summary = ResultSummary(self._expectations,
- self._test_files | skip_chunk)
- self._print_expected_results_of_type(result_summary,
- test_expectations.PASS, "passes")
- self._print_expected_results_of_type(result_summary,
- test_expectations.FAIL, "failures")
- self._print_expected_results_of_type(result_summary,
- test_expectations.FLAKY, "flaky")
- self._print_expected_results_of_type(result_summary,
- test_expectations.SKIP, "skipped")
-
- if self._options.force:
- self._printer.print_expected('Running all tests, including '
- 'skips (--force)')
- else:
- # Note that we don't actually run the skipped tests (they were
- # subtracted out of self._test_files, above), but we stub out the
- # results here so the statistics can remain accurate.
- for test in skip_chunk:
- result = test_results.TestResult(test,
- failures=[], test_run_time=0, total_time_for_all_diffs=0,
- time_for_diffs=0)
- result.type = test_expectations.SKIP
- result_summary.add(result, expected=True)
- self._printer.print_expected('')
-
- return result_summary
-
- def _get_dir_for_test_file(self, test_file):
- """Returns the highest-level directory by which to shard the given
- test file."""
- index = test_file.rfind(os.sep + LAYOUT_TESTS_DIRECTORY)
-
- test_file = test_file[index + len(LAYOUT_TESTS_DIRECTORY):]
- test_file_parts = test_file.split(os.sep, 1)
- directory = test_file_parts[0]
- test_file = test_file_parts[1]
-
- # The http tests are very stable on mac/linux.
- # TODO(ojan): Make the http server on Windows be apache so we can
- # turn shard the http tests there as well. Switching to apache is
- # what made them stable on linux/mac.
- return_value = directory
- while ((directory != 'http' or sys.platform in ('darwin', 'linux2'))
- and test_file.find(os.sep) >= 0):
- test_file_parts = test_file.split(os.sep, 1)
- directory = test_file_parts[0]
- return_value = os.path.join(return_value, directory)
- test_file = test_file_parts[1]
-
- return return_value
-
- def _get_test_input_for_file(self, test_file):
- """Returns the appropriate TestInput object for the file. Mostly this
- is used for looking up the timeout value (in ms) to use for the given
- test."""
- if self._test_is_slow(test_file):
- return TestInput(test_file, self._options.slow_time_out_ms)
- return TestInput(test_file, self._options.time_out_ms)
-
- def _test_requires_lock(self, test_file):
- """Return True if the test needs to be locked when
- running multiple copies of NRWTs."""
- split_path = test_file.split(os.sep)
- return 'http' in split_path or 'websocket' in split_path
-
- def _test_is_slow(self, test_file):
- return self._expectations.has_modifier(test_file,
- test_expectations.SLOW)
-
- def _shard_tests(self, test_files, use_real_shards):
- """Groups tests into batches.
- This helps ensure that tests that depend on each other (aka bad tests!)
- continue to run together as most cross-tests dependencies tend to
- occur within the same directory. If use_real_shards is false, we
- put each (non-HTTP/websocket) test into its own shard for maximum
- concurrency instead of trying to do any sort of real sharding.
-
- Return:
- A list of lists of TestInput objects.
- """
- # FIXME: when we added http locking, we changed how this works such
- # that we always lump all of the HTTP threads into a single shard.
- # That will slow down experimental-fully-parallel, but it's unclear
- # what the best alternative is completely revamping how we track
- # when to grab the lock.
-
- test_lists = []
- tests_to_http_lock = []
- if not use_real_shards:
- for test_file in test_files:
- test_input = self._get_test_input_for_file(test_file)
- if self._test_requires_lock(test_file):
- tests_to_http_lock.append(test_input)
- else:
- test_lists.append((".", [test_input]))
- else:
- tests_by_dir = {}
- for test_file in test_files:
- directory = self._get_dir_for_test_file(test_file)
- test_input = self._get_test_input_for_file(test_file)
- if self._test_requires_lock(test_file):
- tests_to_http_lock.append(test_input)
- else:
- tests_by_dir.setdefault(directory, [])
- tests_by_dir[directory].append(test_input)
- # Sort by the number of tests in the dir so that the ones with the
- # most tests get run first in order to maximize parallelization.
- # Number of tests is a good enough, but not perfect, approximation
- # of how long that set of tests will take to run. We can't just use
- # a PriorityQueue until we move to Python 2.6.
- for directory in tests_by_dir:
- test_list = tests_by_dir[directory]
- # Keep the tests in alphabetical order.
- # FIXME: Remove once tests are fixed so they can be run in any
- # order.
- test_list.reverse()
- test_list_tuple = (directory, test_list)
- test_lists.append(test_list_tuple)
- test_lists.sort(lambda a, b: cmp(len(b[1]), len(a[1])))
-
- # Put the http tests first. There are only a couple hundred of them,
- # but each http test takes a very long time to run, so sorting by the
- # number of tests doesn't accurately capture how long they take to run.
- if tests_to_http_lock:
- tests_to_http_lock.reverse()
- test_lists.insert(0, ("tests_to_http_lock", tests_to_http_lock))
-
- return test_lists
-
- def _contains_tests(self, subdir):
- for test_file in self._test_files:
- if test_file.find(subdir) >= 0:
- return True
- return False
-
- def _num_workers(self):
- return int(self._options.child_processes)
-
- def _run_tests(self, file_list, result_summary):
- """Runs the tests in the file_list.
-
- Return: A tuple (keyboard_interrupted, thread_timings, test_timings,
- individual_test_timings)
- keyboard_interrupted is whether someone typed Ctrl^C
- thread_timings is a list of dicts with the total runtime
- of each thread with 'name', 'num_tests', 'total_time' properties
- test_timings is a list of timings for each sharded subdirectory
- of the form [time, directory_name, num_tests]
- individual_test_timings is a list of run times for each test
- in the form {filename:filename, test_run_time:test_run_time}
- result_summary: summary object to populate with the results
- """
-
- self._printer.print_update('Sharding tests ...')
- num_workers = self._num_workers()
- test_lists = self._shard_tests(file_list,
- num_workers > 1 and not self._options.experimental_fully_parallel)
- filename_queue = Queue.Queue()
- for item in test_lists:
- filename_queue.put(item)
-
- self._printer.print_update('Starting %s ...' %
- grammar.pluralize('worker', num_workers))
- message_broker = self._message_broker
- self._current_filename_queue = filename_queue
- self._current_result_summary = result_summary
- threads = message_broker.start_workers(self)
-
- self._printer.print_update("Starting testing ...")
- keyboard_interrupted = False
- try:
- message_broker.run_message_loop()
- except KeyboardInterrupt:
- _log.info("Interrupted, exiting")
- message_broker.cancel_workers()
- keyboard_interrupted = True
- except:
- # Unexpected exception; don't try to clean up workers.
- _log.info("Exception raised, exiting")
- raise
-
- thread_timings, test_timings, individual_test_timings = \
- self._collect_timing_info(threads)
-
- return (keyboard_interrupted, thread_timings, test_timings,
- individual_test_timings)
-
- def update(self):
- self.update_summary(self._current_result_summary)
-
- def _collect_timing_info(self, threads):
- test_timings = {}
- individual_test_timings = []
- thread_timings = []
-
- for thread in threads:
- thread_timings.append({'name': thread.getName(),
- 'num_tests': thread.get_num_tests(),
- 'total_time': thread.get_total_time()})
- test_timings.update(thread.get_test_group_timing_stats())
- individual_test_timings.extend(thread.get_test_results())
-
- return (thread_timings, test_timings, individual_test_timings)
-
- def needs_http(self):
- """Returns whether the test runner needs an HTTP server."""
- return self._contains_tests(self.HTTP_SUBDIR)
-
- def needs_websocket(self):
- """Returns whether the test runner needs a WEBSOCKET server."""
- return self._contains_tests(self.WEBSOCKET_SUBDIR)
-
- def set_up_run(self):
- """Configures the system to be ready to run tests.
-
- Returns a ResultSummary object if we should continue to run tests,
- or None if we should abort.
-
- """
- # This must be started before we check the system dependencies,
- # since the helper may do things to make the setup correct.
- self._printer.print_update("Starting helper ...")
- self._port.start_helper()
-
- # Check that the system dependencies (themes, fonts, ...) are correct.
- if not self._options.nocheck_sys_deps:
- self._printer.print_update("Checking system dependencies ...")
- if not self._port.check_sys_deps(self.needs_http()):
- self._port.stop_helper()
- return None
-
- if self._options.clobber_old_results:
- self._clobber_old_results()
-
- # Create the output directory if it doesn't already exist.
- self._port.maybe_make_directory(self._options.results_directory)
-
- self._port.setup_test_run()
-
- self._printer.print_update("Preparing tests ...")
- result_summary = self.prepare_lists_and_print_output()
- if not result_summary:
- return None
-
- return result_summary
-
- def run(self, result_summary):
- """Run all our tests on all our test files.
-
- For each test file, we run each test type. If there are any failures,
- we collect them for reporting.
-
- Args:
- result_summary: a summary object tracking the test results.
-
- Return:
- The number of unexpected results (0 == success)
- """
- # gather_test_files() must have been called first to initialize us.
- # If we didn't find any files to test, we've errored out already in
- # prepare_lists_and_print_output().
- assert(len(self._test_files))
-
- start_time = time.time()
-
- keyboard_interrupted, thread_timings, test_timings, \
- individual_test_timings = (
- self._run_tests(self._test_files_list, result_summary))
-
- # We exclude the crashes from the list of results to retry, because
- # we want to treat even a potentially flaky crash as an error.
- failures = self._get_failures(result_summary, include_crashes=False)
- retry_summary = result_summary
- while (len(failures) and self._options.retry_failures and
- not self._retrying and not keyboard_interrupted):
- _log.info('')
- _log.info("Retrying %d unexpected failure(s) ..." % len(failures))
- _log.info('')
- self._retrying = True
- retry_summary = ResultSummary(self._expectations, failures.keys())
- # Note that we intentionally ignore the return value here.
- self._run_tests(failures.keys(), retry_summary)
- failures = self._get_failures(retry_summary, include_crashes=True)
-
- end_time = time.time()
-
- self._print_timing_statistics(end_time - start_time,
- thread_timings, test_timings,
- individual_test_timings,
- result_summary)
-
- self._print_result_summary(result_summary)
-
- sys.stdout.flush()
- sys.stderr.flush()
-
- self._printer.print_one_line_summary(result_summary.total,
- result_summary.expected,
- result_summary.unexpected)
-
- unexpected_results = summarize_unexpected_results(self._port,
- self._expectations, result_summary, retry_summary)
- self._printer.print_unexpected_results(unexpected_results)
-
- if self._options.record_results:
- # Write the same data to log files and upload generated JSON files
- # to appengine server.
- self._upload_json_files(unexpected_results, result_summary,
- individual_test_timings)
-
- # 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:
- self._show_results_html_file()
-
- # Now that we've completed all the processing we can, we re-raise
- # a KeyboardInterrupt if necessary so the caller can handle it.
- if keyboard_interrupted:
- raise KeyboardInterrupt
-
- # Ignore flaky failures and unexpected passes so we don't turn the
- # bot red for those.
- return unexpected_results['num_regressions']
-
- def clean_up_run(self):
- """Restores the system after we're done running tests."""
-
- _log.debug("flushing stdout")
- sys.stdout.flush()
- _log.debug("flushing stderr")
- sys.stderr.flush()
- _log.debug("stopping helper")
- self._port.stop_helper()
-
- def update_summary(self, result_summary):
- """Update the summary and print results with any completed tests."""
- while True:
- try:
- result = test_results.TestResult.loads(self._result_queue.get_nowait())
- except Queue.Empty:
- return
-
- expected = self._expectations.matches_an_expected_result(
- result.filename, result.type, self._options.pixel_tests)
- result_summary.add(result, expected)
- exp_str = self._expectations.get_expectations_string(
- result.filename)
- got_str = self._expectations.expectation_to_string(result.type)
- self._printer.print_test_result(result, expected, exp_str, got_str)
- self._printer.print_progress(result_summary, self._retrying,
- self._test_files_list)
-
- def _clobber_old_results(self):
- # Just clobber the actual test results directories since the other
- # files in the results directory are explicitly used for cross-run
- # tracking.
- self._printer.print_update("Clobbering old results in %s" %
- self._options.results_directory)
- layout_tests_dir = self._port.layout_tests_dir()
- possible_dirs = self._port.test_dirs()
- for dirname in possible_dirs:
- if os.path.isdir(os.path.join(layout_tests_dir, dirname)):
- shutil.rmtree(os.path.join(self._options.results_directory,
- dirname),
- ignore_errors=True)
-
- def _get_failures(self, result_summary, include_crashes):
- """Filters a dict of results and returns only the failures.
-
- Args:
- result_summary: the results of the test run
- include_crashes: whether crashes are included in the output.
- We use False when finding the list of failures to retry
- to see if the results were flaky. Although the crashes may also be
- flaky, we treat them as if they aren't so that they're not ignored.
- Returns:
- a dict of files -> results
- """
- failed_results = {}
- for test, result in result_summary.unexpected_results.iteritems():
- if (result == test_expectations.PASS or
- result == test_expectations.CRASH and not include_crashes):
- continue
- failed_results[test] = result
-
- return failed_results
-
- def _upload_json_files(self, unexpected_results, result_summary,
- individual_test_timings):
- """Writes the results of the test run as JSON files into the results
- dir and upload the files to the appengine server.
-
- There are three different files written into the results dir:
- unexpected_results.json: A short list of any unexpected results.
- This is used by the buildbots to display results.
- expectations.json: This is used by the flakiness dashboard.
- results.json: A full list of the results - used by the flakiness
- dashboard and the aggregate results dashboard.
-
- Args:
- unexpected_results: dict of unexpected results
- result_summary: full summary object
- individual_test_timings: list of test times (used by the flakiness
- dashboard).
- """
- results_directory = self._options.results_directory
- _log.debug("Writing JSON files in %s." % results_directory)
- unexpected_json_path = os.path.join(results_directory, "unexpected_results.json")
- with codecs.open(unexpected_json_path, "w", "utf-8") as file:
- simplejson.dump(unexpected_results, file, sort_keys=True, indent=2)
-
- # Write a json file of the test_expectations.txt file for the layout
- # tests dashboard.
- expectations_path = os.path.join(results_directory, "expectations.json")
- expectations_json = \
- self._expectations.get_expectations_json_for_all_platforms()
- with codecs.open(expectations_path, "w", "utf-8") as file:
- file.write(u"ADD_EXPECTATIONS(%s);" % expectations_json)
-
- generator = json_layout_results_generator.JSONLayoutResultsGenerator(
- self._port, self._options.builder_name, self._options.build_name,
- self._options.build_number, self._options.results_directory,
- BUILDER_BASE_URL, individual_test_timings,
- self._expectations, result_summary, self._test_files_list,
- not self._options.upload_full_results,
- self._options.test_results_server,
- "layout-tests",
- self._options.master_name)
-
- _log.debug("Finished writing JSON files.")
-
- json_files = ["expectations.json"]
- if self._options.upload_full_results:
- json_files.append("results.json")
- else:
- json_files.append("incremental_results.json")
-
- generator.upload_json_files(json_files)
-
- def _print_config(self):
- """Prints the configuration for the test run."""
- p = self._printer
- p.print_config("Using port '%s'" % self._port.name())
- p.print_config("Placing test results in %s" %
- self._options.results_directory)
- if self._options.new_baseline:
- p.print_config("Placing new baselines in %s" %
- self._port.baseline_path())
- p.print_config("Using %s build" % self._options.configuration)
- if self._options.pixel_tests:
- p.print_config("Pixel tests enabled")
- else:
- p.print_config("Pixel tests disabled")
-
- p.print_config("Regular timeout: %s, slow test timeout: %s" %
- (self._options.time_out_ms,
- self._options.slow_time_out_ms))
-
- if self._num_workers() == 1:
- p.print_config("Running one %s" % self._port.driver_name())
- else:
- p.print_config("Running %s %ss in parallel" %
- (self._options.child_processes,
- self._port.driver_name()))
- p.print_config('Command line: ' +
- ' '.join(self._port.driver_cmd_line()))
- p.print_config("Worker model: %s" % self._options.worker_model)
- p.print_config("")
-
- 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.
-
- Args:
- result_summary - the object containing all the results to report on
- result_type - the particular result type to report in the summary.
- result_type_str - a string description of the result_type.
- """
- tests = self._expectations.get_tests_with_result_type(result_type)
- now = result_summary.tests_by_timeline[test_expectations.NOW]
- wontfix = result_summary.tests_by_timeline[test_expectations.WONTFIX]
-
- # We use a fancy format string in order to print the data out in a
- # nicely-aligned table.
- fmtstr = ("Expect: %%5d %%-8s (%%%dd now, %%%dd wontfix)"
- % (self._num_digits(now), self._num_digits(wontfix)))
- self._printer.print_expected(fmtstr %
- (len(tests), result_type_str, len(tests & now), len(tests & wontfix)))
-
- def _num_digits(self, num):
- """Returns the number of digits needed to represent the length of a
- sequence."""
- ndigits = 1
- if len(num):
- ndigits = int(math.log10(len(num))) + 1
- return ndigits
-
- def _print_timing_statistics(self, total_time, thread_timings,
- directory_test_timings, individual_test_timings,
- result_summary):
- """Record timing-specific information for the test run.
-
- Args:
- total_time: total elapsed time (in seconds) for the test run
- thread_timings: wall clock time each thread ran for
- directory_test_timings: timing by directory
- individual_test_timings: timing by file
- result_summary: summary object for the test run
- """
- self._printer.print_timing("Test timing:")
- self._printer.print_timing(" %6.2f total testing time" % total_time)
- self._printer.print_timing("")
- self._printer.print_timing("Thread timing:")
- cuml_time = 0
- for t in thread_timings:
- self._printer.print_timing(" %10s: %5d tests, %6.2f secs" %
- (t['name'], t['num_tests'], t['total_time']))
- cuml_time += t['total_time']
- self._printer.print_timing(" %6.2f cumulative, %6.2f optimal" %
- (cuml_time, cuml_time / int(self._options.child_processes)))
- self._printer.print_timing("")
-
- self._print_aggregate_test_statistics(individual_test_timings)
- self._print_individual_test_times(individual_test_timings,
- result_summary)
- self._print_directory_timings(directory_test_timings)
-
- def _print_aggregate_test_statistics(self, individual_test_timings):
- """Prints aggregate statistics (e.g. median, mean, etc.) for all tests.
- Args:
- individual_test_timings: List of dump_render_tree_thread.TestStats
- for all tests.
- """
- test_types = [] # Unit tests don't actually produce any timings.
- if individual_test_timings:
- test_types = individual_test_timings[0].time_for_diffs.keys()
- times_for_dump_render_tree = []
- times_for_diff_processing = []
- times_per_test_type = {}
- for test_type in test_types:
- times_per_test_type[test_type] = []
-
- for test_stats in individual_test_timings:
- times_for_dump_render_tree.append(test_stats.test_run_time)
- times_for_diff_processing.append(
- test_stats.total_time_for_all_diffs)
- time_for_diffs = test_stats.time_for_diffs
- for test_type in test_types:
- times_per_test_type[test_type].append(
- time_for_diffs[test_type])
-
- self._print_statistics_for_test_timings(
- "PER TEST TIME IN TESTSHELL (seconds):",
- times_for_dump_render_tree)
- self._print_statistics_for_test_timings(
- "PER TEST DIFF PROCESSING TIMES (seconds):",
- times_for_diff_processing)
- for test_type in test_types:
- self._print_statistics_for_test_timings(
- "PER TEST TIMES BY TEST TYPE: %s" % test_type,
- times_per_test_type[test_type])
-
- def _print_individual_test_times(self, individual_test_timings,
- result_summary):
- """Prints the run times for slow, timeout and crash tests.
- Args:
- individual_test_timings: List of dump_render_tree_thread.TestStats
- for all tests.
- result_summary: summary object for test run
- """
- # Reverse-sort by the time spent in DumpRenderTree.
- individual_test_timings.sort(lambda a, b:
- cmp(b.test_run_time, a.test_run_time))
-
- num_printed = 0
- slow_tests = []
- timeout_or_crash_tests = []
- unexpected_slow_tests = []
- for test_tuple in individual_test_timings:
- filename = test_tuple.filename
- is_timeout_crash_or_slow = False
- if self._test_is_slow(filename):
- is_timeout_crash_or_slow = True
- slow_tests.append(test_tuple)
-
- if filename in result_summary.failures:
- result = result_summary.results[filename].type
- if (result == test_expectations.TIMEOUT or
- result == test_expectations.CRASH):
- is_timeout_crash_or_slow = True
- timeout_or_crash_tests.append(test_tuple)
-
- if (not is_timeout_crash_or_slow and
- num_printed < printing.NUM_SLOW_TESTS_TO_LOG):
- num_printed = num_printed + 1
- unexpected_slow_tests.append(test_tuple)
-
- self._printer.print_timing("")
- self._print_test_list_timing("%s slowest tests that are not "
- "marked as SLOW and did not timeout/crash:" %
- printing.NUM_SLOW_TESTS_TO_LOG, unexpected_slow_tests)
- self._printer.print_timing("")
- self._print_test_list_timing("Tests marked as SLOW:", slow_tests)
- self._printer.print_timing("")
- self._print_test_list_timing("Tests that timed out or crashed:",
- timeout_or_crash_tests)
- self._printer.print_timing("")
-
- def _print_test_list_timing(self, title, test_list):
- """Print timing info for each test.
-
- Args:
- title: section heading
- test_list: tests that fall in this section
- """
- if self._printer.disabled('slowest'):
- return
-
- self._printer.print_timing(title)
- for test_tuple in test_list:
- filename = test_tuple.filename[len(
- self._port.layout_tests_dir()) + 1:]
- filename = filename.replace('\\', '/')
- test_run_time = round(test_tuple.test_run_time, 1)
- self._printer.print_timing(" %s took %s seconds" %
- (filename, test_run_time))
-
- def _print_directory_timings(self, directory_test_timings):
- """Print timing info by directory for any directories that
- take > 10 seconds to run.
-
- Args:
- directory_test_timing: time info for each directory
- """
- timings = []
- for directory in directory_test_timings:
- num_tests, time_for_directory = directory_test_timings[directory]
- timings.append((round(time_for_directory, 1), directory,
- num_tests))
- timings.sort()
-
- self._printer.print_timing("Time to process slowest subdirectories:")
- min_seconds_to_print = 10
- for timing in timings:
- if timing[0] > min_seconds_to_print:
- self._printer.print_timing(
- " %s took %s seconds to run %s tests." % (timing[1],
- timing[0], timing[2]))
- self._printer.print_timing("")
-
- def _print_statistics_for_test_timings(self, title, timings):
- """Prints the median, mean and standard deviation of the values in
- timings.
-
- Args:
- title: Title for these timings.
- timings: A list of floats representing times.
- """
- self._printer.print_timing(title)
- timings.sort()
-
- num_tests = len(timings)
- if not num_tests:
- return
- percentile90 = timings[int(.9 * num_tests)]
- percentile99 = timings[int(.99 * num_tests)]
-
- if num_tests % 2 == 1:
- median = timings[((num_tests - 1) / 2) - 1]
- else:
- lower = timings[num_tests / 2 - 1]
- upper = timings[num_tests / 2]
- median = (float(lower + upper)) / 2
-
- mean = sum(timings) / num_tests
-
- for time in timings:
- sum_of_deviations = math.pow(time - mean, 2)
-
- std_deviation = math.sqrt(sum_of_deviations / num_tests)
- self._printer.print_timing(" Median: %6.3f" % median)
- self._printer.print_timing(" Mean: %6.3f" % mean)
- self._printer.print_timing(" 90th percentile: %6.3f" % percentile90)
- self._printer.print_timing(" 99th percentile: %6.3f" % percentile99)
- self._printer.print_timing(" Standard dev: %6.3f" % std_deviation)
- self._printer.print_timing("")
-
- def _print_result_summary(self, result_summary):
- """Print a short summary about how many tests passed.
-
- Args:
- result_summary: information to log
- """
- failed = len(result_summary.failures)
- skipped = len(
- result_summary.tests_by_expectation[test_expectations.SKIP])
- total = result_summary.total
- passed = total - failed - skipped
- pct_passed = 0.0
- if total > 0:
- pct_passed = float(passed) * 100 / total
-
- self._printer.print_actual("")
- self._printer.print_actual("=> Results: %d/%d tests passed (%.1f%%)" %
- (passed, total, pct_passed))
- self._printer.print_actual("")
- self._print_result_summary_entry(result_summary,
- test_expectations.NOW, "Tests to be fixed")
-
- self._printer.print_actual("")
- self._print_result_summary_entry(result_summary,
- test_expectations.WONTFIX,
- "Tests that will only be fixed if they crash (WONTFIX)")
- self._printer.print_actual("")
-
- def _print_result_summary_entry(self, result_summary, timeline,
- heading):
- """Print a summary block of results for a particular timeline of test.
-
- Args:
- result_summary: summary to print results for
- timeline: the timeline to print results for (NOT, WONTFIX, etc.)
- heading: a textual description of the timeline
- """
- total = len(result_summary.tests_by_timeline[timeline])
- not_passing = (total -
- len(result_summary.tests_by_expectation[test_expectations.PASS] &
- result_summary.tests_by_timeline[timeline]))
- self._printer.print_actual("=> %s (%d):" % (heading, not_passing))
-
- for result in TestExpectationsFile.EXPECTATION_ORDER:
- if result == test_expectations.PASS:
- continue
- results = (result_summary.tests_by_expectation[result] &
- result_summary.tests_by_timeline[timeline])
- desc = TestExpectationsFile.EXPECTATION_DESCRIPTIONS[result]
- if not_passing and len(results):
- pct = len(results) * 100.0 / not_passing
- self._printer.print_actual(" %5d %-24s (%4.1f%%)" %
- (len(results), desc[len(results) != 1], pct))
-
- def _results_html(self, test_files, failures, title="Test Failures", override_time=None):
- """
- test_files = a list of file paths
- failures = dictionary mapping test paths to failure objects
- title = title printed at top of test
- override_time = current time (used by unit tests)
- """
- page = """<html>
- <head>
- <title>Layout Test Results (%(time)s)</title>
- </head>
- <body>
- <h2>%(title)s (%(time)s)</h2>
- """ % {'title': title, 'time': override_time or time.asctime()}
-
- for test_file in sorted(test_files):
- test_name = self._port.relative_test_filename(test_file)
- test_url = self._port.filename_to_uri(test_file)
- page += u"<p><a href='%s'>%s</a><br />\n" % (test_url, test_name)
- test_failures = failures.get(test_file, [])
- for failure in test_failures:
- page += (u"&nbsp;&nbsp;%s<br/>" %
- failure.result_html_output(test_name))
- page += "</p>\n"
- page += "</body></html>\n"
- return page
-
- def _write_results_html_file(self, result_summary):
- """Write results.html which is a summary of tests that failed.
-
- Args:
- result_summary: a summary of the results :)
-
- Returns:
- True if any results were written (since expected failures may be
- omitted)
- """
- # test failures
- if self._options.full_results_html:
- results_title = "Test Failures"
- test_files = result_summary.failures.keys()
- else:
- results_title = "Unexpected Test Failures"
- unexpected_failures = self._get_failures(result_summary,
- include_crashes=True)
- test_files = unexpected_failures.keys()
- if not len(test_files):
- return False
-
- out_filename = os.path.join(self._options.results_directory,
- "results.html")
- with codecs.open(out_filename, "w", "utf-8") as results_file:
- html = self._results_html(test_files, result_summary.failures, results_title)
- results_file.write(html)
-
- return True
-
- def _show_results_html_file(self):
- """Shows the results.html page."""
- results_filename = os.path.join(self._options.results_directory,
- "results.html")
- self._port.show_results_html_file(results_filename)
-
-
-def read_test_files(files):
- tests = []
- for file in files:
- try:
- with codecs.open(file, 'r', 'utf-8') as file_contents:
- # FIXME: This could be cleaner using a list comprehension.
- for line in file_contents:
- line = test_expectations.strip_comments(line)
- if line:
- tests.append(line)
- except IOError, e:
- if e.errno == errno.ENOENT:
- _log.critical('')
- _log.critical('--test-list file "%s" not found' % file)
- raise
- return tests
-
-
-def run(port, options, args, regular_output=sys.stderr,
- buildbot_output=sys.stdout):
- """Run the tests.
-
- Args:
- port: Port object for port-specific behavior
- options: a dictionary of command line options
- args: a list of sub directories or files to test
- regular_output: a stream-like object that we can send logging/debug
- output to
- buildbot_output: a stream-like object that we can write all output that
- is intended to be parsed by the buildbot to
- Returns:
- the number of unexpected results that occurred, or -1 if there is an
- error.
-
- """
- _set_up_derived_options(port, options)
-
- printer = printing.Printer(port, options, regular_output, buildbot_output,
- int(options.child_processes), options.experimental_fully_parallel)
- if options.help_printing:
- printer.help_printing()
- printer.cleanup()
- return 0
-
- last_unexpected_results = _gather_unexpected_results(options)
- if options.print_last_failures:
- printer.write("\n".join(last_unexpected_results) + "\n")
- printer.cleanup()
- return 0
-
- broker = message_broker.get(port, options)
-
- # We wrap any parts of the run that are slow or likely to raise exceptions
- # in a try/finally to ensure that we clean up the logging configuration.
- num_unexpected_results = -1
- try:
- test_runner = TestRunner(port, options, printer, broker)
- test_runner._print_config()
-
- printer.print_update("Collecting tests ...")
- try:
- test_runner.collect_tests(args, last_unexpected_results)
- except IOError, e:
- if e.errno == errno.ENOENT:
- return -1
- raise
-
- printer.print_update("Parsing expectations ...")
- if options.lint_test_files:
- return test_runner.lint()
- test_runner.parse_expectations(port.test_platform_name(),
- options.configuration == 'Debug')
-
- printer.print_update("Checking build ...")
- if not port.check_build(test_runner.needs_http()):
- _log.error("Build check failed")
- return -1
-
- result_summary = test_runner.set_up_run()
- if result_summary:
- num_unexpected_results = test_runner.run(result_summary)
- test_runner.clean_up_run()
- _log.debug("Testing completed, Exit status: %d" %
- num_unexpected_results)
- finally:
- broker.cleanup()
- printer.cleanup()
-
- return num_unexpected_results
-
-
-def _set_up_derived_options(port_obj, options):
- """Sets the options values that depend on other options values."""
-
- if options.worker_model == 'inline':
- if options.child_processes and int(options.child_processes) > 1:
- _log.warning("--worker-model=inline overrides --child-processes")
- options.child_processes = "1"
- if not options.child_processes:
- options.child_processes = os.environ.get("WEBKIT_TEST_CHILD_PROCESSES",
- str(port_obj.default_child_processes()))
-
- if not options.configuration:
- options.configuration = port_obj.default_configuration()
-
- if options.pixel_tests is None:
- options.pixel_tests = True
-
- if not options.use_apache:
- options.use_apache = sys.platform in ('darwin', 'linux2')
-
- if not os.path.isabs(options.results_directory):
- # This normalizes the path to the build dir.
- # FIXME: how this happens is not at all obvious; this is a dumb
- # interface and should be cleaned up.
- options.results_directory = port_obj.results_directory()
-
- if not options.time_out_ms:
- if options.configuration == "Debug":
- options.time_out_ms = str(2 * TestRunner.DEFAULT_TEST_TIMEOUT_MS)
- else:
- options.time_out_ms = str(TestRunner.DEFAULT_TEST_TIMEOUT_MS)
-
- options.slow_time_out_ms = str(5 * int(options.time_out_ms))
-
-
-def _gather_unexpected_results(options):
- """Returns the unexpected results from the previous run, if any."""
- last_unexpected_results = []
- if options.print_last_failures or options.retest_last_failures:
- unexpected_results_filename = os.path.join(
- options.results_directory, "unexpected_results.json")
- with codecs.open(unexpected_results_filename, "r", "utf-8") as file:
- results = simplejson.load(file)
- last_unexpected_results = results['tests'].keys()
- return last_unexpected_results
-
-
-def _compat_shim_callback(option, opt_str, value, parser):
- print "Ignoring unsupported option: %s" % opt_str
-
-
-def _compat_shim_option(option_name, **kwargs):
- return optparse.make_option(option_name, action="callback",
- callback=_compat_shim_callback,
- help="Ignored, for old-run-webkit-tests compat only.", **kwargs)
-
-
-def parse_args(args=None):
- """Provides a default set of command line args.
-
- Returns a tuple of options, args from optparse"""
-
- # FIXME: All of these options should be stored closer to the code which
- # FIXME: actually uses them. configuration_options should move
- # FIXME: to WebKitPort and be shared across all scripts.
- configuration_options = [
- optparse.make_option("-t", "--target", dest="configuration",
- help="(DEPRECATED)"),
- # FIXME: --help should display which configuration is default.
- optparse.make_option('--debug', action='store_const', const='Debug',
- dest="configuration",
- help='Set the configuration to Debug'),
- optparse.make_option('--release', action='store_const',
- const='Release', dest="configuration",
- help='Set the configuration to Release'),
- # old-run-webkit-tests also accepts -c, --configuration CONFIGURATION.
- ]
-
- print_options = printing.print_options()
-
- # FIXME: These options should move onto the ChromiumPort.
- chromium_options = [
- optparse.make_option("--chromium", action="store_true", default=False,
- help="use the Chromium port"),
- optparse.make_option("--startup-dialog", action="store_true",
- default=False, help="create a dialog on DumpRenderTree startup"),
- optparse.make_option("--gp-fault-error-box", action="store_true",
- default=False, help="enable Windows GP fault error box"),
- optparse.make_option("--multiple-loads",
- type="int", help="turn on multiple loads of each test"),
- optparse.make_option("--js-flags",
- type="string", help="JavaScript flags to pass to tests"),
- optparse.make_option("--nocheck-sys-deps", action="store_true",
- default=False,
- help="Don't check the system dependencies (themes)"),
- optparse.make_option("--use-drt", action="store_true",
- default=None,
- help="Use DumpRenderTree instead of test_shell"),
- optparse.make_option("--accelerated-compositing",
- action="store_true",
- help="Use hardware-accelated compositing for rendering"),
- optparse.make_option("--no-accelerated-compositing",
- action="store_false",
- dest="accelerated_compositing",
- help="Don't use hardware-accelerated compositing for rendering"),
- optparse.make_option("--accelerated-2d-canvas",
- action="store_true",
- help="Use hardware-accelerated 2D Canvas calls"),
- optparse.make_option("--no-accelerated-2d-canvas",
- action="store_false",
- dest="accelerated_2d_canvas",
- help="Don't use hardware-accelerated 2D Canvas calls"),
- ]
-
- # Missing Mac-specific old-run-webkit-tests options:
- # FIXME: Need: -g, --guard for guard malloc support on Mac.
- # FIXME: Need: -l --leaks Enable leaks checking.
- # FIXME: Need: --sample-on-timeout Run sample on timeout
-
- old_run_webkit_tests_compat = [
- # NRWT doesn't generate results by default anyway.
- _compat_shim_option("--no-new-test-results"),
- # NRWT doesn't sample on timeout yet anyway.
- _compat_shim_option("--no-sample-on-timeout"),
- # FIXME: NRWT needs to support remote links eventually.
- _compat_shim_option("--use-remote-links-to-tests"),
- # FIXME: NRWT doesn't need this option as much since failures are
- # designed to be cheap. We eventually plan to add this support.
- _compat_shim_option("--exit-after-n-failures", nargs=1, type="int"),
- ]
-
- results_options = [
- # NEED for bots: --use-remote-links-to-tests Link to test files
- # within the SVN repository in the results.
- optparse.make_option("-p", "--pixel-tests", action="store_true",
- dest="pixel_tests", help="Enable pixel-to-pixel PNG comparisons"),
- optparse.make_option("--no-pixel-tests", action="store_false",
- dest="pixel_tests", help="Disable pixel-to-pixel PNG comparisons"),
- optparse.make_option("--tolerance",
- help="Ignore image differences less than this percentage (some "
- "ports may ignore this option)", type="float"),
- optparse.make_option("--results-directory",
- default="layout-test-results",
- help="Output results directory source dir, relative to Debug or "
- "Release"),
- optparse.make_option("--new-baseline", action="store_true",
- default=False, help="Save all generated results as new baselines "
- "into the platform directory, overwriting whatever's "
- "already there."),
- optparse.make_option("--reset-results", action="store_true",
- default=False, help="Reset any existing baselines to the "
- "generated results"),
- optparse.make_option("--no-show-results", action="store_false",
- default=True, dest="show_results",
- help="Don't launch a browser with results after the tests "
- "are done"),
- # FIXME: We should have a helper function to do this sort of
- # deprectated mapping and automatically log, etc.
- optparse.make_option("--noshow-results", action="store_false",
- dest="show_results",
- help="Deprecated, same as --no-show-results."),
- optparse.make_option("--no-launch-safari", action="store_false",
- dest="show_results",
- help="old-run-webkit-tests compat, same as --noshow-results."),
- # old-run-webkit-tests:
- # --[no-]launch-safari Launch (or do not launch) Safari to display
- # test results (default: launch)
- optparse.make_option("--full-results-html", action="store_true",
- default=False,
- help="Show all failures in results.html, rather than only "
- "regressions"),
- optparse.make_option("--clobber-old-results", action="store_true",
- default=False, help="Clobbers test results from previous runs."),
- optparse.make_option("--platform",
- help="Override the platform for expected results"),
- optparse.make_option("--no-record-results", action="store_false",
- default=True, dest="record_results",
- help="Don't record the results."),
- # old-run-webkit-tests also has HTTP toggle options:
- # --[no-]http Run (or do not run) http tests
- # (default: run)
- ]
-
- test_options = [
- optparse.make_option("--build", dest="build",
- action="store_true", default=True,
- help="Check to ensure the DumpRenderTree build is up-to-date "
- "(default)."),
- optparse.make_option("--no-build", dest="build",
- action="store_false", help="Don't check to see if the "
- "DumpRenderTree build is up-to-date."),
- # old-run-webkit-tests has --valgrind instead of wrapper.
- optparse.make_option("--wrapper",
- help="wrapper command to insert before invocations of "
- "DumpRenderTree; option is split on whitespace before "
- "running. (Example: --wrapper='valgrind --smc-check=all')"),
- # old-run-webkit-tests:
- # -i|--ignore-tests Comma-separated list of directories
- # or tests to ignore
- optparse.make_option("--test-list", action="append",
- help="read list of tests to run from file", metavar="FILE"),
- # old-run-webkit-tests uses --skipped==[default|ignore|only]
- # instead of --force:
- optparse.make_option("--force", action="store_true", default=False,
- help="Run all tests, even those marked SKIP in the test list"),
- optparse.make_option("--use-apache", action="store_true",
- default=False, help="Whether to use apache instead of lighttpd."),
- optparse.make_option("--time-out-ms",
- help="Set the timeout for each test"),
- # old-run-webkit-tests calls --randomize-order --random:
- optparse.make_option("--randomize-order", action="store_true",
- default=False, help=("Run tests in random order (useful "
- "for tracking down corruption)")),
- optparse.make_option("--run-chunk",
- help=("Run a specified chunk (n:l), the nth of len l, "
- "of the layout tests")),
- optparse.make_option("--run-part", help=("Run a specified part (n:m), "
- "the nth of m parts, of the layout tests")),
- # old-run-webkit-tests calls --batch-size: --nthly n
- # Restart DumpRenderTree every n tests (default: 1000)
- optparse.make_option("--batch-size",
- help=("Run a the tests in batches (n), after every n tests, "
- "DumpRenderTree is relaunched."), type="int", default=0),
- # old-run-webkit-tests calls --run-singly: -1|--singly
- # Isolate each test case run (implies --nthly 1 --verbose)
- optparse.make_option("--run-singly", action="store_true",
- default=False, help="run a separate DumpRenderTree for each test"),
- optparse.make_option("--child-processes",
- help="Number of DumpRenderTrees to run in parallel."),
- # FIXME: Display default number of child processes that will run.
- optparse.make_option("--worker-model", action="store",
- default="threads", help=("controls worker model. Valid values are "
- "'inline' and 'threads' (default).")),
- optparse.make_option("--experimental-fully-parallel",
- action="store_true", default=False,
- help="run all tests in parallel"),
- # FIXME: Need --exit-after-n-failures N
- # Exit after the first N failures instead of running all tests
- # FIXME: Need --exit-after-n-crashes N
- # Exit after the first N crashes instead of running all tests
- # FIXME: consider: --iterations n
- # Number of times to run the set of tests (e.g. ABCABCABC)
- optparse.make_option("--print-last-failures", action="store_true",
- default=False, help="Print the tests in the last run that "
- "had unexpected failures (or passes) and then exit."),
- optparse.make_option("--retest-last-failures", action="store_true",
- default=False, help="re-test the tests in the last run that "
- "had unexpected failures (or passes)."),
- optparse.make_option("--retry-failures", action="store_true",
- default=True,
- help="Re-try any tests that produce unexpected results (default)"),
- optparse.make_option("--no-retry-failures", action="store_false",
- dest="retry_failures",
- help="Don't re-try any tests that produce unexpected results."),
- ]
-
- misc_options = [
- optparse.make_option("--lint-test-files", action="store_true",
- default=False, help=("Makes sure the test files parse for all "
- "configurations. Does not run any tests.")),
- ]
-
- # FIXME: Move these into json_results_generator.py
- results_json_options = [
- optparse.make_option("--master-name", help="The name of the buildbot master."),
- optparse.make_option("--builder-name", default="DUMMY_BUILDER_NAME",
- help=("The name of the builder shown on the waterfall running "
- "this script e.g. WebKit.")),
- optparse.make_option("--build-name", default="DUMMY_BUILD_NAME",
- help=("The name of the builder used in its path, e.g. "
- "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.")),
- optparse.make_option("--upload-full-results",
- action="store_true",
- default=False,
- help="If true, upload full json results to server."),
- ]
-
- option_list = (configuration_options + print_options +
- chromium_options + results_options + test_options +
- misc_options + results_json_options +
- old_run_webkit_tests_compat)
- option_parser = optparse.OptionParser(option_list=option_list)
-
- return option_parser.parse_args(args)
-
-
-def main():
- options, args = parse_args()
- port_obj = port.get(options.platform, options)
- return run(port_obj, options, args)
-
-
-if '__main__' == __name__:
- try:
- sys.exit(main())
- except KeyboardInterrupt:
- # this mirrors what the shell normally does
- sys.exit(signal.SIGINT + 128)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py
deleted file mode 100644
index 20a4ac0..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py
+++ /dev/null
@@ -1,540 +0,0 @@
-#!/usr/bin/python
-# Copyright (C) 2010 Google Inc. All rights reserved.
-# Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Szeged
-#
-# 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 run_webkit_tests."""
-
-import codecs
-import itertools
-import logging
-import os
-import Queue
-import shutil
-import sys
-import tempfile
-import thread
-import time
-import threading
-import unittest
-
-from webkitpy.common import array_stream
-from webkitpy.common.system import outputcapture
-from webkitpy.common.system import user
-from webkitpy.layout_tests import port
-from webkitpy.layout_tests import run_webkit_tests
-from webkitpy.layout_tests.layout_package import dump_render_tree_thread
-from webkitpy.layout_tests.port.test import TestPort, TestDriver
-from webkitpy.python24.versioning import compare_version
-from webkitpy.test.skip import skip_if
-
-from webkitpy.thirdparty.mock import Mock
-
-
-class MockUser():
- def __init__(self):
- self.url = None
-
- def open_url(self, url):
- self.url = url
-
-
-def passing_run(extra_args=None, port_obj=None, record_results=False,
- tests_included=False):
- extra_args = extra_args or []
- args = ['--print', 'nothing']
- if not '--platform' in extra_args:
- args.extend(['--platform', 'test'])
- if not record_results:
- args.append('--no-record-results')
- if not '--child-processes' in extra_args:
- args.extend(['--worker-model', 'inline'])
- args.extend(extra_args)
- if not tests_included:
- # We use the glob to test that globbing works.
- args.extend(['passes',
- 'http/tests',
- 'websocket/tests',
- 'failures/expected/*'])
- options, parsed_args = run_webkit_tests.parse_args(args)
- if not port_obj:
- port_obj = port.get(port_name=options.platform, options=options,
- user=MockUser())
- res = run_webkit_tests.run(port_obj, options, parsed_args)
- return res == 0
-
-
-def logging_run(extra_args=None, port_obj=None, tests_included=False):
- extra_args = extra_args or []
- args = ['--no-record-results']
- if not '--platform' in extra_args:
- args.extend(['--platform', 'test'])
- if not '--child-processes' in extra_args:
- args.extend(['--worker-model', 'inline'])
- args.extend(extra_args)
- if not tests_included:
- args.extend(['passes',
- 'http/tests',
- 'websocket/tests',
- 'failures/expected/*'])
-
- oc = outputcapture.OutputCapture()
- try:
- oc.capture_output()
- options, parsed_args = run_webkit_tests.parse_args(args)
- user = MockUser()
- if not port_obj:
- port_obj = port.get(port_name=options.platform, options=options,
- user=user)
- buildbot_output = array_stream.ArrayStream()
- regular_output = array_stream.ArrayStream()
- res = run_webkit_tests.run(port_obj, options, parsed_args,
- buildbot_output=buildbot_output,
- regular_output=regular_output)
- finally:
- oc.restore_output()
- return (res, buildbot_output, regular_output, user)
-
-
-def get_tests_run(extra_args=None, tests_included=False, flatten_batches=False):
- extra_args = extra_args or []
- args = [
- '--print', 'nothing',
- '--platform', 'test',
- '--no-record-results',
- '--worker-model', 'inline']
- args.extend(extra_args)
- if not tests_included:
- # Not including http tests since they get run out of order (that
- # behavior has its own test, see test_get_test_file_queue)
- args.extend(['passes', 'failures'])
- options, parsed_args = run_webkit_tests.parse_args(args)
- user = MockUser()
-
- test_batches = []
-
- class RecordingTestDriver(TestDriver):
- def __init__(self, port, worker_number):
- TestDriver.__init__(self, port, worker_number)
- self._current_test_batch = None
-
- def poll(self):
- # So that we don't create a new driver for every test
- return None
-
- def stop(self):
- self._current_test_batch = None
-
- def run_test(self, test_input):
- if self._current_test_batch is None:
- self._current_test_batch = []
- test_batches.append(self._current_test_batch)
- test_name = self._port.relative_test_filename(test_input.filename)
- self._current_test_batch.append(test_name)
- return TestDriver.run_test(self, test_input)
-
- class RecordingTestPort(TestPort):
- def create_driver(self, worker_number):
- return RecordingTestDriver(self, worker_number)
-
- recording_port = RecordingTestPort(options=options, user=user)
- logging_run(extra_args=args, port_obj=recording_port, tests_included=True)
-
- if flatten_batches:
- return list(itertools.chain(*test_batches))
-
- return test_batches
-
-class MainTest(unittest.TestCase):
- def test_accelerated_compositing(self):
- # This just tests that we recognize the command line args
- self.assertTrue(passing_run(['--accelerated-compositing']))
- self.assertTrue(passing_run(['--no-accelerated-compositing']))
-
- def test_accelerated_2d_canvas(self):
- # This just tests that we recognize the command line args
- self.assertTrue(passing_run(['--accelerated-2d-canvas']))
- self.assertTrue(passing_run(['--no-accelerated-2d-canvas']))
-
- def test_basic(self):
- self.assertTrue(passing_run())
-
- def test_batch_size(self):
- batch_tests_run = get_tests_run(['--batch-size', '2'])
- for batch in batch_tests_run:
- self.assertTrue(len(batch) <= 2, '%s had too many tests' % ', '.join(batch))
-
- def test_child_process_1(self):
- (res, buildbot_output, regular_output, user) = logging_run(
- ['--print', 'config', '--child-processes', '1'])
- self.assertTrue('Running one DumpRenderTree\n'
- in regular_output.get())
-
- def test_child_processes_2(self):
- (res, buildbot_output, regular_output, user) = logging_run(
- ['--print', 'config', '--child-processes', '2'])
- self.assertTrue('Running 2 DumpRenderTrees in parallel\n'
- in regular_output.get())
-
- def test_exception_raised(self):
- self.assertRaises(ValueError, logging_run,
- ['failures/expected/exception.html'], tests_included=True)
-
- def test_full_results_html(self):
- # FIXME: verify html?
- self.assertTrue(passing_run(['--full-results-html']))
-
- def test_help_printing(self):
- res, out, err, user = logging_run(['--help-printing'])
- self.assertEqual(res, 0)
- self.assertTrue(out.empty())
- self.assertFalse(err.empty())
-
- def test_hung_thread(self):
- res, out, err, user = logging_run(['--run-singly', '--time-out-ms=50',
- 'failures/expected/hang.html'],
- tests_included=True)
- self.assertEqual(res, 0)
- self.assertFalse(out.empty())
- self.assertFalse(err.empty())
-
- def test_keyboard_interrupt(self):
- # Note that this also tests running a test marked as SKIP if
- # you specify it explicitly.
- self.assertRaises(KeyboardInterrupt, logging_run,
- ['failures/expected/keyboard.html'], tests_included=True)
-
- def test_last_results(self):
- passing_run(['--clobber-old-results'], record_results=True)
- (res, buildbot_output, regular_output, user) = logging_run(
- ['--print-last-failures'])
- self.assertEqual(regular_output.get(), ['\n\n'])
- self.assertEqual(buildbot_output.get(), [])
-
- def test_lint_test_files(self):
- # FIXME: add errors?
- res, out, err, user = logging_run(['--lint-test-files'],
- tests_included=True)
- self.assertEqual(res, 0)
- self.assertTrue(out.empty())
- self.assertTrue(any(['lint succeeded' in msg for msg in err.get()]))
-
- def test_no_tests_found(self):
- res, out, err, user = logging_run(['resources'], tests_included=True)
- self.assertEqual(res, -1)
- self.assertTrue(out.empty())
- self.assertTrue('No tests to run.\n' in err.get())
-
- def test_no_tests_found_2(self):
- res, out, err, user = logging_run(['foo'], tests_included=True)
- self.assertEqual(res, -1)
- self.assertTrue(out.empty())
- self.assertTrue('No tests to run.\n' in err.get())
-
- def test_randomize_order(self):
- # FIXME: verify order was shuffled
- self.assertTrue(passing_run(['--randomize-order']))
-
- def test_run_chunk(self):
- # Test that we actually select the right chunk
- all_tests_run = get_tests_run(flatten_batches=True)
- chunk_tests_run = get_tests_run(['--run-chunk', '1:4'], flatten_batches=True)
- self.assertEquals(all_tests_run[4:8], chunk_tests_run)
-
- # Test that we wrap around if the number of tests is not evenly divisible by the chunk size
- tests_to_run = ['passes/error.html', 'passes/image.html', 'passes/platform_image.html', 'passes/text.html']
- chunk_tests_run = get_tests_run(['--run-chunk', '1:3'] + tests_to_run, tests_included=True, flatten_batches=True)
- self.assertEquals(['passes/text.html', 'passes/error.html', 'passes/image.html'], chunk_tests_run)
-
- def test_run_force(self):
- # This raises an exception because we run
- # failures/expected/exception.html, which is normally SKIPped.
- self.assertRaises(ValueError, logging_run, ['--force'])
-
- def test_run_part(self):
- # Test that we actually select the right part
- tests_to_run = ['passes/error.html', 'passes/image.html', 'passes/platform_image.html', 'passes/text.html']
- tests_run = get_tests_run(['--run-part', '1:2'] + tests_to_run, tests_included=True, flatten_batches=True)
- self.assertEquals(['passes/error.html', 'passes/image.html'], tests_run)
-
- # Test that we wrap around if the number of tests is not evenly divisible by the chunk size
- # (here we end up with 3 parts, each with 2 tests, and we only have 4 tests total, so the
- # last part repeats the first two tests).
- chunk_tests_run = get_tests_run(['--run-part', '3:3'] + tests_to_run, tests_included=True, flatten_batches=True)
- self.assertEquals(['passes/error.html', 'passes/image.html'], chunk_tests_run)
-
- def test_run_singly(self):
- batch_tests_run = get_tests_run(['--run-singly'])
- for batch in batch_tests_run:
- self.assertEquals(len(batch), 1, '%s had too many tests' % ', '.join(batch))
-
- def test_single_file(self):
- tests_run = get_tests_run(['passes/text.html'], tests_included=True, flatten_batches=True)
- self.assertEquals(['passes/text.html'], tests_run)
-
- def test_test_list(self):
- filename = tempfile.mktemp()
- tmpfile = file(filename, mode='w+')
- tmpfile.write('passes/text.html')
- tmpfile.close()
- tests_run = get_tests_run(['--test-list=%s' % filename], tests_included=True, flatten_batches=True)
- self.assertEquals(['passes/text.html'], tests_run)
- os.remove(filename)
- res, out, err, user = logging_run(['--test-list=%s' % filename],
- tests_included=True)
- self.assertEqual(res, -1)
- self.assertFalse(err.empty())
-
- def test_unexpected_failures(self):
- # Run tests including the unexpected failures.
- self._url_opened = None
- res, out, err, user = logging_run(tests_included=True)
- self.assertEqual(res, 1)
- self.assertFalse(out.empty())
- self.assertFalse(err.empty())
- self.assertEqual(user.url, '/tmp/layout-test-results/results.html')
-
- def test_results_directory_absolute(self):
- # We run a configuration that should fail, to generate output, then
- # look for what the output results url was.
-
- tmpdir = tempfile.mkdtemp()
- res, out, err, user = logging_run(['--results-directory=' + tmpdir],
- tests_included=True)
- self.assertEqual(user.url, os.path.join(tmpdir, 'results.html'))
- shutil.rmtree(tmpdir, ignore_errors=True)
-
- def test_results_directory_default(self):
- # We run a configuration that should fail, to generate output, then
- # look for what the output results url was.
-
- # This is the default location.
- res, out, err, user = logging_run(tests_included=True)
- self.assertEqual(user.url, '/tmp/layout-test-results/results.html')
-
- def test_results_directory_relative(self):
- # We run a configuration that should fail, to generate output, then
- # look for what the output results url was.
-
- res, out, err, user = logging_run(['--results-directory=foo'],
- tests_included=True)
- self.assertEqual(user.url, '/tmp/foo/results.html')
-
- def test_tolerance(self):
- class ImageDiffTestPort(TestPort):
- def diff_image(self, expected_contents, actual_contents,
- diff_filename=None):
- self.tolerance_used_for_diff_image = self._options.tolerance
- return True
-
- def get_port_for_run(args):
- options, parsed_args = run_webkit_tests.parse_args(args)
- test_port = ImageDiffTestPort(options=options, user=MockUser())
- passing_run(args, port_obj=test_port, tests_included=True)
- return test_port
-
- base_args = ['--pixel-tests', 'failures/expected/*']
-
- # If we pass in an explicit tolerance argument, then that will be used.
- test_port = get_port_for_run(base_args + ['--tolerance', '.1'])
- self.assertEqual(0.1, test_port.tolerance_used_for_diff_image)
- test_port = get_port_for_run(base_args + ['--tolerance', '0'])
- self.assertEqual(0, test_port.tolerance_used_for_diff_image)
-
- # Otherwise the port's default tolerance behavior (including ignoring it)
- # should be used.
- test_port = get_port_for_run(base_args)
- self.assertEqual(None, test_port.tolerance_used_for_diff_image)
-
- def test_worker_model__inline(self):
- self.assertTrue(passing_run(['--worker-model', 'inline']))
-
- def test_worker_model__threads(self):
- self.assertTrue(passing_run(['--worker-model', 'threads']))
-
- def test_worker_model__processes(self):
- self.assertRaises(ValueError, logging_run,
- ['--worker-model', 'processes'])
-
- def test_worker_model__unknown(self):
- self.assertRaises(ValueError, logging_run,
- ['--worker-model', 'unknown'])
-
-MainTest = skip_if(MainTest, sys.platform == 'cygwin' and compare_version(sys, '2.6')[0] < 0, 'new-run-webkit-tests tests hang on Cygwin Python 2.5.2')
-
-
-
-def _mocked_open(original_open, file_list):
- def _wrapper(name, mode, encoding):
- if name.find("-expected.") != -1 and mode.find("w") != -1:
- # we don't want to actually write new baselines, so stub these out
- name.replace('\\', '/')
- file_list.append(name)
- return original_open(os.devnull, mode, encoding)
- return original_open(name, mode, encoding)
- return _wrapper
-
-
-class RebaselineTest(unittest.TestCase):
- def assertBaselines(self, file_list, file):
- "assert that the file_list contains the baselines."""
- for ext in [".txt", ".png", ".checksum"]:
- baseline = file + "-expected" + ext
- self.assertTrue(any(f.find(baseline) != -1 for f in file_list))
-
- # FIXME: Add tests to ensure that we're *not* writing baselines when we're not
- # supposed to be.
-
- def disabled_test_reset_results(self):
- # FIXME: This test is disabled until we can rewrite it to use a
- # mock filesystem.
- #
- # Test that we update expectations in place. If the expectation
- # is missing, update the expected generic location.
- file_list = []
- passing_run(['--pixel-tests',
- '--reset-results',
- 'passes/image.html',
- 'failures/expected/missing_image.html'],
- tests_included=True)
- self.assertEqual(len(file_list), 6)
- self.assertBaselines(file_list,
- "data/passes/image")
- self.assertBaselines(file_list,
- "data/failures/expected/missing_image")
-
- def disabled_test_new_baseline(self):
- # FIXME: This test is disabled until we can rewrite it to use a
- # mock filesystem.
- #
- # Test that we update the platform expectations. If the expectation
- # is mssing, then create a new expectation in the platform dir.
- file_list = []
- original_open = codecs.open
- try:
- # Test that we update the platform expectations. If the expectation
- # is mssing, then create a new expectation in the platform dir.
- file_list = []
- codecs.open = _mocked_open(original_open, file_list)
- passing_run(['--pixel-tests',
- '--new-baseline',
- 'passes/image.html',
- 'failures/expected/missing_image.html'],
- tests_included=True)
- self.assertEqual(len(file_list), 6)
- self.assertBaselines(file_list,
- "data/platform/test/passes/image")
- self.assertBaselines(file_list,
- "data/platform/test/failures/expected/missing_image")
- finally:
- codecs.open = original_open
-
-
-class TestRunnerWrapper(run_webkit_tests.TestRunner):
- def _get_test_input_for_file(self, test_file):
- return test_file
-
-
-class TestRunnerTest(unittest.TestCase):
- def test_results_html(self):
- mock_port = Mock()
- mock_port.relative_test_filename = lambda name: name
- mock_port.filename_to_uri = lambda name: name
-
- runner = run_webkit_tests.TestRunner(port=mock_port, options=Mock(),
- printer=Mock(), message_broker=Mock())
- expected_html = u"""<html>
- <head>
- <title>Layout Test Results (time)</title>
- </head>
- <body>
- <h2>Title (time)</h2>
- <p><a href='test_path'>test_path</a><br />
-</p>
-</body></html>
-"""
- html = runner._results_html(["test_path"], {}, "Title", override_time="time")
- self.assertEqual(html, expected_html)
-
- def test_shard_tests(self):
- # Test that _shard_tests in run_webkit_tests.TestRunner really
- # put the http tests first in the queue.
- runner = TestRunnerWrapper(port=Mock(), options=Mock(),
- printer=Mock(), message_broker=Mock())
-
- test_list = [
- "LayoutTests/websocket/tests/unicode.htm",
- "LayoutTests/animations/keyframes.html",
- "LayoutTests/http/tests/security/view-source-no-refresh.html",
- "LayoutTests/websocket/tests/websocket-protocol-ignored.html",
- "LayoutTests/fast/css/display-none-inline-style-change-crash.html",
- "LayoutTests/http/tests/xmlhttprequest/supported-xml-content-types.html",
- "LayoutTests/dom/html/level2/html/HTMLAnchorElement03.html",
- "LayoutTests/ietestcenter/Javascript/11.1.5_4-4-c-1.html",
- "LayoutTests/dom/html/level2/html/HTMLAnchorElement06.html",
- ]
-
- expected_tests_to_http_lock = set([
- 'LayoutTests/websocket/tests/unicode.htm',
- 'LayoutTests/http/tests/security/view-source-no-refresh.html',
- 'LayoutTests/websocket/tests/websocket-protocol-ignored.html',
- 'LayoutTests/http/tests/xmlhttprequest/supported-xml-content-types.html',
- ])
-
- # FIXME: Ideally the HTTP tests don't have to all be in one shard.
- single_thread_results = runner._shard_tests(test_list, False)
- multi_thread_results = runner._shard_tests(test_list, True)
-
- self.assertEqual("tests_to_http_lock", single_thread_results[0][0])
- self.assertEqual(expected_tests_to_http_lock, set(single_thread_results[0][1]))
- self.assertEqual("tests_to_http_lock", multi_thread_results[0][0])
- self.assertEqual(expected_tests_to_http_lock, set(multi_thread_results[0][1]))
-
-
-class DryrunTest(unittest.TestCase):
- # 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.
- def test_darwin(self):
- if sys.platform != "darwin":
- return
-
- self.assertTrue(passing_run(['--platform', 'test']))
- self.assertTrue(passing_run(['--platform', 'dryrun',
- 'fast/html']))
- self.assertTrue(passing_run(['--platform', 'dryrun-mac',
- 'fast/html']))
-
- def test_test(self):
- self.assertTrue(passing_run(['--platform', 'dryrun-test',
- '--pixel-tests']))
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/__init__.py b/WebKitTools/Scripts/webkitpy/layout_tests/test_types/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/__init__.py
+++ /dev/null
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/image_diff.py b/WebKitTools/Scripts/webkitpy/layout_tests/test_types/image_diff.py
deleted file mode 100644
index da466c8..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/image_diff.py
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Compares the image output of a test to the expected image output.
-
-Compares hashes for the generated and expected images. If the output doesn't
-match, returns FailureImageHashMismatch and outputs both hashes into the layout
-test results directory.
-"""
-
-from __future__ import with_statement
-
-import codecs
-import errno
-import logging
-import os
-import shutil
-
-from webkitpy.layout_tests.layout_package import test_failures
-from webkitpy.layout_tests.test_types import test_type_base
-
-# Cache whether we have the image_diff executable available.
-_compare_available = True
-_compare_msg_printed = False
-
-_log = logging.getLogger("webkitpy.layout_tests.test_types.image_diff")
-
-
-class ImageDiff(test_type_base.TestTypeBase):
-
- def _save_baseline_files(self, filename, image, image_hash,
- generate_new_baseline):
- """Saves new baselines for the PNG and checksum.
-
- Args:
- filename: test filename
- image: a image output
- image_hash: a checksum of the image
- generate_new_baseline: whether to generate a new, platform-specific
- baseline, or update the existing one
- """
- self._save_baseline_data(filename, image, ".png", encoding=None,
- generate_new_baseline=generate_new_baseline)
- self._save_baseline_data(filename, image_hash, ".checksum",
- encoding="ascii",
- generate_new_baseline=generate_new_baseline)
-
- def _copy_image(self, filename, actual_image, expected_image):
- self.write_output_files(filename, '.png',
- output=actual_image, expected=expected_image,
- encoding=None, print_text_diffs=False)
-
- def _copy_image_hash(self, filename, actual_image_hash, expected_image_hash):
- self.write_output_files(filename, '.checksum',
- actual_image_hash, expected_image_hash,
- encoding="ascii", print_text_diffs=False)
-
- def _create_diff_image(self, port, filename, actual_image, expected_image):
- """Creates the visual diff of the expected/actual PNGs.
-
- Returns True if the images are different.
- """
- diff_filename = self.output_filename(filename,
- self.FILENAME_SUFFIX_COMPARE)
- return port.diff_image(actual_image, expected_image, diff_filename)
-
- def compare_output(self, port, filename, test_args, actual_test_output,
- expected_test_output):
- """Implementation of CompareOutput that checks the output image and
- checksum against the expected files from the LayoutTest directory.
- """
- failures = []
-
- # If we didn't produce a hash file, this test must be text-only.
- if actual_test_output.image_hash is None:
- return failures
-
- # If we're generating a new baseline, we pass.
- if test_args.new_baseline or test_args.reset_results:
- self._save_baseline_files(filename, actual_test_output.image,
- actual_test_output.image_hash,
- test_args.new_baseline)
- return failures
-
- if not expected_test_output.image:
- # Report a missing expected PNG file.
- self._copy_image(filename, actual_test_output.image, expected_image=None)
- self._copy_image_hash(filename, actual_test_output.image_hash,
- expected_test_output.image_hash)
- failures.append(test_failures.FailureMissingImage())
- return failures
- if not expected_test_output.image_hash:
- # Report a missing expected checksum file.
- self._copy_image(filename, actual_test_output.image,
- expected_test_output.image)
- self._copy_image_hash(filename, actual_test_output.image_hash,
- expected_image_hash=None)
- failures.append(test_failures.FailureMissingImageHash())
- return failures
-
- if actual_test_output.image_hash == expected_test_output.image_hash:
- # Hash matched (no diff needed, okay to return).
- return failures
-
- self._copy_image(filename, actual_test_output.image,
- expected_test_output.image)
- self._copy_image_hash(filename, actual_test_output.image_hash,
- expected_test_output.image_hash)
-
- # Even though we only use the result in one codepath below but we
- # still need to call CreateImageDiff for other codepaths.
- images_are_different = self._create_diff_image(port, filename,
- actual_test_output.image,
- expected_test_output.image)
- if not images_are_different:
- failures.append(test_failures.FailureImageHashIncorrect())
- else:
- failures.append(test_failures.FailureImageHashMismatch())
-
- return failures
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/test_type_base.py b/WebKitTools/Scripts/webkitpy/layout_tests/test_types/test_type_base.py
deleted file mode 100644
index 4b96b3a..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/test_type_base.py
+++ /dev/null
@@ -1,223 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Defines the interface TestTypeBase which other test types inherit from.
-
-Also defines the TestArguments "struct" to pass them additional arguments.
-"""
-
-from __future__ import with_statement
-
-import codecs
-import cgi
-import errno
-import logging
-import os.path
-
-_log = logging.getLogger("webkitpy.layout_tests.test_types.test_type_base")
-
-
-class TestArguments(object):
- """Struct-like wrapper for additional arguments needed by
- specific tests."""
- # Whether to save new baseline results.
- new_baseline = False
-
- # Path to the actual PNG file generated by pixel tests
- png_path = None
-
- # Value of checksum generated by pixel tests.
- hash = None
-
- # Whether to use wdiff to generate by-word diffs.
- wdiff = False
-
-# Python bug workaround. See the wdiff code in WriteOutputFiles for an
-# explanation.
-_wdiff_available = True
-
-
-class TestTypeBase(object):
-
- # Filename pieces when writing failures to the test results directory.
- FILENAME_SUFFIX_ACTUAL = "-actual"
- FILENAME_SUFFIX_EXPECTED = "-expected"
- FILENAME_SUFFIX_DIFF = "-diff"
- FILENAME_SUFFIX_WDIFF = "-wdiff.html"
- FILENAME_SUFFIX_PRETTY_PATCH = "-pretty-diff.html"
- FILENAME_SUFFIX_COMPARE = "-diff.png"
-
- def __init__(self, port, root_output_dir):
- """Initialize a TestTypeBase object.
-
- Args:
- port: object implementing port-specific information and methods
- root_output_dir: The unix style path to the output dir.
- """
- self._root_output_dir = root_output_dir
- self._port = port
-
- def _make_output_directory(self, filename):
- """Creates the output directory (if needed) for a given test
- filename."""
- output_filename = os.path.join(self._root_output_dir,
- self._port.relative_test_filename(filename))
- self._port.maybe_make_directory(os.path.split(output_filename)[0])
-
- def _save_baseline_data(self, filename, data, modifier, encoding,
- generate_new_baseline=True):
- """Saves a new baseline file into the port's baseline directory.
-
- The file will be named simply "<test>-expected<modifier>", suitable for
- use as the expected results in a later run.
-
- Args:
- filename: path to the test file
- data: result to be saved as the new baseline
- modifier: type of the result file, e.g. ".txt" or ".png"
- encoding: file encoding (none, "utf-8", etc.)
- generate_new_baseline: whether to enerate a new, platform-specific
- baseline, or update the existing one
- """
-
- if generate_new_baseline:
- relative_dir = os.path.dirname(
- self._port.relative_test_filename(filename))
- baseline_path = self._port.baseline_path()
- output_dir = os.path.join(baseline_path, relative_dir)
- output_file = os.path.basename(os.path.splitext(filename)[0] +
- self.FILENAME_SUFFIX_EXPECTED + modifier)
- self._port.maybe_make_directory(output_dir)
- output_path = os.path.join(output_dir, output_file)
- _log.debug('writing new baseline result "%s"' % (output_path))
- else:
- output_path = self._port.expected_filename(filename, modifier)
- _log.debug('resetting baseline result "%s"' % output_path)
-
- self._port.update_baseline(output_path, data, encoding)
-
- def output_filename(self, filename, modifier):
- """Returns a filename inside the output dir that contains modifier.
-
- For example, if filename is c:/.../fast/dom/foo.html and modifier is
- "-expected.txt", the return value is
- c:/cygwin/tmp/layout-test-results/fast/dom/foo-expected.txt
-
- Args:
- filename: absolute filename to test file
- modifier: a string to replace the extension of filename with
-
- Return:
- The absolute windows path to the output filename
- """
- output_filename = os.path.join(self._root_output_dir,
- self._port.relative_test_filename(filename))
- return os.path.splitext(output_filename)[0] + modifier
-
- def compare_output(self, port, filename, test_args, actual_test_output,
- expected_test_output):
- """Method that compares the output from the test with the
- expected value.
-
- This is an abstract method to be implemented by all sub classes.
-
- Args:
- port: object implementing port-specific information and methods
- filename: absolute filename to test file
- test_args: a TestArguments object holding optional additional
- arguments
- actual_test_output: a TestOutput object which represents actual test
- output
- expected_test_output: a TestOutput object which represents a expected
- test output
-
- Return:
- a list of TestFailure objects, empty if the test passes
- """
- raise NotImplementedError
-
- def _write_into_file_at_path(self, file_path, contents, encoding):
- """This method assumes that byte_array is already encoded
- into the right format."""
- open_mode = 'w'
- if encoding is None:
- open_mode = 'w+b'
- with codecs.open(file_path, open_mode, encoding=encoding) as file:
- file.write(contents)
-
- def write_output_files(self, filename, file_type,
- output, expected, encoding,
- print_text_diffs=False):
- """Writes the test output, the expected output and optionally the diff
- between the two to files in the results directory.
-
- The full output filename of the actual, for example, will be
- <filename>-actual<file_type>
- For instance,
- my_test-actual.txt
-
- Args:
- filename: The test filename
- file_type: A string describing the test output file type, e.g. ".txt"
- output: A string containing the test output
- expected: A string containing the expected test output
- print_text_diffs: True for text diffs. (FIXME: We should be able to get this from the file type?)
- """
- self._make_output_directory(filename)
- actual_filename = self.output_filename(filename, self.FILENAME_SUFFIX_ACTUAL + file_type)
- expected_filename = self.output_filename(filename, self.FILENAME_SUFFIX_EXPECTED + file_type)
- # FIXME: This function is poorly designed. We should be passing in some sort of
- # encoding information from the callers.
- if output:
- self._write_into_file_at_path(actual_filename, output, encoding)
- if expected:
- self._write_into_file_at_path(expected_filename, expected, encoding)
-
- if not output or not expected:
- return
-
- if not print_text_diffs:
- return
-
- # Note: We pass encoding=None for all diff writes, as we treat diff
- # output as binary. Diff output may contain multiple files in
- # conflicting encodings.
- diff = self._port.diff_text(expected, output, expected_filename, actual_filename)
- diff_filename = self.output_filename(filename, self.FILENAME_SUFFIX_DIFF + file_type)
- self._write_into_file_at_path(diff_filename, diff, encoding=None)
-
- # Shell out to wdiff to get colored inline diffs.
- wdiff = self._port.wdiff_text(expected_filename, actual_filename)
- wdiff_filename = self.output_filename(filename, self.FILENAME_SUFFIX_WDIFF)
- self._write_into_file_at_path(wdiff_filename, wdiff, encoding=None)
-
- # Use WebKit's PrettyPatch.rb to get an HTML diff.
- pretty_patch = self._port.pretty_patch_text(diff_filename)
- pretty_patch_filename = self.output_filename(filename, self.FILENAME_SUFFIX_PRETTY_PATCH)
- self._write_into_file_at_path(pretty_patch_filename, pretty_patch, encoding=None)
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/test_type_base_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/test_types/test_type_base_unittest.py
deleted file mode 100644
index 5dbfcb6..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/test_type_base_unittest.py
+++ /dev/null
@@ -1,47 +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.
-
-""""Tests stray tests not covered by regular code paths."""
-
-import test_type_base
-import unittest
-
-from webkitpy.thirdparty.mock import Mock
-
-
-class Test(unittest.TestCase):
-
- def test_compare_output_notimplemented(self):
- test_type = test_type_base.TestTypeBase(None, None)
- self.assertRaises(NotImplementedError, test_type.compare_output,
- None, "foo.txt", '',
- test_type_base.TestArguments(), 'Debug')
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/text_diff.py b/WebKitTools/Scripts/webkitpy/layout_tests/test_types/text_diff.py
deleted file mode 100644
index ca4b17d..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/test_types/text_diff.py
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env 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.
-
-"""Compares the text output of a test to the expected text output.
-
-If the output doesn't match, returns FailureTextMismatch and outputs the diff
-files into the layout test results directory.
-"""
-
-from __future__ import with_statement
-
-import codecs
-import errno
-import logging
-import os.path
-
-from webkitpy.layout_tests.layout_package import test_failures
-from webkitpy.layout_tests.test_types import test_type_base
-
-_log = logging.getLogger("webkitpy.layout_tests.test_types.text_diff")
-
-
-class TestTextDiff(test_type_base.TestTypeBase):
-
- def _get_normalized_output_text(self, output):
- # Some tests produce "\r\n" explicitly. Our system (Python/Cygwin)
- # helpfully changes the "\n" to "\r\n", resulting in "\r\r\n".
- norm = output.replace("\r\r\n", "\r\n").strip("\r\n").replace(
- "\r\n", "\n")
- return norm + "\n"
-
- def compare_output(self, port, filename, test_args, actual_test_output,
- expected_test_output):
- """Implementation of CompareOutput that checks the output text against
- the expected text from the LayoutTest directory."""
- failures = []
-
- # If we're generating a new baseline, we pass.
- if test_args.new_baseline or test_args.reset_results:
- # Although all test_shell/DumpRenderTree output should be utf-8,
- # we do not ever decode it inside run-webkit-tests. For some tests
- # DumpRenderTree may not output utf-8 text (e.g. webarchives).
- self._save_baseline_data(filename, actual_test_output.text,
- ".txt", encoding=None,
- generate_new_baseline=test_args.new_baseline)
- return failures
-
- # Normalize text to diff
- actual_text = self._get_normalized_output_text(actual_test_output.text)
- # Assuming expected_text is already normalized.
- expected_text = expected_test_output.text
-
- # Write output files for new tests, too.
- if port.compare_text(actual_text, expected_text):
- # Text doesn't match, write output files.
- self.write_output_files(filename, ".txt", actual_text,
- expected_text, encoding=None,
- print_text_diffs=True)
-
- if expected_text == '':
- failures.append(test_failures.FailureMissingResult())
- else:
- failures.append(test_failures.FailureTextMismatch())
-
- return failures
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests.py
deleted file mode 100755
index f4c8098..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests.py
+++ /dev/null
@@ -1,160 +0,0 @@
-#!/usr/bin/env 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:
-# 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 COMPUTER, INC. ``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 COMPUTER, INC. OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
-# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import with_statement
-
-import glob
-import logging
-import optparse
-import os
-import re
-import sys
-import webkitpy.common.checkout.scm as scm
-
-_log = logging.getLogger("webkitpy.layout_tests."
- "update-webgl-conformance-tests")
-
-
-def remove_first_line_comment(text):
- return re.compile(r'^<!--.*?-->\s*', re.DOTALL).sub('', text)
-
-
-def translate_includes(text):
- # Mapping of single filename to relative path under WebKit root.
- # Assumption: these filenames are globally unique.
- include_mapping = {
- "js-test-style.css": "../../js/resources",
- "js-test-pre.js": "../../js/resources",
- "js-test-post.js": "../../js/resources",
- "desktop-gl-constants.js": "resources",
- }
-
- for filename, path in include_mapping.items():
- search = r'(?:[^"\'= ]*/)?' + re.escape(filename)
- replace = os.path.join(path, filename)
- text = re.sub(search, replace, text)
-
- return text
-
-
-def translate_khronos_test(text):
- """
- This method translates the contents of a Khronos test to a WebKit test.
- """
-
- translateFuncs = [
- remove_first_line_comment,
- translate_includes,
- ]
-
- for f in translateFuncs:
- text = f(text)
-
- return text
-
-
-def update_file(in_filename, out_dir):
- # check in_filename exists
- # check out_dir exists
- out_filename = os.path.join(out_dir, os.path.basename(in_filename))
-
- _log.debug("Processing " + in_filename)
- with open(in_filename, 'r') as in_file:
- with open(out_filename, 'w') as out_file:
- out_file.write(translate_khronos_test(in_file.read()))
-
-
-def update_directory(in_dir, out_dir):
- for filename in glob.glob(os.path.join(in_dir, '*.html')):
- update_file(os.path.join(in_dir, filename), out_dir)
-
-
-def default_out_dir():
- current_scm = scm.detect_scm_system(os.path.dirname(sys.argv[0]))
- if not current_scm:
- return os.getcwd()
- root_dir = current_scm.checkout_root
- if not root_dir:
- return os.getcwd()
- out_dir = os.path.join(root_dir, "LayoutTests/fast/canvas/webgl")
- if os.path.isdir(out_dir):
- return out_dir
- return os.getcwd()
-
-
-def configure_logging(options):
- """Configures the logging system."""
- log_fmt = '%(levelname)s: %(message)s'
- log_datefmt = '%y%m%d %H:%M:%S'
- log_level = logging.INFO
- if options.verbose:
- log_fmt = ('%(asctime)s %(filename)s:%(lineno)-4d %(levelname)s '
- '%(message)s')
- log_level = logging.DEBUG
- logging.basicConfig(level=log_level, format=log_fmt,
- datefmt=log_datefmt)
-
-
-def option_parser():
- usage = "usage: %prog [options] (input file or directory)"
- parser = optparse.OptionParser(usage=usage)
- parser.add_option('-v', '--verbose',
- action='store_true',
- default=False,
- help='include debug-level logging')
- parser.add_option('-o', '--output',
- action='store',
- type='string',
- default=default_out_dir(),
- metavar='DIR',
- help='specify an output directory to place files '
- 'in [default: %default]')
- return parser
-
-
-def main():
- parser = option_parser()
- (options, args) = parser.parse_args()
- configure_logging(options)
-
- if len(args) == 0:
- _log.error("Must specify an input directory or filename.")
- parser.print_help()
- return 1
-
- in_name = args[0]
- if os.path.isfile(in_name):
- update_file(in_name, options.output)
- elif os.path.isdir(in_name):
- update_directory(in_name, options.output)
- else:
- _log.error("'%s' is not a directory or a file.", in_name)
- return 2
-
- return 0
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests_unittest.py
deleted file mode 100644
index 7393b70..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests_unittest.py
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/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 update_webgl_conformance_tests."""
-
-import unittest
-from webkitpy.layout_tests import update_webgl_conformance_tests as webgl
-
-
-def construct_script(name):
- return "<script src=\"" + name + "\"></script>\n"
-
-
-def construct_style(name):
- return "<link rel=\"stylesheet\" href=\"" + name + "\">"
-
-
-class TestTranslation(unittest.TestCase):
- def assert_unchanged(self, text):
- self.assertEqual(text, webgl.translate_khronos_test(text))
-
- def assert_translate(self, input, output):
- self.assertEqual(output, webgl.translate_khronos_test(input))
-
- def test_simple_unchanged(self):
- self.assert_unchanged("")
- self.assert_unchanged("<html></html>")
-
- def test_header_strip(self):
- single_line_header = "<!-- single line header. -->"
- multi_line_header = """<!-- this is a multi-line
- header. it should all be removed too.
- -->"""
- text = "<html></html>"
- self.assert_translate(single_line_header, "")
- self.assert_translate(single_line_header + text, text)
- self.assert_translate(multi_line_header + text, text)
-
- def dont_strip_other_headers(self):
- self.assert_unchanged("<html>\n<!-- don't remove comments on other lines. -->\n</html>")
-
- def test_include_rewriting(self):
- # Mappings to None are unchanged
- styles = {
- "../resources/js-test-style.css": "../../js/resources/js-test-style.css",
- "fail.css": None,
- "resources/stylesheet.css": None,
- "../resources/style.css": None,
- }
- scripts = {
- "../resources/js-test-pre.js": "../../js/resources/js-test-pre.js",
- "../resources/js-test-post.js": "../../js/resources/js-test-post.js",
- "../resources/desktop-gl-constants.js": "resources/desktop-gl-constants.js",
-
- "resources/shadow-offset.js": None,
- "../resources/js-test-post-async.js": None,
- }
-
- input_text = ""
- output_text = ""
- for input, output in styles.items():
- input_text += construct_style(input)
- output_text += construct_style(output if output else input)
- for input, output in scripts.items():
- input_text += construct_script(input)
- output_text += construct_script(output if output else input)
-
- head = '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">\n<html>\n<head>\n'
- foot = '</head>\n<body>\n</body>\n</html>'
- input_text = head + input_text + foot
- output_text = head + output_text + foot
- self.assert_translate(input_text, output_text)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/python24/__init__.py b/WebKitTools/Scripts/webkitpy/python24/__init__.py
deleted file mode 100644
index ef65bee..0000000
--- a/WebKitTools/Scripts/webkitpy/python24/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/python24/versioning.py b/WebKitTools/Scripts/webkitpy/python24/versioning.py
deleted file mode 100644
index 8b1f21b..0000000
--- a/WebKitTools/Scripts/webkitpy/python24/versioning.py
+++ /dev/null
@@ -1,133 +0,0 @@
-# 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.
-
-"""Supports Python version checking."""
-
-import logging
-import sys
-
-_log = logging.getLogger("webkitpy.python24.versioning")
-
-# The minimum Python version the webkitpy package supports.
-_MINIMUM_SUPPORTED_PYTHON_VERSION = "2.5"
-
-
-def compare_version(sysmodule=None, target_version=None):
- """Compare the current Python version with a target version.
-
- Args:
- sysmodule: An object with version and version_info data attributes
- used to detect the current Python version. The attributes
- should have the same semantics as sys.version and
- sys.version_info. This parameter should only be used
- for unit testing. Defaults to sys.
- target_version: A string representing the Python version to compare
- the current version against. The string should have
- one of the following three forms: 2, 2.5, or 2.5.3.
- Defaults to the minimum version that the webkitpy
- package supports.
-
- Returns:
- A triple of (comparison, current_version, target_version).
-
- comparison: An integer representing the result of comparing the
- current version with the target version. A positive
- number means the current version is greater than the
- target, 0 means they are the same, and a negative number
- means the current version is less than the target.
- This method compares version information only up
- to the precision of the given target version. For
- example, if the target version is 2.6 and the current
- version is 2.5.3, this method uses 2.5 for the purposes
- of comparing with the target.
- current_version: A string representing the current Python version, for
- example 2.5.3.
- target_version: A string representing the version that the current
- version was compared against, for example 2.5.
-
- """
- if sysmodule is None:
- sysmodule = sys
- if target_version is None:
- target_version = _MINIMUM_SUPPORTED_PYTHON_VERSION
-
- # The number of version parts to compare.
- precision = len(target_version.split("."))
-
- # We use sys.version_info rather than sys.version since its first
- # three elements are guaranteed to be integers.
- current_version_info_to_compare = sysmodule.version_info[:precision]
- # Convert integers to strings.
- current_version_info_to_compare = map(str, current_version_info_to_compare)
- current_version_to_compare = ".".join(current_version_info_to_compare)
-
- # Compare version strings lexicographically.
- if current_version_to_compare > target_version:
- comparison = 1
- elif current_version_to_compare == target_version:
- comparison = 0
- else:
- comparison = -1
-
- # The version number portion of the current version string, for
- # example "2.6.4".
- current_version = sysmodule.version.split()[0]
-
- return (comparison, current_version, target_version)
-
-
-# FIXME: Add a logging level parameter to allow the version message
-# to be logged at levels other than WARNING, for example CRITICAL.
-def check_version(log=None, sysmodule=None, target_version=None):
- """Check the current Python version against a target version.
-
- Logs a warning message if the current version is less than the
- target version.
-
- Args:
- log: A logging.logger instance to use when logging the version warning.
- Defaults to the logger of this module.
- sysmodule: See the compare_version() docstring.
- target_version: See the compare_version() docstring.
-
- Returns:
- A boolean value of whether the current version is greater than
- or equal to the target version.
-
- """
- if log is None:
- log = _log
-
- (comparison, current_version, target_version) = \
- compare_version(sysmodule, target_version)
-
- if comparison >= 0:
- # Then the current version is at least the minimum version.
- return True
-
- message = ("WebKit Python scripts do not support your current Python "
- "version (%s). The minimum supported version is %s.\n"
- " See the following page to upgrade your Python version:\n\n"
- " http://trac.webkit.org/wiki/PythonGuidelines\n"
- % (current_version, target_version))
- log.warn(message)
- return False
diff --git a/WebKitTools/Scripts/webkitpy/python24/versioning_unittest.py b/WebKitTools/Scripts/webkitpy/python24/versioning_unittest.py
deleted file mode 100644
index 6939e2d..0000000
--- a/WebKitTools/Scripts/webkitpy/python24/versioning_unittest.py
+++ /dev/null
@@ -1,134 +0,0 @@
-# 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.
-
-"""Contains unit tests for versioning.py."""
-
-import logging
-import unittest
-
-from webkitpy.common.system.logtesting import LogTesting
-from webkitpy.python24.versioning import check_version
-from webkitpy.python24.versioning import compare_version
-
-class MockSys(object):
-
- """A mock sys module for passing to version-checking methods."""
-
- def __init__(self, current_version):
- """Create an instance.
-
- current_version: A version string with major, minor, and micro
- version parts.
-
- """
- version_info = current_version.split(".")
- version_info = map(int, version_info)
-
- self.version = current_version + " Version details."
- self.version_info = version_info
-
-
-class CompareVersionTest(unittest.TestCase):
-
- """Tests compare_version()."""
-
- def _mock_sys(self, current_version):
- return MockSys(current_version)
-
- def test_default_minimum_version(self):
- """Test the configured minimum version that webkitpy supports."""
- (comparison, current_version, min_version) = compare_version()
- self.assertEquals(min_version, "2.5")
-
- def compare_version(self, target_version, current_version=None):
- """Call compare_version()."""
- if current_version is None:
- current_version = "2.5.3"
- mock_sys = self._mock_sys(current_version)
- return compare_version(mock_sys, target_version)
-
- def compare(self, target_version, current_version=None):
- """Call compare_version(), and return the comparison."""
- return self.compare_version(target_version, current_version)[0]
-
- def test_returned_current_version(self):
- """Test the current_version return value."""
- current_version = self.compare_version("2.5")[1]
- self.assertEquals(current_version, "2.5.3")
-
- def test_returned_target_version(self):
- """Test the current_version return value."""
- target_version = self.compare_version("2.5")[2]
- self.assertEquals(target_version, "2.5")
-
- def test_target_version_major(self):
- """Test major version for target."""
- self.assertEquals(-1, self.compare("3"))
- self.assertEquals(0, self.compare("2"))
- self.assertEquals(1, self.compare("2", "3.0.0"))
-
- def test_target_version_minor(self):
- """Test minor version for target."""
- self.assertEquals(-1, self.compare("2.6"))
- self.assertEquals(0, self.compare("2.5"))
- self.assertEquals(1, self.compare("2.4"))
-
- def test_target_version_micro(self):
- """Test minor version for target."""
- self.assertEquals(-1, self.compare("2.5.4"))
- self.assertEquals(0, self.compare("2.5.3"))
- self.assertEquals(1, self.compare("2.5.2"))
-
-
-class CheckVersionTest(unittest.TestCase):
-
- """Tests check_version()."""
-
- def setUp(self):
- self._log = LogTesting.setUp(self)
-
- def tearDown(self):
- self._log.tearDown()
-
- def _check_version(self, minimum_version):
- """Call check_version()."""
- mock_sys = MockSys("2.5.3")
- return check_version(sysmodule=mock_sys, target_version=minimum_version)
-
- def test_true_return_value(self):
- """Test the configured minimum version that webkitpy supports."""
- is_current = self._check_version("2.4")
- self.assertEquals(True, is_current)
- self._log.assertMessages([]) # No warning was logged.
-
- def test_false_return_value(self):
- """Test the configured minimum version that webkitpy supports."""
- is_current = self._check_version("2.6")
- self.assertEquals(False, is_current)
- expected_message = ('WARNING: WebKit Python scripts do not support '
- 'your current Python version (2.5.3). '
- 'The minimum supported version is 2.6.\n '
- 'See the following page to upgrade your Python '
- 'version:\n\n '
- 'http://trac.webkit.org/wiki/PythonGuidelines\n\n')
- self._log.assertMessages([expected_message])
-
diff --git a/WebKitTools/Scripts/webkitpy/style/__init__.py b/WebKitTools/Scripts/webkitpy/style/__init__.py
deleted file mode 100644
index ef65bee..0000000
--- a/WebKitTools/Scripts/webkitpy/style/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/style/checker.py b/WebKitTools/Scripts/webkitpy/style/checker.py
deleted file mode 100644
index e10eec5..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checker.py
+++ /dev/null
@@ -1,739 +0,0 @@
-# Copyright (C) 2009 Google Inc. All rights reserved.
-# Copyright (C) 2010 Chris Jerdonek (chris.jerdonek@gmail.com)
-# Copyright (C) 2010 ProFUSION embedded systems
-#
-# 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.
-
-"""Front end of some style-checker modules."""
-
-import logging
-import os.path
-import sys
-
-from checkers.common import categories as CommonCategories
-from checkers.common import CarriageReturnChecker
-from checkers.cpp import CppChecker
-from checkers.python import PythonChecker
-from checkers.test_expectations import TestExpectationsChecker
-from checkers.text import TextChecker
-from error_handlers import DefaultStyleErrorHandler
-from filter import FilterConfiguration
-from optparser import ArgumentParser
-from optparser import DefaultCommandOptionValues
-from webkitpy.style_references import configure_logging as _configure_logging
-
-_log = logging.getLogger("webkitpy.style.checker")
-
-# These are default option values for the command-line option parser.
-_DEFAULT_MIN_CONFIDENCE = 1
-_DEFAULT_OUTPUT_FORMAT = 'emacs'
-
-
-# FIXME: For style categories we will never want to have, remove them.
-# For categories for which we want to have similar functionality,
-# modify the implementation and enable them.
-#
-# Throughout this module, we use "filter rule" rather than "filter"
-# for an individual boolean filter flag like "+foo". This allows us to
-# reserve "filter" for what one gets by collectively applying all of
-# the filter rules.
-#
-# The base filter rules are the filter rules that begin the list of
-# filter rules used to check style. For example, these rules precede
-# any user-specified filter rules. Since by default all categories are
-# checked, this list should normally include only rules that begin
-# with a "-" sign.
-_BASE_FILTER_RULES = [
- '-build/endif_comment',
- '-build/include_what_you_use', # <string> for std::string
- '-build/storage_class', # const static
- '-legal/copyright',
- '-readability/multiline_comment',
- '-readability/braces', # int foo() {};
- '-readability/fn_size',
- '-readability/casting',
- '-readability/function',
- '-runtime/arrays', # variable length array
- '-runtime/casting',
- '-runtime/sizeof',
- '-runtime/explicit', # explicit
- '-runtime/virtual', # virtual dtor
- '-runtime/printf',
- '-runtime/threadsafe_fn',
- '-runtime/rtti',
- '-whitespace/blank_line',
- '-whitespace/end_of_line',
- '-whitespace/labels',
- # List Python pep8 categories last.
- #
- # Because much of WebKit's Python code base does not abide by the
- # PEP8 79 character limit, we ignore the 79-character-limit category
- # pep8/E501 for now.
- #
- # FIXME: Consider bringing WebKit's Python code base into conformance
- # with the 79 character limit, or some higher limit that is
- # agreeable to the WebKit project.
- '-pep8/E501',
- ]
-
-
-# The path-specific filter rules.
-#
-# This list is order sensitive. Only the first path substring match
-# is used. See the FilterConfiguration documentation in filter.py
-# for more information on this list.
-#
-# Each string appearing in this nested list should have at least
-# one associated unit test assertion. These assertions are located,
-# for example, in the test_path_rules_specifier() unit test method of
-# checker_unittest.py.
-_PATH_RULES_SPECIFIER = [
- # Files in these directories are consumers of the WebKit
- # API and therefore do not follow the same header including
- # discipline as WebCore.
-
- ([# TestNetscapePlugIn has no config.h and uses funny names like
- # NPP_SetWindow.
- "WebKitTools/DumpRenderTree/TestNetscapePlugIn/",
- # The API test harnesses have no config.h and use funny macros like
- # TEST_CLASS_NAME.
- "WebKitTools/WebKitAPITest/",
- "WebKitTools/TestWebKitAPI/"],
- ["-build/include",
- "-readability/naming"]),
- ([# The EFL APIs use EFL naming style, which includes
- # both lower-cased and camel-cased, underscore-sparated
- # values.
- "WebKit/efl/ewk/",
- # There is no clean way to avoid "yy_*" names used by flex.
- "WebCore/css/CSSParser.cpp",
- # Qt code uses '_' in some places (such as private slots
- # and on test xxx_data methos on tests)
- "JavaScriptCore/qt/api/",
- "WebKit/qt/Api/",
- "WebKit/qt/tests/",
- "WebKit/qt/declarative/",
- "WebKit/qt/examples/"],
- ["-readability/naming"]),
- ([# The GTK+ APIs use GTK+ naming style, which includes
- # lower-cased, underscore-separated values.
- # Also, GTK+ allows the use of NULL.
- "WebCore/bindings/scripts/test/GObject",
- "WebKit/gtk/webkit/",
- "WebKitTools/DumpRenderTree/gtk/"],
- ["-readability/naming",
- "-readability/null"]),
- ([# Header files in ForwardingHeaders have no header guards or
- # exceptional header guards (e.g., WebCore_FWD_Debugger_h).
- "/ForwardingHeaders/"],
- ["-build/header_guard"]),
- ([# assembler has lots of opcodes that use underscores, so
- # we don't check for underscores in that directory.
- "/JavaScriptCore/assembler/"],
- ["-readability/naming"]),
-
- # WebKit2 rules:
- # WebKit2 doesn't use config.h, and certain directories have other
- # idiosyncracies.
- ([# NPAPI has function names with underscores.
- "WebKit2/WebProcess/Plugins/Netscape"],
- ["-build/include_order",
- "-readability/naming"]),
- ([# The WebKit2 C API has names with underscores and whitespace-aligned
- # struct members.
- "WebKit2/UIProcess/API/C/",
- "WebKit2/WebProcess/InjectedBundle/API/c/"],
- ["-build/include_order",
- "-readability/naming",
- "-whitespace/declaration"]),
- ([# Nothing in WebKit2 uses config.h.
- "WebKit2/"],
- ["-build/include_order"]),
-
- # For third-party Python code, keep only the following checks--
- #
- # No tabs: to avoid having to set the SVN allow-tabs property.
- # No trailing white space: since this is easy to correct.
- # No carriage-return line endings: since this is easy to correct.
- #
- (["webkitpy/thirdparty/"],
- ["-",
- "+pep8/W191", # Tabs
- "+pep8/W291", # Trailing white space
- "+whitespace/carriage_return"]),
-]
-
-
-_CPP_FILE_EXTENSIONS = [
- 'c',
- 'cpp',
- 'h',
- ]
-
-_PYTHON_FILE_EXTENSION = 'py'
-
-# FIXME: Include 'vcproj' files as text files after creating a mechanism
-# for exempting them from the carriage-return checker (since they
-# are Windows-only files).
-_TEXT_FILE_EXTENSIONS = [
- 'ac',
- 'cc',
- 'cgi',
- 'css',
- 'exp',
- 'flex',
- 'gyp',
- 'gypi',
- 'html',
- 'idl',
- 'in',
- 'js',
- 'mm',
- 'php',
- 'pl',
- 'pm',
- 'pri',
- 'pro',
- 'rb',
- 'sh',
- 'txt',
-# 'vcproj', # See FIXME above.
- 'wm',
- 'xhtml',
- 'y',
- ]
-
-
-# Files to skip that are less obvious.
-#
-# Some files should be skipped when checking style. For example,
-# WebKit maintains some files in Mozilla style on purpose to ease
-# future merges.
-_SKIPPED_FILES_WITH_WARNING = [
- "gtk2drawing.c", # WebCore/platform/gtk/gtk2drawing.c
- "gtkdrawing.h", # WebCore/platform/gtk/gtkdrawing.h
- "WebKit/gtk/tests/",
- # Soup API that is still being cooked, will be removed from WebKit
- # in a few months when it is merged into soup proper. The style
- # follows the libsoup style completely.
- "WebCore/platform/network/soup/cache/",
- ]
-
-
-# Files to skip that are more common or obvious.
-#
-# This list should be in addition to files with FileType.NONE. Files
-# with FileType.NONE are automatically skipped without warning.
-_SKIPPED_FILES_WITHOUT_WARNING = [
- "LayoutTests/",
- ]
-
-
-# The maximum number of errors to report per file, per category.
-# If a category is not a key, then it has no maximum.
-_MAX_REPORTS_PER_CATEGORY = {
- "whitespace/carriage_return": 1
-}
-
-
-def _all_categories():
- """Return the set of all categories used by check-webkit-style."""
- # Take the union across all checkers.
- categories = CommonCategories.union(CppChecker.categories)
- categories = categories.union(TestExpectationsChecker.categories)
-
- # FIXME: Consider adding all of the pep8 categories. Since they
- # are not too meaningful for documentation purposes, for
- # now we add only the categories needed for the unit tests
- # (which validate the consistency of the configuration
- # settings against the known categories, etc).
- categories = categories.union(["pep8/W191", "pep8/W291", "pep8/E501"])
-
- return categories
-
-
-def _check_webkit_style_defaults():
- """Return the default command-line options for check-webkit-style."""
- return DefaultCommandOptionValues(min_confidence=_DEFAULT_MIN_CONFIDENCE,
- output_format=_DEFAULT_OUTPUT_FORMAT)
-
-
-# This function assists in optparser not having to import from checker.
-def check_webkit_style_parser():
- all_categories = _all_categories()
- default_options = _check_webkit_style_defaults()
- return ArgumentParser(all_categories=all_categories,
- base_filter_rules=_BASE_FILTER_RULES,
- default_options=default_options)
-
-
-def check_webkit_style_configuration(options):
- """Return a StyleProcessorConfiguration instance for check-webkit-style.
-
- Args:
- options: A CommandOptionValues instance.
-
- """
- filter_configuration = FilterConfiguration(
- base_rules=_BASE_FILTER_RULES,
- path_specific=_PATH_RULES_SPECIFIER,
- user_rules=options.filter_rules)
-
- return StyleProcessorConfiguration(filter_configuration=filter_configuration,
- max_reports_per_category=_MAX_REPORTS_PER_CATEGORY,
- min_confidence=options.min_confidence,
- output_format=options.output_format,
- stderr_write=sys.stderr.write)
-
-
-def _create_log_handlers(stream):
- """Create and return a default list of logging.Handler instances.
-
- Format WARNING messages and above to display the logging level, and
- messages strictly below WARNING not to display it.
-
- Args:
- stream: See the configure_logging() docstring.
-
- """
- # Handles logging.WARNING and above.
- error_handler = logging.StreamHandler(stream)
- error_handler.setLevel(logging.WARNING)
- formatter = logging.Formatter("%(levelname)s: %(message)s")
- error_handler.setFormatter(formatter)
-
- # Create a logging.Filter instance that only accepts messages
- # below WARNING (i.e. filters out anything WARNING or above).
- non_error_filter = logging.Filter()
- # The filter method accepts a logging.LogRecord instance.
- non_error_filter.filter = lambda record: record.levelno < logging.WARNING
-
- non_error_handler = logging.StreamHandler(stream)
- non_error_handler.addFilter(non_error_filter)
- formatter = logging.Formatter("%(message)s")
- non_error_handler.setFormatter(formatter)
-
- return [error_handler, non_error_handler]
-
-
-def _create_debug_log_handlers(stream):
- """Create and return a list of logging.Handler instances for debugging.
-
- Args:
- stream: See the configure_logging() docstring.
-
- """
- handler = logging.StreamHandler(stream)
- formatter = logging.Formatter("%(name)s: %(levelname)-8s %(message)s")
- handler.setFormatter(formatter)
-
- return [handler]
-
-
-def configure_logging(stream, logger=None, is_verbose=False):
- """Configure logging, and return the list of handlers added.
-
- Returns:
- A list of references to the logging handlers added to the root
- logger. This allows the caller to later remove the handlers
- using logger.removeHandler. This is useful primarily during unit
- testing where the caller may want to configure logging temporarily
- and then undo the configuring.
-
- Args:
- stream: A file-like object to which to log. The stream must
- define an "encoding" data attribute, or else logging
- raises an error.
- logger: A logging.logger instance to configure. This parameter
- should be used only in unit tests. Defaults to the
- root logger.
- is_verbose: A boolean value of whether logging should be verbose.
-
- """
- # If the stream does not define an "encoding" data attribute, the
- # logging module can throw an error like the following:
- #
- # Traceback (most recent call last):
- # File "/System/Library/Frameworks/Python.framework/Versions/2.6/...
- # lib/python2.6/logging/__init__.py", line 761, in emit
- # self.stream.write(fs % msg.encode(self.stream.encoding))
- # LookupError: unknown encoding: unknown
- if logger is None:
- logger = logging.getLogger()
-
- if is_verbose:
- logging_level = logging.DEBUG
- handlers = _create_debug_log_handlers(stream)
- else:
- logging_level = logging.INFO
- handlers = _create_log_handlers(stream)
-
- handlers = _configure_logging(logging_level=logging_level, logger=logger,
- handlers=handlers)
-
- return handlers
-
-
-# Enum-like idiom
-class FileType:
-
- NONE = 0 # FileType.NONE evaluates to False.
- # Alphabetize remaining types
- CPP = 1
- PYTHON = 2
- TEXT = 3
-
-
-class CheckerDispatcher(object):
-
- """Supports determining whether and how to check style, based on path."""
-
- def _file_extension(self, file_path):
- """Return the file extension without the leading dot."""
- return os.path.splitext(file_path)[1].lstrip(".")
-
- def should_skip_with_warning(self, file_path):
- """Return whether the given file should be skipped with a warning."""
- for skipped_file in _SKIPPED_FILES_WITH_WARNING:
- if file_path.find(skipped_file) >= 0:
- return True
- return False
-
- def should_skip_without_warning(self, file_path):
- """Return whether the given file should be skipped without a warning."""
- if not self._file_type(file_path): # FileType.NONE.
- return True
- # Since "LayoutTests" is in _SKIPPED_FILES_WITHOUT_WARNING, make
- # an exception to prevent files like "LayoutTests/ChangeLog" and
- # "LayoutTests/ChangeLog-2009-06-16" from being skipped.
- # Files like 'test_expectations.txt' and 'drt_expectations.txt'
- # are also should not be skipped.
- #
- # FIXME: Figure out a good way to avoid having to add special logic
- # for this special case.
- basename = os.path.basename(file_path)
- if basename.startswith('ChangeLog'):
- return False
- elif basename == 'test_expectations.txt' or basename == 'drt_expectations.txt':
- return False
- for skipped_file in _SKIPPED_FILES_WITHOUT_WARNING:
- if file_path.find(skipped_file) >= 0:
- return True
- return False
-
- def _file_type(self, file_path):
- """Return the file type corresponding to the given file."""
- file_extension = self._file_extension(file_path)
-
- if (file_extension in _CPP_FILE_EXTENSIONS) or (file_path == '-'):
- # FIXME: Do something about the comment below and the issue it
- # raises since cpp_style already relies on the extension.
- #
- # Treat stdin as C++. Since the extension is unknown when
- # reading from stdin, cpp_style tests should not rely on
- # the extension.
- return FileType.CPP
- elif file_extension == _PYTHON_FILE_EXTENSION:
- return FileType.PYTHON
- elif (os.path.basename(file_path).startswith('ChangeLog') or
- (not file_extension and "WebKitTools/Scripts/" in file_path) or
- file_extension in _TEXT_FILE_EXTENSIONS):
- return FileType.TEXT
- else:
- return FileType.NONE
-
- def _create_checker(self, file_type, file_path, handle_style_error,
- min_confidence):
- """Instantiate and return a style checker based on file type."""
- if file_type == FileType.NONE:
- checker = None
- elif file_type == FileType.CPP:
- file_extension = self._file_extension(file_path)
- checker = CppChecker(file_path, file_extension,
- handle_style_error, min_confidence)
- elif file_type == FileType.PYTHON:
- checker = PythonChecker(file_path, handle_style_error)
- elif file_type == FileType.TEXT:
- basename = os.path.basename(file_path)
- if basename == 'test_expectations.txt' or basename == 'drt_expectations.txt':
- checker = TestExpectationsChecker(file_path, handle_style_error)
- else:
- checker = TextChecker(file_path, handle_style_error)
- else:
- raise ValueError('Invalid file type "%(file_type)s": the only valid file types '
- "are %(NONE)s, %(CPP)s, and %(TEXT)s."
- % {"file_type": file_type,
- "NONE": FileType.NONE,
- "CPP": FileType.CPP,
- "TEXT": FileType.TEXT})
-
- return checker
-
- def dispatch(self, file_path, handle_style_error, min_confidence):
- """Instantiate and return a style checker based on file path."""
- file_type = self._file_type(file_path)
-
- checker = self._create_checker(file_type,
- file_path,
- handle_style_error,
- min_confidence)
- return checker
-
-
-# FIXME: Remove the stderr_write attribute from this class and replace
-# its use with calls to a logging module logger.
-class StyleProcessorConfiguration(object):
-
- """Stores configuration values for the StyleProcessor class.
-
- Attributes:
- min_confidence: An integer between 1 and 5 inclusive that is the
- minimum confidence level of style errors to report.
-
- max_reports_per_category: The maximum number of errors to report
- per category, per file.
-
- stderr_write: A function that takes a string as a parameter and
- serves as stderr.write.
-
- """
-
- def __init__(self,
- filter_configuration,
- max_reports_per_category,
- min_confidence,
- output_format,
- stderr_write):
- """Create a StyleProcessorConfiguration instance.
-
- Args:
- filter_configuration: A FilterConfiguration instance. The default
- is the "empty" filter configuration, which
- means that all errors should be checked.
-
- max_reports_per_category: The maximum number of errors to report
- per category, per file.
-
- min_confidence: An integer between 1 and 5 inclusive that is the
- minimum confidence level of style errors to report.
- The default is 1, which reports all style errors.
-
- output_format: A string that is the output format. The supported
- output formats are "emacs" which emacs can parse
- and "vs7" which Microsoft Visual Studio 7 can parse.
-
- stderr_write: A function that takes a string as a parameter and
- serves as stderr.write.
-
- """
- self._filter_configuration = filter_configuration
- self._output_format = output_format
-
- self.max_reports_per_category = max_reports_per_category
- self.min_confidence = min_confidence
- self.stderr_write = stderr_write
-
- def is_reportable(self, category, confidence_in_error, file_path):
- """Return whether an error is reportable.
-
- An error is reportable if both the confidence in the error is
- at least the minimum confidence level and the current filter
- says the category should be checked for the given path.
-
- Args:
- category: A string that is a style category.
- confidence_in_error: An integer between 1 and 5 inclusive that is
- the application's confidence in the error.
- A higher number means greater confidence.
- file_path: The path of the file being checked
-
- """
- if confidence_in_error < self.min_confidence:
- return False
-
- return self._filter_configuration.should_check(category, file_path)
-
- def write_style_error(self,
- category,
- confidence_in_error,
- file_path,
- line_number,
- message):
- """Write a style error to the configured stderr."""
- if self._output_format == 'vs7':
- format_string = "%s(%s): %s [%s] [%d]\n"
- else:
- format_string = "%s:%s: %s [%s] [%d]\n"
-
- self.stderr_write(format_string % (file_path,
- line_number,
- message,
- category,
- confidence_in_error))
-
-
-class ProcessorBase(object):
-
- """The base class for processors of lists of lines."""
-
- def should_process(self, file_path):
- """Return whether the file at file_path should be processed.
-
- The TextFileReader class calls this method prior to reading in
- the lines of a file. Use this method, for example, to prevent
- the style checker from reading binary files into memory.
-
- """
- raise NotImplementedError('Subclasses should implement.')
-
- def process(self, lines, file_path, **kwargs):
- """Process lines of text read from a file.
-
- Args:
- lines: A list of lines of text to process.
- file_path: The path from which the lines were read.
- **kwargs: This argument signifies that the process() method of
- subclasses of ProcessorBase may support additional
- keyword arguments.
- For example, a style checker's check() method
- may support a "reportable_lines" parameter that represents
- the line numbers of the lines for which style errors
- should be reported.
-
- """
- raise NotImplementedError('Subclasses should implement.')
-
-
-class StyleProcessor(ProcessorBase):
-
- """A ProcessorBase for checking style.
-
- Attributes:
- error_count: An integer that is the total number of reported
- errors for the lifetime of this instance.
-
- """
-
- def __init__(self, configuration, mock_dispatcher=None,
- mock_increment_error_count=None,
- mock_carriage_checker_class=None):
- """Create an instance.
-
- Args:
- configuration: A StyleProcessorConfiguration instance.
- mock_dispatcher: A mock CheckerDispatcher instance. This
- parameter is for unit testing. Defaults to a
- CheckerDispatcher instance.
- mock_increment_error_count: A mock error-count incrementer.
- mock_carriage_checker_class: A mock class for checking and
- transforming carriage returns.
- This parameter is for unit testing.
- Defaults to CarriageReturnChecker.
-
- """
- if mock_dispatcher is None:
- dispatcher = CheckerDispatcher()
- else:
- dispatcher = mock_dispatcher
-
- if mock_increment_error_count is None:
- # The following blank line is present to avoid flagging by pep8.py.
-
- def increment_error_count():
- """Increment the total count of reported errors."""
- self.error_count += 1
- else:
- increment_error_count = mock_increment_error_count
-
- if mock_carriage_checker_class is None:
- # This needs to be a class rather than an instance since the
- # process() method instantiates one using parameters.
- carriage_checker_class = CarriageReturnChecker
- else:
- carriage_checker_class = mock_carriage_checker_class
-
- self.error_count = 0
-
- self._carriage_checker_class = carriage_checker_class
- self._configuration = configuration
- self._dispatcher = dispatcher
- self._increment_error_count = increment_error_count
-
- def should_process(self, file_path):
- """Return whether the file should be checked for style."""
- if self._dispatcher.should_skip_without_warning(file_path):
- return False
- if self._dispatcher.should_skip_with_warning(file_path):
- _log.warn('File exempt from style guide. Skipping: "%s"'
- % file_path)
- return False
- return True
-
- def process(self, lines, file_path, line_numbers=None):
- """Check the given lines for style.
-
- Arguments:
- lines: A list of all lines in the file to check.
- file_path: The path of the file to process. If possible, the path
- should be relative to the source root. Otherwise,
- path-specific logic may not behave as expected.
- line_numbers: A list of line numbers of the lines for which
- style errors should be reported, or None if errors
- for all lines should be reported. When not None, this
- list normally contains the line numbers corresponding
- to the modified lines of a patch.
-
- """
- _log.debug("Checking style: " + file_path)
-
- style_error_handler = DefaultStyleErrorHandler(
- configuration=self._configuration,
- file_path=file_path,
- increment_error_count=self._increment_error_count,
- line_numbers=line_numbers)
-
- carriage_checker = self._carriage_checker_class(style_error_handler)
-
- # FIXME: We should probably use the SVN "eol-style" property
- # or a white list to decide whether or not to do
- # the carriage-return check. Originally, we did the
- # check only if (os.linesep != '\r\n').
- #
- # Check for and remove trailing carriage returns ("\r").
- lines = carriage_checker.check(lines)
-
- min_confidence = self._configuration.min_confidence
- checker = self._dispatcher.dispatch(file_path,
- style_error_handler,
- min_confidence)
-
- if checker is None:
- raise AssertionError("File should not be checked: '%s'" % file_path)
-
- _log.debug("Using class: " + checker.__class__.__name__)
-
- checker.check(lines)
diff --git a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py b/WebKitTools/Scripts/webkitpy/style/checker_unittest.py
deleted file mode 100755
index 94d2c29..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py
+++ /dev/null
@@ -1,779 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8; -*-
-#
-# Copyright (C) 2009 Google Inc. All rights reserved.
-# Copyright (C) 2009 Torch Mobile Inc.
-# Copyright (C) 2009 Apple Inc. 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 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 style.py."""
-
-import logging
-import os
-import unittest
-
-import checker as style
-from webkitpy.style_references import LogTesting
-from webkitpy.style_references import TestLogStream
-from checker import _BASE_FILTER_RULES
-from checker import _MAX_REPORTS_PER_CATEGORY
-from checker import _PATH_RULES_SPECIFIER as PATH_RULES_SPECIFIER
-from checker import _all_categories
-from checker import check_webkit_style_configuration
-from checker import check_webkit_style_parser
-from checker import configure_logging
-from checker import CheckerDispatcher
-from checker import ProcessorBase
-from checker import StyleProcessor
-from checker import StyleProcessorConfiguration
-from checkers.cpp import CppChecker
-from checkers.python import PythonChecker
-from checkers.text import TextChecker
-from error_handlers import DefaultStyleErrorHandler
-from filter import validate_filter_rules
-from filter import FilterConfiguration
-from optparser import ArgumentParser
-from optparser import CommandOptionValues
-from webkitpy.common.system.logtesting import LoggingTestCase
-from webkitpy.style.filereader import TextFileReader
-
-
-class ConfigureLoggingTestBase(unittest.TestCase):
-
- """Base class for testing configure_logging().
-
- Sub-classes should implement:
-
- is_verbose: The is_verbose value to pass to configure_logging().
-
- """
-
- def setUp(self):
- is_verbose = self.is_verbose
-
- log_stream = TestLogStream(self)
-
- # Use a logger other than the root logger or one prefixed with
- # webkit so as not to conflict with test-webkitpy logging.
- logger = logging.getLogger("unittest")
-
- # Configure the test logger not to pass messages along to the
- # root logger. This prevents test messages from being
- # propagated to loggers used by test-webkitpy logging (e.g.
- # the root logger).
- logger.propagate = False
-
- self._handlers = configure_logging(stream=log_stream, logger=logger,
- is_verbose=is_verbose)
- self._log = logger
- self._log_stream = log_stream
-
- def tearDown(self):
- """Reset logging to its original state.
-
- This method ensures that the logging configuration set up
- for a unit test does not affect logging in other unit tests.
-
- """
- logger = self._log
- for handler in self._handlers:
- logger.removeHandler(handler)
-
- def assert_log_messages(self, messages):
- """Assert that the logged messages equal the given messages."""
- self._log_stream.assertMessages(messages)
-
-
-class ConfigureLoggingTest(ConfigureLoggingTestBase):
-
- """Tests the configure_logging() function."""
-
- is_verbose = False
-
- def test_warning_message(self):
- self._log.warn("test message")
- self.assert_log_messages(["WARNING: test message\n"])
-
- def test_below_warning_message(self):
- # We test the boundary case of a logging level equal to 29.
- # In practice, we will probably only be calling log.info(),
- # which corresponds to a logging level of 20.
- level = logging.WARNING - 1 # Equals 29.
- self._log.log(level, "test message")
- self.assert_log_messages(["test message\n"])
-
- def test_debug_message(self):
- self._log.debug("test message")
- self.assert_log_messages([])
-
- def test_two_messages(self):
- self._log.info("message1")
- self._log.info("message2")
- self.assert_log_messages(["message1\n", "message2\n"])
-
-
-class ConfigureLoggingVerboseTest(ConfigureLoggingTestBase):
-
- """Tests the configure_logging() function with is_verbose True."""
-
- is_verbose = True
-
- def test_debug_message(self):
- self._log.debug("test message")
- self.assert_log_messages(["unittest: DEBUG test message\n"])
-
-
-class GlobalVariablesTest(unittest.TestCase):
-
- """Tests validity of the global variables."""
-
- def _all_categories(self):
- return _all_categories()
-
- def defaults(self):
- return style._check_webkit_style_defaults()
-
- def test_webkit_base_filter_rules(self):
- base_filter_rules = _BASE_FILTER_RULES
- defaults = self.defaults()
- already_seen = []
- validate_filter_rules(base_filter_rules, self._all_categories())
- # Also do some additional checks.
- for rule in base_filter_rules:
- # Check no leading or trailing white space.
- self.assertEquals(rule, rule.strip())
- # All categories are on by default, so defaults should
- # begin with -.
- self.assertTrue(rule.startswith('-'))
- # Check no rule occurs twice.
- self.assertFalse(rule in already_seen)
- already_seen.append(rule)
-
- def test_defaults(self):
- """Check that default arguments are valid."""
- default_options = self.defaults()
-
- # FIXME: We should not need to call parse() to determine
- # whether the default arguments are valid.
- parser = ArgumentParser(all_categories=self._all_categories(),
- base_filter_rules=[],
- default_options=default_options)
- # No need to test the return value here since we test parse()
- # on valid arguments elsewhere.
- #
- # The default options are valid: no error or SystemExit.
- parser.parse(args=[])
-
- def test_path_rules_specifier(self):
- all_categories = self._all_categories()
- for (sub_paths, path_rules) in PATH_RULES_SPECIFIER:
- validate_filter_rules(path_rules, self._all_categories())
-
- config = FilterConfiguration(path_specific=PATH_RULES_SPECIFIER)
-
- def assertCheck(path, category):
- """Assert that the given category should be checked."""
- message = ('Should check category "%s" for path "%s".'
- % (category, path))
- self.assertTrue(config.should_check(category, path))
-
- def assertNoCheck(path, category):
- """Assert that the given category should not be checked."""
- message = ('Should not check category "%s" for path "%s".'
- % (category, path))
- self.assertFalse(config.should_check(category, path), message)
-
- assertCheck("random_path.cpp",
- "build/include")
- assertNoCheck("WebKitTools/WebKitAPITest/main.cpp",
- "build/include")
- assertCheck("random_path.cpp",
- "readability/naming")
- assertNoCheck("WebKit/gtk/webkit/webkit.h",
- "readability/naming")
- assertNoCheck("WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp",
- "readability/null")
- assertNoCheck("WebKit/efl/ewk/ewk_view.h",
- "readability/naming")
- assertNoCheck("WebCore/css/CSSParser.cpp",
- "readability/naming")
-
- # Test if Qt exceptions are indeed working
- assertCheck("JavaScriptCore/qt/api/qscriptengine.cpp",
- "readability/braces")
- assertCheck("WebKit/qt/Api/qwebpage.cpp",
- "readability/braces")
- assertCheck("WebKit/qt/tests/qwebelement/tst_qwebelement.cpp",
- "readability/braces")
- assertCheck("WebKit/qt/declarative/platformplugin/WebPlugin.cpp",
- "readability/braces")
- assertCheck("WebKit/qt/examples/platformplugin/WebPlugin.cpp",
- "readability/braces")
- assertNoCheck("JavaScriptCore/qt/api/qscriptengine.cpp",
- "readability/naming")
- assertNoCheck("WebKit/qt/Api/qwebpage.cpp",
- "readability/naming")
- assertNoCheck("WebKit/qt/tests/qwebelement/tst_qwebelement.cpp",
- "readability/naming")
- assertNoCheck("WebKit/qt/declarative/platformplugin/WebPlugin.cpp",
- "readability/naming")
- assertNoCheck("WebKit/qt/examples/platformplugin/WebPlugin.cpp",
- "readability/naming")
-
- assertNoCheck("WebCore/ForwardingHeaders/debugger/Debugger.h",
- "build/header_guard")
-
- # Third-party Python code: webkitpy/thirdparty
- path = "WebKitTools/Scripts/webkitpy/thirdparty/mock.py"
- assertNoCheck(path, "build/include")
- assertNoCheck(path, "pep8/E401") # A random pep8 category.
- assertCheck(path, "pep8/W191")
- assertCheck(path, "pep8/W291")
- assertCheck(path, "whitespace/carriage_return")
-
- def test_max_reports_per_category(self):
- """Check that _MAX_REPORTS_PER_CATEGORY is valid."""
- all_categories = self._all_categories()
- for category in _MAX_REPORTS_PER_CATEGORY.iterkeys():
- self.assertTrue(category in all_categories,
- 'Key "%s" is not a category' % category)
-
-
-class CheckWebKitStyleFunctionTest(unittest.TestCase):
-
- """Tests the functions with names of the form check_webkit_style_*."""
-
- def test_check_webkit_style_configuration(self):
- # Exercise the code path to make sure the function does not error out.
- option_values = CommandOptionValues()
- configuration = check_webkit_style_configuration(option_values)
-
- def test_check_webkit_style_parser(self):
- # Exercise the code path to make sure the function does not error out.
- parser = check_webkit_style_parser()
-
-
-class CheckerDispatcherSkipTest(unittest.TestCase):
-
- """Tests the "should skip" methods of the CheckerDispatcher class."""
-
- def setUp(self):
- self._dispatcher = CheckerDispatcher()
-
- def test_should_skip_with_warning(self):
- """Test should_skip_with_warning()."""
- # Check a non-skipped file.
- self.assertFalse(self._dispatcher.should_skip_with_warning("foo.txt"))
-
- # Check skipped files.
- paths_to_skip = [
- "gtk2drawing.c",
- "gtkdrawing.h",
- "WebCore/platform/gtk/gtk2drawing.c",
- "WebCore/platform/gtk/gtkdrawing.h",
- "WebKit/gtk/tests/testatk.c",
- ]
-
- for path in paths_to_skip:
- self.assertTrue(self._dispatcher.should_skip_with_warning(path),
- "Checking: " + path)
-
- def _assert_should_skip_without_warning(self, path, is_checker_none,
- expected):
- # Check the file type before asserting the return value.
- checker = self._dispatcher.dispatch(file_path=path,
- handle_style_error=None,
- min_confidence=3)
- message = 'while checking: %s' % path
- self.assertEquals(checker is None, is_checker_none, message)
- self.assertEquals(self._dispatcher.should_skip_without_warning(path),
- expected, message)
-
- def test_should_skip_without_warning__true(self):
- """Test should_skip_without_warning() for True return values."""
- # Check a file with NONE file type.
- path = 'foo.asdf' # Non-sensical file extension.
- self._assert_should_skip_without_warning(path,
- is_checker_none=True,
- expected=True)
-
- # Check files with non-NONE file type. These examples must be
- # drawn from the _SKIPPED_FILES_WITHOUT_WARNING configuration
- # variable.
- path = os.path.join('LayoutTests', 'foo.txt')
- self._assert_should_skip_without_warning(path,
- is_checker_none=False,
- expected=True)
-
- def test_should_skip_without_warning__false(self):
- """Test should_skip_without_warning() for False return values."""
- paths = ['foo.txt',
- os.path.join('LayoutTests', 'ChangeLog'),
- ]
-
- for path in paths:
- self._assert_should_skip_without_warning(path,
- is_checker_none=False,
- expected=False)
-
-
-class CheckerDispatcherDispatchTest(unittest.TestCase):
-
- """Tests dispatch() method of CheckerDispatcher class."""
-
- def mock_handle_style_error(self):
- pass
-
- def dispatch(self, file_path):
- """Call dispatch() with the given file path."""
- dispatcher = CheckerDispatcher()
- checker = dispatcher.dispatch(file_path,
- self.mock_handle_style_error,
- min_confidence=3)
- return checker
-
- def assert_checker_none(self, file_path):
- """Assert that the dispatched checker is None."""
- checker = self.dispatch(file_path)
- self.assertTrue(checker is None, 'Checking: "%s"' % file_path)
-
- def assert_checker(self, file_path, expected_class):
- """Assert the type of the dispatched checker."""
- checker = self.dispatch(file_path)
- got_class = checker.__class__
- self.assertEquals(got_class, expected_class,
- 'For path "%(file_path)s" got %(got_class)s when '
- "expecting %(expected_class)s."
- % {"file_path": file_path,
- "got_class": got_class,
- "expected_class": expected_class})
-
- def assert_checker_cpp(self, file_path):
- """Assert that the dispatched checker is a CppChecker."""
- self.assert_checker(file_path, CppChecker)
-
- def assert_checker_python(self, file_path):
- """Assert that the dispatched checker is a PythonChecker."""
- self.assert_checker(file_path, PythonChecker)
-
- def assert_checker_text(self, file_path):
- """Assert that the dispatched checker is a TextChecker."""
- self.assert_checker(file_path, TextChecker)
-
- def test_cpp_paths(self):
- """Test paths that should be checked as C++."""
- paths = [
- "-",
- "foo.c",
- "foo.cpp",
- "foo.h",
- ]
-
- for path in paths:
- self.assert_checker_cpp(path)
-
- # Check checker attributes on a typical input.
- file_base = "foo"
- file_extension = "c"
- file_path = file_base + "." + file_extension
- self.assert_checker_cpp(file_path)
- checker = self.dispatch(file_path)
- self.assertEquals(checker.file_extension, file_extension)
- self.assertEquals(checker.file_path, file_path)
- self.assertEquals(checker.handle_style_error, self.mock_handle_style_error)
- self.assertEquals(checker.min_confidence, 3)
- # Check "-" for good measure.
- file_base = "-"
- file_extension = ""
- file_path = file_base
- self.assert_checker_cpp(file_path)
- checker = self.dispatch(file_path)
- self.assertEquals(checker.file_extension, file_extension)
- self.assertEquals(checker.file_path, file_path)
-
- def test_python_paths(self):
- """Test paths that should be checked as Python."""
- paths = [
- "foo.py",
- "WebKitTools/Scripts/modules/text_style.py",
- ]
-
- for path in paths:
- self.assert_checker_python(path)
-
- # Check checker attributes on a typical input.
- file_base = "foo"
- file_extension = "css"
- file_path = file_base + "." + file_extension
- self.assert_checker_text(file_path)
- checker = self.dispatch(file_path)
- self.assertEquals(checker.file_path, file_path)
- self.assertEquals(checker.handle_style_error,
- self.mock_handle_style_error)
-
- def test_text_paths(self):
- """Test paths that should be checked as text."""
- paths = [
- "ChangeLog",
- "ChangeLog-2009-06-16",
- "foo.ac",
- "foo.cc",
- "foo.cgi",
- "foo.css",
- "foo.exp",
- "foo.flex",
- "foo.gyp",
- "foo.gypi",
- "foo.html",
- "foo.idl",
- "foo.in",
- "foo.js",
- "foo.mm",
- "foo.php",
- "foo.pl",
- "foo.pm",
- "foo.pri",
- "foo.pro",
- "foo.rb",
- "foo.sh",
- "foo.txt",
- "foo.wm",
- "foo.xhtml",
- "foo.y",
- os.path.join("WebCore", "ChangeLog"),
- os.path.join("WebCore", "inspector", "front-end", "inspector.js"),
- os.path.join("WebKitTools", "Scripts", "check-webkit-style"),
- ]
-
- for path in paths:
- self.assert_checker_text(path)
-
- # Check checker attributes on a typical input.
- file_base = "foo"
- file_extension = "css"
- file_path = file_base + "." + file_extension
- self.assert_checker_text(file_path)
- checker = self.dispatch(file_path)
- self.assertEquals(checker.file_path, file_path)
- self.assertEquals(checker.handle_style_error, self.mock_handle_style_error)
-
- def test_none_paths(self):
- """Test paths that have no file type.."""
- paths = [
- "Makefile",
- "foo.asdf", # Non-sensical file extension.
- "foo.png",
- "foo.exe",
- "foo.vcproj",
- ]
-
- for path in paths:
- self.assert_checker_none(path)
-
-
-class StyleProcessorConfigurationTest(unittest.TestCase):
-
- """Tests the StyleProcessorConfiguration class."""
-
- def setUp(self):
- self._error_messages = []
- """The messages written to _mock_stderr_write() of this class."""
-
- def _mock_stderr_write(self, message):
- self._error_messages.append(message)
-
- def _style_checker_configuration(self, output_format="vs7"):
- """Return a StyleProcessorConfiguration instance for testing."""
- base_rules = ["-whitespace", "+whitespace/tab"]
- filter_configuration = FilterConfiguration(base_rules=base_rules)
-
- return StyleProcessorConfiguration(
- filter_configuration=filter_configuration,
- max_reports_per_category={"whitespace/newline": 1},
- min_confidence=3,
- output_format=output_format,
- stderr_write=self._mock_stderr_write)
-
- def test_init(self):
- """Test the __init__() method."""
- configuration = self._style_checker_configuration()
-
- # Check that __init__ sets the "public" data attributes correctly.
- self.assertEquals(configuration.max_reports_per_category,
- {"whitespace/newline": 1})
- self.assertEquals(configuration.stderr_write, self._mock_stderr_write)
- self.assertEquals(configuration.min_confidence, 3)
-
- def test_is_reportable(self):
- """Test the is_reportable() method."""
- config = self._style_checker_configuration()
-
- self.assertTrue(config.is_reportable("whitespace/tab", 3, "foo.txt"))
-
- # Test the confidence check code path by varying the confidence.
- self.assertFalse(config.is_reportable("whitespace/tab", 2, "foo.txt"))
-
- # Test the category check code path by varying the category.
- self.assertFalse(config.is_reportable("whitespace/line", 4, "foo.txt"))
-
- def _call_write_style_error(self, output_format):
- config = self._style_checker_configuration(output_format=output_format)
- config.write_style_error(category="whitespace/tab",
- confidence_in_error=5,
- file_path="foo.h",
- line_number=100,
- message="message")
-
- def test_write_style_error_emacs(self):
- """Test the write_style_error() method."""
- self._call_write_style_error("emacs")
- self.assertEquals(self._error_messages,
- ["foo.h:100: message [whitespace/tab] [5]\n"])
-
- def test_write_style_error_vs7(self):
- """Test the write_style_error() method."""
- self._call_write_style_error("vs7")
- self.assertEquals(self._error_messages,
- ["foo.h(100): message [whitespace/tab] [5]\n"])
-
-
-class StyleProcessor_EndToEndTest(LoggingTestCase):
-
- """Test the StyleProcessor class with an emphasis on end-to-end tests."""
-
- def setUp(self):
- LoggingTestCase.setUp(self)
- self._messages = []
-
- def _mock_stderr_write(self, message):
- """Save a message so it can later be asserted."""
- self._messages.append(message)
-
- def test_init(self):
- """Test __init__ constructor."""
- configuration = StyleProcessorConfiguration(
- filter_configuration=FilterConfiguration(),
- max_reports_per_category={},
- min_confidence=3,
- output_format="vs7",
- stderr_write=self._mock_stderr_write)
- processor = StyleProcessor(configuration)
-
- self.assertEquals(processor.error_count, 0)
- self.assertEquals(self._messages, [])
-
- def test_process(self):
- configuration = StyleProcessorConfiguration(
- filter_configuration=FilterConfiguration(),
- max_reports_per_category={},
- min_confidence=3,
- output_format="vs7",
- stderr_write=self._mock_stderr_write)
- processor = StyleProcessor(configuration)
-
- processor.process(lines=['line1', 'Line with tab:\t'],
- file_path='foo.txt')
- self.assertEquals(processor.error_count, 1)
- expected_messages = ['foo.txt(2): Line contains tab character. '
- '[whitespace/tab] [5]\n']
- self.assertEquals(self._messages, expected_messages)
-
-
-class StyleProcessor_CodeCoverageTest(LoggingTestCase):
-
- """Test the StyleProcessor class with an emphasis on code coverage.
-
- This class makes heavy use of mock objects.
-
- """
-
- class MockDispatchedChecker(object):
-
- """A mock checker dispatched by the MockDispatcher."""
-
- def __init__(self, file_path, min_confidence, style_error_handler):
- self.file_path = file_path
- self.min_confidence = min_confidence
- self.style_error_handler = style_error_handler
-
- def check(self, lines):
- self.lines = lines
-
- class MockDispatcher(object):
-
- """A mock CheckerDispatcher class."""
-
- def __init__(self):
- self.dispatched_checker = None
-
- def should_skip_with_warning(self, file_path):
- return file_path.endswith('skip_with_warning.txt')
-
- def should_skip_without_warning(self, file_path):
- return file_path.endswith('skip_without_warning.txt')
-
- def dispatch(self, file_path, style_error_handler, min_confidence):
- if file_path.endswith('do_not_process.txt'):
- return None
-
- checker = StyleProcessor_CodeCoverageTest.MockDispatchedChecker(
- file_path,
- min_confidence,
- style_error_handler)
-
- # Save the dispatched checker so the current test case has a
- # way to access and check it.
- self.dispatched_checker = checker
-
- return checker
-
- def setUp(self):
- LoggingTestCase.setUp(self)
- # We can pass an error-message swallower here because error message
- # output is tested instead in the end-to-end test case above.
- configuration = StyleProcessorConfiguration(
- filter_configuration=FilterConfiguration(),
- max_reports_per_category={"whitespace/newline": 1},
- min_confidence=3,
- output_format="vs7",
- stderr_write=self._swallow_stderr_message)
-
- mock_carriage_checker_class = self._create_carriage_checker_class()
- mock_dispatcher = self.MockDispatcher()
- # We do not need to use a real incrementer here because error-count
- # incrementing is tested instead in the end-to-end test case above.
- mock_increment_error_count = self._do_nothing
-
- processor = StyleProcessor(configuration=configuration,
- mock_carriage_checker_class=mock_carriage_checker_class,
- mock_dispatcher=mock_dispatcher,
- mock_increment_error_count=mock_increment_error_count)
-
- self._configuration = configuration
- self._mock_dispatcher = mock_dispatcher
- self._processor = processor
-
- def _do_nothing(self):
- # We provide this function so the caller can pass it to the
- # StyleProcessor constructor. This lets us assert the equality of
- # the DefaultStyleErrorHandler instance generated by the process()
- # method with an expected instance.
- pass
-
- def _swallow_stderr_message(self, message):
- """Swallow a message passed to stderr.write()."""
- # This is a mock stderr.write() for passing to the constructor
- # of the StyleProcessorConfiguration class.
- pass
-
- def _create_carriage_checker_class(self):
-
- # Create a reference to self with a new name so its name does not
- # conflict with the self introduced below.
- test_case = self
-
- class MockCarriageChecker(object):
-
- """A mock carriage-return checker."""
-
- def __init__(self, style_error_handler):
- self.style_error_handler = style_error_handler
-
- # This gives the current test case access to the
- # instantiated carriage checker.
- test_case.carriage_checker = self
-
- def check(self, lines):
- # Save the lines so the current test case has a way to access
- # and check them.
- self.lines = lines
-
- return lines
-
- return MockCarriageChecker
-
- def test_should_process__skip_without_warning(self):
- """Test should_process() for a skip-without-warning file."""
- file_path = "foo/skip_without_warning.txt"
-
- self.assertFalse(self._processor.should_process(file_path))
-
- def test_should_process__skip_with_warning(self):
- """Test should_process() for a skip-with-warning file."""
- file_path = "foo/skip_with_warning.txt"
-
- self.assertFalse(self._processor.should_process(file_path))
-
- self.assertLog(['WARNING: File exempt from style guide. '
- 'Skipping: "foo/skip_with_warning.txt"\n'])
-
- def test_should_process__true_result(self):
- """Test should_process() for a file that should be processed."""
- file_path = "foo/skip_process.txt"
-
- self.assertTrue(self._processor.should_process(file_path))
-
- def test_process__checker_dispatched(self):
- """Test the process() method for a path with a dispatched checker."""
- file_path = 'foo.txt'
- lines = ['line1', 'line2']
- line_numbers = [100]
-
- expected_error_handler = DefaultStyleErrorHandler(
- configuration=self._configuration,
- file_path=file_path,
- increment_error_count=self._do_nothing,
- line_numbers=line_numbers)
-
- self._processor.process(lines=lines,
- file_path=file_path,
- line_numbers=line_numbers)
-
- # Check that the carriage-return checker was instantiated correctly
- # and was passed lines correctly.
- carriage_checker = self.carriage_checker
- self.assertEquals(carriage_checker.style_error_handler,
- expected_error_handler)
- self.assertEquals(carriage_checker.lines, ['line1', 'line2'])
-
- # Check that the style checker was dispatched correctly and was
- # passed lines correctly.
- checker = self._mock_dispatcher.dispatched_checker
- self.assertEquals(checker.file_path, 'foo.txt')
- self.assertEquals(checker.min_confidence, 3)
- self.assertEquals(checker.style_error_handler, expected_error_handler)
-
- self.assertEquals(checker.lines, ['line1', 'line2'])
-
- def test_process__no_checker_dispatched(self):
- """Test the process() method for a path with no dispatched checker."""
- path = os.path.join('foo', 'do_not_process.txt')
- self.assertRaises(AssertionError, self._processor.process,
- lines=['line1', 'line2'], file_path=path,
- line_numbers=[100])
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/__init__.py b/WebKitTools/Scripts/webkitpy/style/checkers/__init__.py
deleted file mode 100644
index ef65bee..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checkers/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/common.py b/WebKitTools/Scripts/webkitpy/style/checkers/common.py
deleted file mode 100644
index 76aa956..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checkers/common.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# 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.
-
-"""Supports style checking not specific to any one file type."""
-
-
-# FIXME: Test this list in the same way that the list of CppChecker
-# categories is tested, for example by checking that all of its
-# elements appear in the unit tests. This should probably be done
-# after moving the relevant cpp_unittest.ErrorCollector code
-# into a shared location and refactoring appropriately.
-categories = set([
- "whitespace/carriage_return",
- "whitespace/tab"])
-
-
-class CarriageReturnChecker(object):
-
- """Supports checking for and handling carriage returns."""
-
- def __init__(self, handle_style_error):
- self._handle_style_error = handle_style_error
-
- def check(self, lines):
- """Check for and strip trailing carriage returns from lines."""
- for line_number in range(len(lines)):
- if not lines[line_number].endswith("\r"):
- continue
-
- self._handle_style_error(line_number + 1, # Correct for offset.
- "whitespace/carriage_return",
- 1,
- "One or more unexpected \\r (^M) found; "
- "better to use only a \\n")
-
- lines[line_number] = lines[line_number].rstrip("\r")
-
- return lines
-
-
-class TabChecker(object):
-
- """Supports checking for and handling tabs."""
-
- def __init__(self, file_path, handle_style_error):
- self.file_path = file_path
- self.handle_style_error = handle_style_error
-
- def check(self, lines):
- # FIXME: share with cpp_style.
- for line_number, line in enumerate(lines):
- if "\t" in line:
- self.handle_style_error(line_number + 1,
- "whitespace/tab", 5,
- "Line contains tab character.")
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/common_unittest.py b/WebKitTools/Scripts/webkitpy/style/checkers/common_unittest.py
deleted file mode 100644
index 1fe1263..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checkers/common_unittest.py
+++ /dev/null
@@ -1,124 +0,0 @@
-# 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 for common.py."""
-
-import unittest
-
-from common import CarriageReturnChecker
-from common import TabChecker
-
-# FIXME: The unit tests for the cpp, text, and common checkers should
-# share supporting test code. This can include, for example, the
-# mock style error handling code and the code to check that all
-# of a checker's categories are covered by the unit tests.
-# Such shared code can be located in a shared test file, perhaps
-# even this file.
-class CarriageReturnCheckerTest(unittest.TestCase):
-
- """Tests check_no_carriage_return()."""
-
- _category = "whitespace/carriage_return"
- _confidence = 1
- _expected_message = ("One or more unexpected \\r (^M) found; "
- "better to use only a \\n")
-
- def setUp(self):
- self._style_errors = [] # The list of accumulated style errors.
-
- def _mock_style_error_handler(self, line_number, category, confidence,
- message):
- """Append the error information to the list of style errors."""
- error = (line_number, category, confidence, message)
- self._style_errors.append(error)
-
- def assert_carriage_return(self, input_lines, expected_lines, error_lines):
- """Process the given line and assert that the result is correct."""
- handle_style_error = self._mock_style_error_handler
-
- checker = CarriageReturnChecker(handle_style_error)
- output_lines = checker.check(input_lines)
-
- # Check both the return value and error messages.
- self.assertEquals(output_lines, expected_lines)
-
- expected_errors = [(line_number, self._category, self._confidence,
- self._expected_message)
- for line_number in error_lines]
- self.assertEquals(self._style_errors, expected_errors)
-
- def test_ends_with_carriage(self):
- self.assert_carriage_return(["carriage return\r"],
- ["carriage return"],
- [1])
-
- def test_ends_with_nothing(self):
- self.assert_carriage_return(["no carriage return"],
- ["no carriage return"],
- [])
-
- def test_ends_with_newline(self):
- self.assert_carriage_return(["no carriage return\n"],
- ["no carriage return\n"],
- [])
-
- def test_carriage_in_middle(self):
- # The CarriageReturnChecker checks only the final character
- # of each line.
- self.assert_carriage_return(["carriage\r in a string"],
- ["carriage\r in a string"],
- [])
-
- def test_multiple_errors(self):
- self.assert_carriage_return(["line1", "line2\r", "line3\r"],
- ["line1", "line2", "line3"],
- [2, 3])
-
-
-class TabCheckerTest(unittest.TestCase):
-
- """Tests for TabChecker."""
-
- def assert_tab(self, input_lines, error_lines):
- """Assert when the given lines contain tabs."""
- self._error_lines = []
-
- def style_error_handler(line_number, category, confidence, message):
- self.assertEqual(category, 'whitespace/tab')
- self.assertEqual(confidence, 5)
- self.assertEqual(message, 'Line contains tab character.')
- self._error_lines.append(line_number)
-
- checker = TabChecker('', style_error_handler)
- checker.check(input_lines)
- self.assertEquals(self._error_lines, error_lines)
-
- def test_notab(self):
- self.assert_tab([''], [])
- self.assert_tab(['foo', 'bar'], [])
-
- def test_tab(self):
- self.assert_tab(['\tfoo'], [1])
- self.assert_tab(['line1', '\tline2', 'line3\t'], [2, 3])
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/cpp.py b/WebKitTools/Scripts/webkitpy/style/checkers/cpp.py
deleted file mode 100644
index 590bba9..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checkers/cpp.py
+++ /dev/null
@@ -1,3126 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2009 Google Inc. All rights reserved.
-# Copyright (C) 2009 Torch Mobile Inc.
-# Copyright (C) 2009 Apple Inc. All rights reserved.
-# 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:
-#
-# * 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.
-
-# This is the modified version of Google's cpplint. The original code is
-# http://google-styleguide.googlecode.com/svn/trunk/cpplint/cpplint.py
-
-"""Support for check-webkit-style."""
-
-import codecs
-import math # for log
-import os
-import os.path
-import re
-import sre_compile
-import string
-import sys
-import unicodedata
-
-
-# Headers that we consider STL headers.
-_STL_HEADERS = frozenset([
- 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception',
- 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set',
- 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'pair.h',
- 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack',
- 'stl_alloc.h', 'stl_relops.h', 'type_traits.h',
- 'utility', 'vector', 'vector.h',
- ])
-
-
-# Non-STL C++ system headers.
-_CPP_HEADERS = frozenset([
- 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype',
- 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath',
- 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef',
- 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype',
- 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream',
- 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip',
- 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream.h',
- 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h',
- 'numeric', 'ostream.h', 'parsestream.h', 'pfstream.h', 'PlotFile.h',
- 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', 'ropeimpl.h',
- 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept',
- 'stdiostream.h', 'streambuf.h', 'stream.h', 'strfile.h', 'string',
- 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', 'valarray',
- ])
-
-
-# Assertion macros. These are defined in base/logging.h and
-# testing/base/gunit.h. Note that the _M versions need to come first
-# for substring matching to work.
-_CHECK_MACROS = [
- 'DCHECK', 'CHECK',
- 'EXPECT_TRUE_M', 'EXPECT_TRUE',
- 'ASSERT_TRUE_M', 'ASSERT_TRUE',
- 'EXPECT_FALSE_M', 'EXPECT_FALSE',
- 'ASSERT_FALSE_M', 'ASSERT_FALSE',
- ]
-
-# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE
-_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS])
-
-for op, replacement in [('==', 'EQ'), ('!=', 'NE'),
- ('>=', 'GE'), ('>', 'GT'),
- ('<=', 'LE'), ('<', 'LT')]:
- _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement
- _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement
- _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement
- _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement
- _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement
- _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement
-
-for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'),
- ('>=', 'LT'), ('>', 'LE'),
- ('<=', 'GT'), ('<', 'GE')]:
- _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement
- _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement
- _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement
- _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement
-
-
-# These constants define types of headers for use with
-# _IncludeState.check_next_include_order().
-_CONFIG_HEADER = 0
-_PRIMARY_HEADER = 1
-_OTHER_HEADER = 2
-_MOC_HEADER = 3
-
-
-# The regexp compilation caching is inlined in all regexp functions for
-# performance reasons; factoring it out into a separate function turns out
-# to be noticeably expensive.
-_regexp_compile_cache = {}
-
-
-def match(pattern, s):
- """Matches the string with the pattern, caching the compiled regexp."""
- if not pattern in _regexp_compile_cache:
- _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
- return _regexp_compile_cache[pattern].match(s)
-
-
-def search(pattern, s):
- """Searches the string for the pattern, caching the compiled regexp."""
- if not pattern in _regexp_compile_cache:
- _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
- return _regexp_compile_cache[pattern].search(s)
-
-
-def sub(pattern, replacement, s):
- """Substitutes occurrences of a pattern, caching the compiled regexp."""
- if not pattern in _regexp_compile_cache:
- _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
- return _regexp_compile_cache[pattern].sub(replacement, s)
-
-
-def subn(pattern, replacement, s):
- """Substitutes occurrences of a pattern, caching the compiled regexp."""
- if not pattern in _regexp_compile_cache:
- _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
- return _regexp_compile_cache[pattern].subn(replacement, s)
-
-
-def iteratively_replace_matches_with_char(pattern, char_replacement, s):
- """Returns the string with replacement done.
-
- Every character in the match is replaced with char.
- Due to the iterative nature, pattern should not match char or
- there will be an infinite loop.
-
- Example:
- pattern = r'<[^>]>' # template parameters
- char_replacement = '_'
- s = 'A<B<C, D>>'
- Returns 'A_________'
-
- Args:
- pattern: The regex to match.
- char_replacement: The character to put in place of every
- character of the match.
- s: The string on which to do the replacements.
-
- Returns:
- True, if the given line is blank.
- """
- while True:
- matched = search(pattern, s)
- if not matched:
- return s
- start_match_index = matched.start(0)
- end_match_index = matched.end(0)
- match_length = end_match_index - start_match_index
- s = s[:start_match_index] + char_replacement * match_length + s[end_match_index:]
-
-
-def up_to_unmatched_closing_paren(s):
- """Splits a string into two parts up to first unmatched ')'.
-
- Args:
- s: a string which is a substring of line after '('
- (e.g., "a == (b + c))").
-
- Returns:
- A pair of strings (prefix before first unmatched ')',
- remainder of s after first unmatched ')'), e.g.,
- up_to_unmatched_closing_paren("a == (b + c)) { ")
- returns "a == (b + c)", " {".
- Returns None, None if there is no unmatched ')'
-
- """
- i = 1
- for pos, c in enumerate(s):
- if c == '(':
- i += 1
- elif c == ')':
- i -= 1
- if i == 0:
- return s[:pos], s[pos + 1:]
- return None, None
-
-class _IncludeState(dict):
- """Tracks line numbers for includes, and the order in which includes appear.
-
- As a dict, an _IncludeState object serves as a mapping between include
- filename and line number on which that file was included.
-
- Call check_next_include_order() once for each header in the file, passing
- in the type constants defined above. Calls in an illegal order will
- raise an _IncludeError with an appropriate error message.
-
- """
- # self._section will move monotonically through this set. If it ever
- # needs to move backwards, check_next_include_order will raise an error.
- _INITIAL_SECTION = 0
- _CONFIG_SECTION = 1
- _PRIMARY_SECTION = 2
- _OTHER_SECTION = 3
-
- _TYPE_NAMES = {
- _CONFIG_HEADER: 'WebCore config.h',
- _PRIMARY_HEADER: 'header this file implements',
- _OTHER_HEADER: 'other header',
- _MOC_HEADER: 'moc file',
- }
- _SECTION_NAMES = {
- _INITIAL_SECTION: "... nothing.",
- _CONFIG_SECTION: "WebCore config.h.",
- _PRIMARY_SECTION: 'a header this file implements.',
- _OTHER_SECTION: 'other header.',
- }
-
- def __init__(self):
- dict.__init__(self)
- self._section = self._INITIAL_SECTION
- self._visited_primary_section = False
- self.header_types = dict();
-
- def visited_primary_section(self):
- return self._visited_primary_section
-
- def check_next_include_order(self, header_type, file_is_header):
- """Returns a non-empty error message if the next header is out of order.
-
- This function also updates the internal state to be ready to check
- the next include.
-
- Args:
- header_type: One of the _XXX_HEADER constants defined above.
- file_is_header: Whether the file that owns this _IncludeState is itself a header
-
- Returns:
- The empty string if the header is in the right order, or an
- error message describing what's wrong.
-
- """
- if header_type == _CONFIG_HEADER and file_is_header:
- return 'Header file should not contain WebCore config.h.'
- if header_type == _PRIMARY_HEADER and file_is_header:
- return 'Header file should not contain itself.'
- if header_type == _MOC_HEADER:
- return ''
-
- error_message = ''
- if self._section != self._OTHER_SECTION:
- before_error_message = ('Found %s before %s' %
- (self._TYPE_NAMES[header_type],
- self._SECTION_NAMES[self._section + 1]))
- after_error_message = ('Found %s after %s' %
- (self._TYPE_NAMES[header_type],
- self._SECTION_NAMES[self._section]))
-
- if header_type == _CONFIG_HEADER:
- if self._section >= self._CONFIG_SECTION:
- error_message = after_error_message
- self._section = self._CONFIG_SECTION
- elif header_type == _PRIMARY_HEADER:
- if self._section >= self._PRIMARY_SECTION:
- error_message = after_error_message
- elif self._section < self._CONFIG_SECTION:
- error_message = before_error_message
- self._section = self._PRIMARY_SECTION
- self._visited_primary_section = True
- else:
- assert header_type == _OTHER_HEADER
- if not file_is_header and self._section < self._PRIMARY_SECTION:
- error_message = before_error_message
- self._section = self._OTHER_SECTION
-
- return error_message
-
-
-class _FunctionState(object):
- """Tracks current function name and the number of lines in its body.
-
- Attributes:
- min_confidence: The minimum confidence level to use while checking style.
-
- """
-
- _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc.
- _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER.
-
- def __init__(self, min_confidence):
- self.min_confidence = min_confidence
- self.current_function = ''
- self.in_a_function = False
- self.lines_in_function = 0
- # Make sure these will not be mistaken for real lines (even when a
- # small amount is added to them).
- self.body_start_line_number = -1000
- self.ending_line_number = -1000
-
- def begin(self, function_name, body_start_line_number, ending_line_number):
- """Start analyzing function body.
-
- Args:
- function_name: The name of the function being tracked.
- ending_line_number: The line number where the function ends.
- """
- self.in_a_function = True
- self.lines_in_function = 0
- self.current_function = function_name
- self.body_start_line_number = body_start_line_number
- self.ending_line_number = ending_line_number
-
- def count(self, line_number):
- """Count line in current function body."""
- if self.in_a_function and line_number >= self.body_start_line_number:
- self.lines_in_function += 1
-
- def check(self, error, line_number):
- """Report if too many lines in function body.
-
- Args:
- error: The function to call with any errors found.
- line_number: The number of the line to check.
- """
- if match(r'T(EST|est)', self.current_function):
- base_trigger = self._TEST_TRIGGER
- else:
- base_trigger = self._NORMAL_TRIGGER
- trigger = base_trigger * 2 ** self.min_confidence
-
- if self.lines_in_function > trigger:
- error_level = int(math.log(self.lines_in_function / base_trigger, 2))
- # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ...
- if error_level > 5:
- error_level = 5
- error(line_number, 'readability/fn_size', error_level,
- 'Small and focused functions are preferred:'
- ' %s has %d non-comment lines'
- ' (error triggered by exceeding %d lines).' % (
- self.current_function, self.lines_in_function, trigger))
-
- def end(self):
- """Stop analyzing function body."""
- self.in_a_function = False
-
-
-class _IncludeError(Exception):
- """Indicates a problem with the include order in a file."""
- pass
-
-
-def is_c_or_objective_c(file_extension):
- """Return whether the file extension corresponds to C or Objective-C.
-
- Args:
- file_extension: The file extension without the leading dot.
-
- """
- return file_extension in ['c', 'm']
-
-
-class FileInfo:
- """Provides utility functions for filenames.
-
- FileInfo provides easy access to the components of a file's path
- relative to the project root.
- """
-
- def __init__(self, filename):
- self._filename = filename
-
- def full_name(self):
- """Make Windows paths like Unix."""
- return os.path.abspath(self._filename).replace('\\', '/')
-
- def repository_name(self):
- """Full name after removing the local path to the repository.
-
- If we have a real absolute path name here we can try to do something smart:
- detecting the root of the checkout and truncating /path/to/checkout from
- the name so that we get header guards that don't include things like
- "C:\Documents and Settings\..." or "/home/username/..." in them and thus
- people on different computers who have checked the source out to different
- locations won't see bogus errors.
- """
- fullname = self.full_name()
-
- if os.path.exists(fullname):
- project_dir = os.path.dirname(fullname)
-
- if os.path.exists(os.path.join(project_dir, ".svn")):
- # If there's a .svn file in the current directory, we
- # recursively look up the directory tree for the top
- # of the SVN checkout
- root_dir = project_dir
- one_up_dir = os.path.dirname(root_dir)
- while os.path.exists(os.path.join(one_up_dir, ".svn")):
- root_dir = os.path.dirname(root_dir)
- one_up_dir = os.path.dirname(one_up_dir)
-
- prefix = os.path.commonprefix([root_dir, project_dir])
- return fullname[len(prefix) + 1:]
-
- # Not SVN? Try to find a git top level directory by
- # searching up from the current path.
- root_dir = os.path.dirname(fullname)
- while (root_dir != os.path.dirname(root_dir)
- and not os.path.exists(os.path.join(root_dir, ".git"))):
- root_dir = os.path.dirname(root_dir)
- if os.path.exists(os.path.join(root_dir, ".git")):
- prefix = os.path.commonprefix([root_dir, project_dir])
- return fullname[len(prefix) + 1:]
-
- # Don't know what to do; header guard warnings may be wrong...
- return fullname
-
- def split(self):
- """Splits the file into the directory, basename, and extension.
-
- For 'chrome/browser/browser.cpp', Split() would
- return ('chrome/browser', 'browser', '.cpp')
-
- Returns:
- A tuple of (directory, basename, extension).
- """
-
- googlename = self.repository_name()
- project, rest = os.path.split(googlename)
- return (project,) + os.path.splitext(rest)
-
- def base_name(self):
- """File base name - text after the final slash, before the final period."""
- return self.split()[1]
-
- def extension(self):
- """File extension - text following the final period."""
- return self.split()[2]
-
- def no_extension(self):
- """File has no source file extension."""
- return '/'.join(self.split()[0:2])
-
- def is_source(self):
- """File has a source file extension."""
- return self.extension()[1:] in ('c', 'cc', 'cpp', 'cxx')
-
-
-# Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard.
-_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile(
- r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)')
-# Matches strings. Escape codes should already be removed by ESCAPES.
-_RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"')
-# Matches characters. Escape codes should already be removed by ESCAPES.
-_RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'")
-# Matches multi-line C++ comments.
-# This RE is a little bit more complicated than one might expect, because we
-# have to take care of space removals tools so we can handle comments inside
-# statements better.
-# The current rule is: We only clear spaces from both sides when we're at the
-# end of the line. Otherwise, we try to remove spaces from the right side,
-# if this doesn't work we try on left side but only if there's a non-character
-# on the right.
-_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile(
- r"""(\s*/\*.*\*/\s*$|
- /\*.*\*/\s+|
- \s+/\*.*\*/(?=\W)|
- /\*.*\*/)""", re.VERBOSE)
-
-
-def is_cpp_string(line):
- """Does line terminate so, that the next symbol is in string constant.
-
- This function does not consider single-line nor multi-line comments.
-
- Args:
- line: is a partial line of code starting from the 0..n.
-
- Returns:
- True, if next character appended to 'line' is inside a
- string constant.
- """
-
- line = line.replace(r'\\', 'XX') # after this, \\" does not match to \"
- return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1
-
-
-def find_next_multi_line_comment_start(lines, line_index):
- """Find the beginning marker for a multiline comment."""
- while line_index < len(lines):
- if lines[line_index].strip().startswith('/*'):
- # Only return this marker if the comment goes beyond this line
- if lines[line_index].strip().find('*/', 2) < 0:
- return line_index
- line_index += 1
- return len(lines)
-
-
-def find_next_multi_line_comment_end(lines, line_index):
- """We are inside a comment, find the end marker."""
- while line_index < len(lines):
- if lines[line_index].strip().endswith('*/'):
- return line_index
- line_index += 1
- return len(lines)
-
-
-def remove_multi_line_comments_from_range(lines, begin, end):
- """Clears a range of lines for multi-line comments."""
- # Having // dummy comments makes the lines non-empty, so we will not get
- # unnecessary blank line warnings later in the code.
- for i in range(begin, end):
- lines[i] = '// dummy'
-
-
-def remove_multi_line_comments(lines, error):
- """Removes multiline (c-style) comments from lines."""
- line_index = 0
- while line_index < len(lines):
- line_index_begin = find_next_multi_line_comment_start(lines, line_index)
- if line_index_begin >= len(lines):
- return
- line_index_end = find_next_multi_line_comment_end(lines, line_index_begin)
- if line_index_end >= len(lines):
- error(line_index_begin + 1, 'readability/multiline_comment', 5,
- 'Could not find end of multi-line comment')
- return
- remove_multi_line_comments_from_range(lines, line_index_begin, line_index_end + 1)
- line_index = line_index_end + 1
-
-
-def cleanse_comments(line):
- """Removes //-comments and single-line C-style /* */ comments.
-
- Args:
- line: A line of C++ source.
-
- Returns:
- The line with single-line comments removed.
- """
- comment_position = line.find('//')
- if comment_position != -1 and not is_cpp_string(line[:comment_position]):
- line = line[:comment_position]
- # get rid of /* ... */
- return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line)
-
-
-class CleansedLines(object):
- """Holds 3 copies of all lines with different preprocessing applied to them.
-
- 1) elided member contains lines without strings and comments,
- 2) lines member contains lines without comments, and
- 3) raw member contains all the lines without processing.
- All these three members are of <type 'list'>, and of the same length.
- """
-
- def __init__(self, lines):
- self.elided = []
- self.lines = []
- self.raw_lines = lines
- self._num_lines = len(lines)
- for line_number in range(len(lines)):
- self.lines.append(cleanse_comments(lines[line_number]))
- elided = self.collapse_strings(lines[line_number])
- self.elided.append(cleanse_comments(elided))
-
- def num_lines(self):
- """Returns the number of lines represented."""
- return self._num_lines
-
- @staticmethod
- def collapse_strings(elided):
- """Collapses strings and chars on a line to simple "" or '' blocks.
-
- We nix strings first so we're not fooled by text like '"http://"'
-
- Args:
- elided: The line being processed.
-
- Returns:
- The line with collapsed strings.
- """
- if not _RE_PATTERN_INCLUDE.match(elided):
- # Remove escaped characters first to make quote/single quote collapsing
- # basic. Things that look like escaped characters shouldn't occur
- # outside of strings and chars.
- elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided)
- elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided)
- elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided)
- return elided
-
-
-def close_expression(clean_lines, line_number, pos):
- """If input points to ( or { or [, finds the position that closes it.
-
- If clean_lines.elided[line_number][pos] points to a '(' or '{' or '[', finds
- the line_number/pos that correspond to the closing of the expression.
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- pos: A position on the line.
-
- Returns:
- A tuple (line, line_number, pos) pointer *past* the closing brace, or
- ('', len(clean_lines.elided), -1) if we never find a close. Note we
- ignore strings and comments when matching; and the line we return is the
- 'cleansed' line at line_number.
- """
-
- line = clean_lines.elided[line_number]
- start_character = line[pos]
- if start_character not in '({[':
- return (line, clean_lines.num_lines(), -1)
- if start_character == '(':
- end_character = ')'
- if start_character == '[':
- end_character = ']'
- if start_character == '{':
- end_character = '}'
-
- num_open = line.count(start_character) - line.count(end_character)
- while num_open > 0:
- line_number += 1
- if line_number >= clean_lines.num_lines():
- return ('', len(clean_lines.elided), -1)
- line = clean_lines.elided[line_number]
- num_open += line.count(start_character) - line.count(end_character)
- # OK, now find the end_character that actually got us back to even
- endpos = len(line)
- while num_open >= 0:
- endpos = line.rfind(')', 0, endpos)
- num_open -= 1 # chopped off another )
- return (line, line_number, endpos + 1)
-
-
-def check_for_copyright(lines, error):
- """Logs an error if no Copyright message appears at the top of the file."""
-
- # We'll say it should occur by line 10. Don't forget there's a
- # dummy line at the front.
- for line in xrange(1, min(len(lines), 11)):
- if re.search(r'Copyright', lines[line], re.I):
- break
- else: # means no copyright line was found
- error(0, 'legal/copyright', 5,
- 'No copyright message found. '
- 'You should have a line: "Copyright [year] <Copyright Owner>"')
-
-
-def get_header_guard_cpp_variable(filename):
- """Returns the CPP variable that should be used as a header guard.
-
- Args:
- filename: The name of a C++ header file.
-
- Returns:
- The CPP variable that should be used as a header guard in the
- named file.
-
- """
-
- # Restores original filename in case that style checker is invoked from Emacs's
- # flymake.
- filename = re.sub(r'_flymake\.h$', '.h', filename)
-
- return sub(r'[-.\s]', '_', os.path.basename(filename))
-
-
-def check_for_header_guard(filename, lines, error):
- """Checks that the file contains a header guard.
-
- Logs an error if no #ifndef header guard is present. For other
- headers, checks that the full pathname is used.
-
- Args:
- filename: The name of the C++ header file.
- lines: An array of strings, each representing a line of the file.
- error: The function to call with any errors found.
- """
-
- cppvar = get_header_guard_cpp_variable(filename)
-
- ifndef = None
- ifndef_line_number = 0
- define = None
- for line_number, line in enumerate(lines):
- line_split = line.split()
- if len(line_split) >= 2:
- # find the first occurrence of #ifndef and #define, save arg
- if not ifndef and line_split[0] == '#ifndef':
- # set ifndef to the header guard presented on the #ifndef line.
- ifndef = line_split[1]
- ifndef_line_number = line_number
- if not define and line_split[0] == '#define':
- define = line_split[1]
- if define and ifndef:
- break
-
- if not ifndef or not define or ifndef != define:
- error(0, 'build/header_guard', 5,
- 'No #ifndef header guard found, suggested CPP variable is: %s' %
- cppvar)
- return
-
- # The guard should be File_h.
- if ifndef != cppvar:
- error(ifndef_line_number, 'build/header_guard', 5,
- '#ifndef header guard has wrong style, please use: %s' % cppvar)
-
-
-def check_for_unicode_replacement_characters(lines, error):
- """Logs an error for each line containing Unicode replacement characters.
-
- These indicate that either the file contained invalid UTF-8 (likely)
- or Unicode replacement characters (which it shouldn't). Note that
- it's possible for this to throw off line numbering if the invalid
- UTF-8 occurred adjacent to a newline.
-
- Args:
- lines: An array of strings, each representing a line of the file.
- error: The function to call with any errors found.
- """
- for line_number, line in enumerate(lines):
- if u'\ufffd' in line:
- error(line_number, 'readability/utf8', 5,
- 'Line contains invalid UTF-8 (or Unicode replacement character).')
-
-
-def check_for_new_line_at_eof(lines, error):
- """Logs an error if there is no newline char at the end of the file.
-
- Args:
- lines: An array of strings, each representing a line of the file.
- error: The function to call with any errors found.
- """
-
- # The array lines() was created by adding two newlines to the
- # original file (go figure), then splitting on \n.
- # To verify that the file ends in \n, we just have to make sure the
- # last-but-two element of lines() exists and is empty.
- if len(lines) < 3 or lines[-2]:
- error(len(lines) - 2, 'whitespace/ending_newline', 5,
- 'Could not find a newline character at the end of the file.')
-
-
-def check_for_multiline_comments_and_strings(clean_lines, line_number, error):
- """Logs an error if we see /* ... */ or "..." that extend past one line.
-
- /* ... */ comments are legit inside macros, for one line.
- Otherwise, we prefer // comments, so it's ok to warn about the
- other. Likewise, it's ok for strings to extend across multiple
- lines, as long as a line continuation character (backslash)
- terminates each line. Although not currently prohibited by the C++
- style guide, it's ugly and unnecessary. We don't do well with either
- in this lint program, so we warn about both.
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- error: The function to call with any errors found.
- """
- line = clean_lines.elided[line_number]
-
- # Remove all \\ (escaped backslashes) from the line. They are OK, and the
- # second (escaped) slash may trigger later \" detection erroneously.
- line = line.replace('\\\\', '')
-
- if line.count('/*') > line.count('*/'):
- error(line_number, 'readability/multiline_comment', 5,
- 'Complex multi-line /*...*/-style comment found. '
- 'Lint may give bogus warnings. '
- 'Consider replacing these with //-style comments, '
- 'with #if 0...#endif, '
- 'or with more clearly structured multi-line comments.')
-
- if (line.count('"') - line.count('\\"')) % 2:
- error(line_number, 'readability/multiline_string', 5,
- 'Multi-line string ("...") found. This lint script doesn\'t '
- 'do well with such strings, and may give bogus warnings. They\'re '
- 'ugly and unnecessary, and you should use concatenation instead".')
-
-
-_THREADING_LIST = (
- ('asctime(', 'asctime_r('),
- ('ctime(', 'ctime_r('),
- ('getgrgid(', 'getgrgid_r('),
- ('getgrnam(', 'getgrnam_r('),
- ('getlogin(', 'getlogin_r('),
- ('getpwnam(', 'getpwnam_r('),
- ('getpwuid(', 'getpwuid_r('),
- ('gmtime(', 'gmtime_r('),
- ('localtime(', 'localtime_r('),
- ('rand(', 'rand_r('),
- ('readdir(', 'readdir_r('),
- ('strtok(', 'strtok_r('),
- ('ttyname(', 'ttyname_r('),
- )
-
-
-def check_posix_threading(clean_lines, line_number, error):
- """Checks for calls to thread-unsafe functions.
-
- Much code has been originally written without consideration of
- multi-threading. Also, engineers are relying on their old experience;
- they have learned posix before threading extensions were added. These
- tests guide the engineers to use thread-safe functions (when using
- posix directly).
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- error: The function to call with any errors found.
- """
- line = clean_lines.elided[line_number]
- for single_thread_function, multithread_safe_function in _THREADING_LIST:
- index = line.find(single_thread_function)
- # Comparisons made explicit for clarity -- pylint: disable-msg=C6403
- if index >= 0 and (index == 0 or (not line[index - 1].isalnum()
- and line[index - 1] not in ('_', '.', '>'))):
- error(line_number, 'runtime/threadsafe_fn', 2,
- 'Consider using ' + multithread_safe_function +
- '...) instead of ' + single_thread_function +
- '...) for improved thread safety.')
-
-
-# Matches invalid increment: *count++, which moves pointer instead of
-# incrementing a value.
-_RE_PATTERN_INVALID_INCREMENT = re.compile(
- r'^\s*\*\w+(\+\+|--);')
-
-
-def check_invalid_increment(clean_lines, line_number, error):
- """Checks for invalid increment *count++.
-
- For example following function:
- void increment_counter(int* count) {
- *count++;
- }
- is invalid, because it effectively does count++, moving pointer, and should
- be replaced with ++*count, (*count)++ or *count += 1.
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- error: The function to call with any errors found.
- """
- line = clean_lines.elided[line_number]
- if _RE_PATTERN_INVALID_INCREMENT.match(line):
- error(line_number, 'runtime/invalid_increment', 5,
- 'Changing pointer instead of value (or unused value of operator*).')
-
-
-class _ClassInfo(object):
- """Stores information about a class."""
-
- def __init__(self, name, line_number):
- self.name = name
- self.line_number = line_number
- self.seen_open_brace = False
- self.is_derived = False
- self.virtual_method_line_number = None
- self.has_virtual_destructor = False
- self.brace_depth = 0
-
-
-class _ClassState(object):
- """Holds the current state of the parse relating to class declarations.
-
- It maintains a stack of _ClassInfos representing the parser's guess
- as to the current nesting of class declarations. The innermost class
- is at the top (back) of the stack. Typically, the stack will either
- be empty or have exactly one entry.
- """
-
- def __init__(self):
- self.classinfo_stack = []
-
- def check_finished(self, error):
- """Checks that all classes have been completely parsed.
-
- Call this when all lines in a file have been processed.
- Args:
- error: The function to call with any errors found.
- """
- if self.classinfo_stack:
- # Note: This test can result in false positives if #ifdef constructs
- # get in the way of brace matching. See the testBuildClass test in
- # cpp_style_unittest.py for an example of this.
- error(self.classinfo_stack[0].line_number, 'build/class', 5,
- 'Failed to find complete declaration of class %s' %
- self.classinfo_stack[0].name)
-
-
-class _FileState(object):
- def __init__(self):
- self._did_inside_namespace_indent_warning = False
-
- def set_did_inside_namespace_indent_warning(self):
- self._did_inside_namespace_indent_warning = True
-
- def did_inside_namespace_indent_warning(self):
- return self._did_inside_namespace_indent_warning
-
-def check_for_non_standard_constructs(clean_lines, line_number,
- class_state, error):
- """Logs an error if we see certain non-ANSI constructs ignored by gcc-2.
-
- Complain about several constructs which gcc-2 accepts, but which are
- not standard C++. Warning about these in lint is one way to ease the
- transition to new compilers.
- - put storage class first (e.g. "static const" instead of "const static").
- - "%lld" instead of %qd" in printf-type functions.
- - "%1$d" is non-standard in printf-type functions.
- - "\%" is an undefined character escape sequence.
- - text after #endif is not allowed.
- - invalid inner-style forward declaration.
- - >? and <? operators, and their >?= and <?= cousins.
- - classes with virtual methods need virtual destructors (compiler warning
- available, but not turned on yet.)
-
- Additionally, check for constructor/destructor style violations as it
- is very convenient to do so while checking for gcc-2 compliance.
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- class_state: A _ClassState instance which maintains information about
- the current stack of nested class declarations being parsed.
- error: A callable to which errors are reported, which takes parameters:
- line number, error level, and message
- """
-
- # Remove comments from the line, but leave in strings for now.
- line = clean_lines.lines[line_number]
-
- if search(r'printf\s*\(.*".*%[-+ ]?\d*q', line):
- error(line_number, 'runtime/printf_format', 3,
- '%q in format strings is deprecated. Use %ll instead.')
-
- if search(r'printf\s*\(.*".*%\d+\$', line):
- error(line_number, 'runtime/printf_format', 2,
- '%N$ formats are unconventional. Try rewriting to avoid them.')
-
- # Remove escaped backslashes before looking for undefined escapes.
- line = line.replace('\\\\', '')
-
- if search(r'("|\').*\\(%|\[|\(|{)', line):
- error(line_number, 'build/printf_format', 3,
- '%, [, (, and { are undefined character escapes. Unescape them.')
-
- # For the rest, work with both comments and strings removed.
- line = clean_lines.elided[line_number]
-
- if search(r'\b(const|volatile|void|char|short|int|long'
- r'|float|double|signed|unsigned'
- r'|schar|u?int8|u?int16|u?int32|u?int64)'
- r'\s+(auto|register|static|extern|typedef)\b',
- line):
- error(line_number, 'build/storage_class', 5,
- 'Storage class (static, extern, typedef, etc) should be first.')
-
- if match(r'\s*#\s*endif\s*[^/\s]+', line):
- error(line_number, 'build/endif_comment', 5,
- 'Uncommented text after #endif is non-standard. Use a comment.')
-
- if match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line):
- error(line_number, 'build/forward_decl', 5,
- 'Inner-style forward declarations are invalid. Remove this line.')
-
- if search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', line):
- error(line_number, 'build/deprecated', 3,
- '>? and <? (max and min) operators are non-standard and deprecated.')
-
- # Track class entry and exit, and attempt to find cases within the
- # class declaration that don't meet the C++ style
- # guidelines. Tracking is very dependent on the code matching Google
- # style guidelines, but it seems to perform well enough in testing
- # to be a worthwhile addition to the checks.
- classinfo_stack = class_state.classinfo_stack
- # Look for a class declaration
- class_decl_match = match(
- r'\s*(template\s*<[\w\s<>,:]*>\s*)?(class|struct)\s+(\w+(::\w+)*)', line)
- if class_decl_match:
- classinfo_stack.append(_ClassInfo(class_decl_match.group(3), line_number))
-
- # Everything else in this function uses the top of the stack if it's
- # not empty.
- if not classinfo_stack:
- return
-
- classinfo = classinfo_stack[-1]
-
- # If the opening brace hasn't been seen look for it and also
- # parent class declarations.
- if not classinfo.seen_open_brace:
- # If the line has a ';' in it, assume it's a forward declaration or
- # a single-line class declaration, which we won't process.
- if line.find(';') != -1:
- classinfo_stack.pop()
- return
- classinfo.seen_open_brace = (line.find('{') != -1)
- # Look for a bare ':'
- if search('(^|[^:]):($|[^:])', line):
- classinfo.is_derived = True
- if not classinfo.seen_open_brace:
- return # Everything else in this function is for after open brace
-
- # The class may have been declared with namespace or classname qualifiers.
- # The constructor and destructor will not have those qualifiers.
- base_classname = classinfo.name.split('::')[-1]
-
- # Look for single-argument constructors that aren't marked explicit.
- # Technically a valid construct, but against style.
- args = match(r'(?<!explicit)\s+%s\s*\(([^,()]+)\)'
- % re.escape(base_classname),
- line)
- if (args
- and args.group(1) != 'void'
- and not match(r'(const\s+)?%s\s*&' % re.escape(base_classname),
- args.group(1).strip())):
- error(line_number, 'runtime/explicit', 5,
- 'Single-argument constructors should be marked explicit.')
-
- # Look for methods declared virtual.
- if search(r'\bvirtual\b', line):
- classinfo.virtual_method_line_number = line_number
- # Only look for a destructor declaration on the same line. It would
- # be extremely unlikely for the destructor declaration to occupy
- # more than one line.
- if search(r'~%s\s*\(' % base_classname, line):
- classinfo.has_virtual_destructor = True
-
- # Look for class end.
- brace_depth = classinfo.brace_depth
- brace_depth = brace_depth + line.count('{') - line.count('}')
- if brace_depth <= 0:
- classinfo = classinfo_stack.pop()
- # Try to detect missing virtual destructor declarations.
- # For now, only warn if a non-derived class with virtual methods lacks
- # a virtual destructor. This is to make it less likely that people will
- # declare derived virtual destructors without declaring the base
- # destructor virtual.
- if ((classinfo.virtual_method_line_number is not None)
- and (not classinfo.has_virtual_destructor)
- and (not classinfo.is_derived)): # Only warn for base classes
- error(classinfo.line_number, 'runtime/virtual', 4,
- 'The class %s probably needs a virtual destructor due to '
- 'having virtual method(s), one declared at line %d.'
- % (classinfo.name, classinfo.virtual_method_line_number))
- else:
- classinfo.brace_depth = brace_depth
-
-
-def check_spacing_for_function_call(line, line_number, error):
- """Checks for the correctness of various spacing around function calls.
-
- Args:
- line: The text of the line to check.
- line_number: The number of the line to check.
- error: The function to call with any errors found.
- """
-
- # Since function calls often occur inside if/for/foreach/while/switch
- # expressions - which have their own, more liberal conventions - we
- # first see if we should be looking inside such an expression for a
- # function call, to which we can apply more strict standards.
- function_call = line # if there's no control flow construct, look at whole line
- for pattern in (r'\bif\s*\((.*)\)\s*{',
- r'\bfor\s*\((.*)\)\s*{',
- r'\bforeach\s*\((.*)\)\s*{',
- r'\bwhile\s*\((.*)\)\s*[{;]',
- r'\bswitch\s*\((.*)\)\s*{'):
- matched = search(pattern, line)
- if matched:
- function_call = matched.group(1) # look inside the parens for function calls
- break
-
- # Except in if/for/foreach/while/switch, there should never be space
- # immediately inside parens (eg "f( 3, 4 )"). We make an exception
- # for nested parens ( (a+b) + c ). Likewise, there should never be
- # a space before a ( when it's a function argument. I assume it's a
- # function argument when the char before the whitespace is legal in
- # a function name (alnum + _) and we're not starting a macro. Also ignore
- # pointers and references to arrays and functions coz they're too tricky:
- # we use a very simple way to recognize these:
- # " (something)(maybe-something)" or
- # " (something)(maybe-something," or
- # " (something)[something]"
- # Note that we assume the contents of [] to be short enough that
- # they'll never need to wrap.
- if ( # Ignore control structures.
- not search(r'\b(if|for|foreach|while|switch|return|new|delete)\b', function_call)
- # Ignore pointers/references to functions.
- and not search(r' \([^)]+\)\([^)]*(\)|,$)', function_call)
- # Ignore pointers/references to arrays.
- and not search(r' \([^)]+\)\[[^\]]+\]', function_call)):
- if search(r'\w\s*\([ \t](?!\s*\\$)', function_call): # a ( used for a fn call
- error(line_number, 'whitespace/parens', 4,
- 'Extra space after ( in function call')
- elif search(r'\([ \t]+(?!(\s*\\)|\()', function_call):
- error(line_number, 'whitespace/parens', 2,
- 'Extra space after (')
- if (search(r'\w\s+\(', function_call)
- and not search(r'#\s*define|typedef', function_call)):
- error(line_number, 'whitespace/parens', 4,
- 'Extra space before ( in function call')
- # If the ) is followed only by a newline or a { + newline, assume it's
- # part of a control statement (if/while/etc), and don't complain
- if search(r'[^)\s]\s+\)(?!\s*$|{\s*$)', function_call):
- error(line_number, 'whitespace/parens', 2,
- 'Extra space before )')
-
-
-def is_blank_line(line):
- """Returns true if the given line is blank.
-
- We consider a line to be blank if the line is empty or consists of
- only white spaces.
-
- Args:
- line: A line of a string.
-
- Returns:
- True, if the given line is blank.
- """
- return not line or line.isspace()
-
-
-def detect_functions(clean_lines, line_number, function_state, error):
- """Finds where functions start and end.
-
- Uses a simplistic algorithm assuming other style guidelines
- (especially spacing) are followed.
- Trivial bodies are unchecked, so constructors with huge initializer lists
- may be missed.
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- function_state: Current function name and lines in body so far.
- error: The function to call with any errors found.
- """
- # Are we now past the end of a function?
- if function_state.ending_line_number + 1 == line_number:
- function_state.end()
-
- # If we're in a function, don't try to detect a new one.
- if function_state.in_a_function:
- return
-
- lines = clean_lines.lines
- line = lines[line_number]
- raw = clean_lines.raw_lines
- raw_line = raw[line_number]
-
- regexp = r'\s*(\w(\w|::|\*|\&|\s|<|>|,|~)*)\(' # decls * & space::name( ...
- match_result = match(regexp, line)
- if not match_result:
- return
-
- # If the name is all caps and underscores, figure it's a macro and
- # ignore it, unless it's TEST or TEST_F.
- function_name = match_result.group(1).split()[-1]
- if function_name != 'TEST' and function_name != 'TEST_F' and match(r'[A-Z_]+$', function_name):
- return
-
- joined_line = ''
- for start_line_number in xrange(line_number, clean_lines.num_lines()):
- start_line = lines[start_line_number]
- joined_line += ' ' + start_line.lstrip()
- if search(r'(;|})', start_line): # Declarations and trivial functions
- return # ... ignore
-
- if search(r'{', start_line):
- # Replace template constructs with _ so that no spaces remain in the function name,
- # while keeping the column numbers of other characters the same as "line".
- line_with_no_templates = iteratively_replace_matches_with_char(r'<[^<>]*>', '_', line)
- match_function = search(r'((\w|:|<|>|,|~)*)\(', line_with_no_templates)
- if not match_function:
- return # The '(' must have been inside of a template.
-
- # Use the column numbers from the modified line to find the
- # function name in the original line.
- function = line[match_function.start(1):match_function.end(1)]
-
- if match(r'TEST', function): # Handle TEST... macros
- parameter_regexp = search(r'(\(.*\))', joined_line)
- if parameter_regexp: # Ignore bad syntax
- function += parameter_regexp.group(1)
- else:
- function += '()'
- open_brace_index = start_line.find('{')
- ending_line_number = close_expression(clean_lines, start_line_number, open_brace_index)[1]
- function_state.begin(function, start_line_number + 1, ending_line_number)
- return
-
- # No body for the function (or evidence of a non-function) was found.
- error(line_number, 'readability/fn_size', 5,
- 'Lint failed to find start of function body.')
-
-
-def check_for_function_lengths(clean_lines, line_number, function_state, error):
- """Reports for long function bodies.
-
- For an overview why this is done, see:
- http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions
-
- Blank/comment lines are not counted so as to avoid encouraging the removal
- of vertical space and commments just to get through a lint check.
- NOLINT *on the last line of a function* disables this check.
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- function_state: Current function name and lines in body so far.
- error: The function to call with any errors found.
- """
- lines = clean_lines.lines
- line = lines[line_number]
- raw = clean_lines.raw_lines
- raw_line = raw[line_number]
-
- if function_state.ending_line_number == line_number: # last line
- if not search(r'\bNOLINT\b', raw_line):
- function_state.check(error, line_number)
- elif not match(r'^\s*$', line):
- function_state.count(line_number) # Count non-blank/non-comment lines.
-
-
-def check_pass_ptr_usage(clean_lines, line_number, function_state, error):
- """Check for proper usage of Pass*Ptr.
-
- Currently this is limited to detecting declarations of Pass*Ptr
- variables inside of functions.
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- function_state: Current function name and lines in body so far.
- error: The function to call with any errors found.
- """
- if not function_state.in_a_function:
- return
-
- lines = clean_lines.lines
- line = lines[line_number]
- if line_number >= function_state.body_start_line_number:
- matched_pass_ptr = match(r'^\s*Pass([A-Z][A-Za-z]*)Ptr<', line)
- if matched_pass_ptr:
- type_name = 'Pass%sPtr' % matched_pass_ptr.group(1)
- error(line_number, 'readability/pass_ptr', 5,
- 'Local variables should never be %s (see '
- 'http://webkit.org/coding/RefPtr.html).' % type_name)
-
-
-def check_spacing(file_extension, clean_lines, line_number, error):
- """Checks for the correctness of various spacing issues in the code.
-
- Things we check for: spaces around operators, spaces after
- if/for/while/switch, no spaces around parens in function calls, two
- spaces between code and comment, don't start a block with a blank
- line, don't end a function with a blank line, don't have too many
- blank lines in a row.
-
- Args:
- file_extension: The current file extension, without the leading dot.
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- error: The function to call with any errors found.
- """
-
- raw = clean_lines.raw_lines
- line = raw[line_number]
-
- # Before nixing comments, check if the line is blank for no good
- # reason. This includes the first line after a block is opened, and
- # blank lines at the end of a function (ie, right before a line like '}').
- if is_blank_line(line):
- elided = clean_lines.elided
- previous_line = elided[line_number - 1]
- previous_brace = previous_line.rfind('{')
- # FIXME: Don't complain if line before blank line, and line after,
- # both start with alnums and are indented the same amount.
- # This ignores whitespace at the start of a namespace block
- # because those are not usually indented.
- if (previous_brace != -1 and previous_line[previous_brace:].find('}') == -1
- and previous_line[:previous_brace].find('namespace') == -1):
- # OK, we have a blank line at the start of a code block. Before we
- # complain, we check if it is an exception to the rule: The previous
- # non-empty line has the parameters of a function header that are indented
- # 4 spaces (because they did not fit in a 80 column line when placed on
- # the same line as the function name). We also check for the case where
- # the previous line is indented 6 spaces, which may happen when the
- # initializers of a constructor do not fit into a 80 column line.
- exception = False
- if match(r' {6}\w', previous_line): # Initializer list?
- # We are looking for the opening column of initializer list, which
- # should be indented 4 spaces to cause 6 space indentation afterwards.
- search_position = line_number - 2
- while (search_position >= 0
- and match(r' {6}\w', elided[search_position])):
- search_position -= 1
- exception = (search_position >= 0
- and elided[search_position][:5] == ' :')
- else:
- # Search for the function arguments or an initializer list. We use a
- # simple heuristic here: If the line is indented 4 spaces; and we have a
- # closing paren, without the opening paren, followed by an opening brace
- # or colon (for initializer lists) we assume that it is the last line of
- # a function header. If we have a colon indented 4 spaces, it is an
- # initializer list.
- exception = (match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)',
- previous_line)
- or match(r' {4}:', previous_line))
-
- if not exception:
- error(line_number, 'whitespace/blank_line', 2,
- 'Blank line at the start of a code block. Is this needed?')
- # This doesn't ignore whitespace at the end of a namespace block
- # because that is too hard without pairing open/close braces;
- # however, a special exception is made for namespace closing
- # brackets which have a comment containing "namespace".
- #
- # Also, ignore blank lines at the end of a block in a long if-else
- # chain, like this:
- # if (condition1) {
- # // Something followed by a blank line
- #
- # } else if (condition2) {
- # // Something else
- # }
- if line_number + 1 < clean_lines.num_lines():
- next_line = raw[line_number + 1]
- if (next_line
- and match(r'\s*}', next_line)
- and next_line.find('namespace') == -1
- and next_line.find('} else ') == -1):
- error(line_number, 'whitespace/blank_line', 3,
- 'Blank line at the end of a code block. Is this needed?')
-
- # Next, we complain if there's a comment too near the text
- comment_position = line.find('//')
- if comment_position != -1:
- # Check if the // may be in quotes. If so, ignore it
- # Comparisons made explicit for clarity -- pylint: disable-msg=C6403
- if (line.count('"', 0, comment_position) - line.count('\\"', 0, comment_position)) % 2 == 0: # not in quotes
- # Allow one space before end of line comment.
- if (not match(r'^\s*$', line[:comment_position])
- and (comment_position >= 1
- and ((line[comment_position - 1] not in string.whitespace)
- or (comment_position >= 2
- and line[comment_position - 2] in string.whitespace)))):
- error(line_number, 'whitespace/comments', 5,
- 'One space before end of line comments')
- # There should always be a space between the // and the comment
- commentend = comment_position + 2
- if commentend < len(line) and not line[commentend] == ' ':
- # but some lines are exceptions -- e.g. if they're big
- # comment delimiters like:
- # //----------------------------------------------------------
- # or they begin with multiple slashes followed by a space:
- # //////// Header comment
- matched = (search(r'[=/-]{4,}\s*$', line[commentend:])
- or search(r'^/+ ', line[commentend:]))
- if not matched:
- error(line_number, 'whitespace/comments', 4,
- 'Should have a space between // and comment')
-
- line = clean_lines.elided[line_number] # get rid of comments and strings
-
- # Don't try to do spacing checks for operator methods
- line = sub(r'operator(==|!=|<|<<|<=|>=|>>|>|\+=|-=|\*=|/=|%=|&=|\|=|^=|<<=|>>=)\(', 'operator\(', line)
- # Don't try to do spacing checks for #include or #import statements at
- # minimum because it messes up checks for spacing around /
- if match(r'\s*#\s*(?:include|import)', line):
- return
- if search(r'[\w.]=[\w.]', line):
- error(line_number, 'whitespace/operators', 4,
- 'Missing spaces around =')
-
- # FIXME: It's not ok to have spaces around binary operators like .
-
- # You should always have whitespace around binary operators.
- # Alas, we can't test < or > because they're legitimately used sans spaces
- # (a->b, vector<int> a). The only time we can tell is a < with no >, and
- # only if it's not template params list spilling into the next line.
- matched = search(r'[^<>=!\s](==|!=|\+=|-=|\*=|/=|/|\|=|&=|<<=|>>=|<=|>=|\|\||\||&&|>>|<<)[^<>=!\s]', line)
- if not matched:
- # Note that while it seems that the '<[^<]*' term in the following
- # regexp could be simplified to '<.*', which would indeed match
- # the same class of strings, the [^<] means that searching for the
- # regexp takes linear rather than quadratic time.
- if not search(r'<[^<]*,\s*$', line): # template params spill
- matched = search(r'[^<>=!\s](<)[^<>=!\s]([^>]|->)*$', line)
- if matched:
- error(line_number, 'whitespace/operators', 3,
- 'Missing spaces around %s' % matched.group(1))
-
- # There shouldn't be space around unary operators
- matched = search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line)
- if matched:
- error(line_number, 'whitespace/operators', 4,
- 'Extra space for operator %s' % matched.group(1))
-
- # A pet peeve of mine: no spaces after an if, while, switch, or for
- matched = search(r' (if\(|for\(|foreach\(|while\(|switch\()', line)
- if matched:
- error(line_number, 'whitespace/parens', 5,
- 'Missing space before ( in %s' % matched.group(1))
-
- # For if/for/foreach/while/switch, the left and right parens should be
- # consistent about how many spaces are inside the parens, and
- # there should either be zero or one spaces inside the parens.
- # We don't want: "if ( foo)" or "if ( foo )".
- # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed.
- matched = search(r'\b(?P<statement>if|for|foreach|while|switch)\s*\((?P<remainder>.*)$', line)
- if matched:
- statement = matched.group('statement')
- condition, rest = up_to_unmatched_closing_paren(matched.group('remainder'))
- if condition is not None:
- condition_match = search(r'(?P<leading>[ ]*)(?P<separator>.).*[^ ]+(?P<trailing>[ ]*)', condition)
- if condition_match:
- n_leading = len(condition_match.group('leading'))
- n_trailing = len(condition_match.group('trailing'))
- if n_leading != 0:
- for_exception = statement == 'for' and condition.startswith(' ;')
- if not for_exception:
- error(line_number, 'whitespace/parens', 5,
- 'Extra space after ( in %s' % statement)
- if n_trailing != 0:
- for_exception = statement == 'for' and condition.endswith('; ')
- if not for_exception:
- error(line_number, 'whitespace/parens', 5,
- 'Extra space before ) in %s' % statement)
-
- # Do not check for more than one command in macros
- in_macro = match(r'\s*#define', line)
- if not in_macro and not match(r'((\s*{\s*}?)|(\s*;?))\s*\\?$', rest):
- error(line_number, 'whitespace/parens', 4,
- 'More than one command on the same line in %s' % statement)
-
- # You should always have a space after a comma (either as fn arg or operator)
- if search(r',[^\s]', line):
- error(line_number, 'whitespace/comma', 3,
- 'Missing space after ,')
-
- matched = search(r'^\s*(?P<token1>[a-zA-Z0-9_\*&]+)\s\s+(?P<token2>[a-zA-Z0-9_\*&]+)', line)
- if matched:
- error(line_number, 'whitespace/declaration', 3,
- 'Extra space between %s and %s' % (matched.group('token1'), matched.group('token2')))
-
- if file_extension == 'cpp':
- # C++ should have the & or * beside the type not the variable name.
- matched = match(r'\s*\w+(?<!\breturn|\bdelete)\s+(?P<pointer_operator>\*|\&)\w+', line)
- if matched:
- error(line_number, 'whitespace/declaration', 3,
- 'Declaration has space between type name and %s in %s' % (matched.group('pointer_operator'), matched.group(0).strip()))
-
- elif file_extension == 'c':
- # C Pointer declaration should have the * beside the variable not the type name.
- matched = search(r'^\s*\w+\*\s+\w+', line)
- if matched:
- error(line_number, 'whitespace/declaration', 3,
- 'Declaration has space between * and variable name in %s' % matched.group(0).strip())
-
- # Next we will look for issues with function calls.
- check_spacing_for_function_call(line, line_number, error)
-
- # Except after an opening paren, you should have spaces before your braces.
- # And since you should never have braces at the beginning of a line, this is
- # an easy test.
- if search(r'[^ ({]{', line):
- error(line_number, 'whitespace/braces', 5,
- 'Missing space before {')
-
- # Make sure '} else {' has spaces.
- if search(r'}else', line):
- error(line_number, 'whitespace/braces', 5,
- 'Missing space before else')
-
- # You shouldn't have spaces before your brackets, except maybe after
- # 'delete []' or 'new char * []'.
- if search(r'\w\s+\[', line) and not search(r'delete\s+\[', line):
- error(line_number, 'whitespace/braces', 5,
- 'Extra space before [')
-
- # You shouldn't have a space before a semicolon at the end of the line.
- # There's a special case for "for" since the style guide allows space before
- # the semicolon there.
- if search(r':\s*;\s*$', line):
- error(line_number, 'whitespace/semicolon', 5,
- 'Semicolon defining empty statement. Use { } instead.')
- elif search(r'^\s*;\s*$', line):
- error(line_number, 'whitespace/semicolon', 5,
- 'Line contains only semicolon. If this should be an empty statement, '
- 'use { } instead.')
- elif (search(r'\s+;\s*$', line) and not search(r'\bfor\b', line)):
- error(line_number, 'whitespace/semicolon', 5,
- 'Extra space before last semicolon. If this should be an empty '
- 'statement, use { } instead.')
- elif (search(r'\b(for|while)\s*\(.*\)\s*;\s*$', line)
- and line.count('(') == line.count(')')
- # Allow do {} while();
- and not search(r'}\s*while', line)):
- error(line_number, 'whitespace/semicolon', 5,
- 'Semicolon defining empty statement for this loop. Use { } instead.')
-
-
-def get_previous_non_blank_line(clean_lines, line_number):
- """Return the most recent non-blank line and its line number.
-
- Args:
- clean_lines: A CleansedLines instance containing the file contents.
- line_number: The number of the line to check.
-
- Returns:
- A tuple with two elements. The first element is the contents of the last
- non-blank line before the current line, or the empty string if this is the
- first non-blank line. The second is the line number of that line, or -1
- if this is the first non-blank line.
- """
-
- previous_line_number = line_number - 1
- while previous_line_number >= 0:
- previous_line = clean_lines.elided[previous_line_number]
- if not is_blank_line(previous_line): # if not a blank line...
- return (previous_line, previous_line_number)
- previous_line_number -= 1
- return ('', -1)
-
-
-def check_namespace_indentation(clean_lines, line_number, file_extension, file_state, error):
- """Looks for indentation errors inside of namespaces.
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- file_extension: The extension (dot not included) of the file.
- file_state: A _FileState instance which maintains information about
- the state of things in the file.
- error: The function to call with any errors found.
- """
-
- line = clean_lines.elided[line_number] # Get rid of comments and strings.
-
- namespace_match = match(r'(?P<namespace_indentation>\s*)namespace\s+\S+\s*{\s*$', line)
- if not namespace_match:
- return
-
- current_indentation_level = len(namespace_match.group('namespace_indentation'))
- if current_indentation_level > 0:
- # Don't warn about an indented namespace if we already warned about indented code.
- if not file_state.did_inside_namespace_indent_warning():
- error(line_number, 'whitespace/indent', 4,
- 'namespace should never be indented.')
- return
- looking_for_semicolon = False;
- line_offset = 0
- in_preprocessor_directive = False;
- for current_line in clean_lines.elided[line_number + 1:]:
- line_offset += 1
- if not current_line.strip():
- continue
- if not current_indentation_level:
- if not (in_preprocessor_directive or looking_for_semicolon):
- if not match(r'\S', current_line) and not file_state.did_inside_namespace_indent_warning():
- file_state.set_did_inside_namespace_indent_warning()
- error(line_number + line_offset, 'whitespace/indent', 4,
- 'Code inside a namespace should not be indented.')
- if in_preprocessor_directive or (current_line.strip()[0] == '#'): # This takes care of preprocessor directive syntax.
- in_preprocessor_directive = current_line[-1] == '\\'
- else:
- looking_for_semicolon = ((current_line.find(';') == -1) and (current_line.strip()[-1] != '}')) or (current_line[-1] == '\\')
- else:
- looking_for_semicolon = False; # If we have a brace we may not need a semicolon.
- current_indentation_level += current_line.count('{') - current_line.count('}')
- if current_indentation_level < 0:
- break;
-
-def check_using_std(file_extension, clean_lines, line_number, error):
- """Looks for 'using std::foo;' statements which should be replaced with 'using namespace std;'.
-
- Args:
- file_extension: The extension of the current file, without the leading dot.
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- error: The function to call with any errors found.
- """
-
- # This check doesn't apply to C or Objective-C implementation files.
- if is_c_or_objective_c(file_extension):
- return
-
- line = clean_lines.elided[line_number] # Get rid of comments and strings.
-
- using_std_match = match(r'\s*using\s+std::(?P<method_name>\S+)\s*;\s*$', line)
- if not using_std_match:
- return
-
- method_name = using_std_match.group('method_name')
- error(line_number, 'build/using_std', 4,
- "Use 'using namespace std;' instead of 'using std::%s;'." % method_name)
-
-
-def check_max_min_macros(file_extension, clean_lines, line_number, error):
- """Looks use of MAX() and MIN() macros that should be replaced with std::max() and std::min().
-
- Args:
- file_extension: The extension of the current file, without the leading dot.
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- error: The function to call with any errors found.
- """
-
- # This check doesn't apply to C or Objective-C implementation files.
- if is_c_or_objective_c(file_extension):
- return
-
- line = clean_lines.elided[line_number] # Get rid of comments and strings.
-
- max_min_macros_search = search(r'\b(?P<max_min_macro>(MAX|MIN))\s*\(', line)
- if not max_min_macros_search:
- return
-
- max_min_macro = max_min_macros_search.group('max_min_macro')
- max_min_macro_lower = max_min_macro.lower()
- error(line_number, 'runtime/max_min_macros', 4,
- 'Use std::%s() or std::%s<type>() instead of the %s() macro.'
- % (max_min_macro_lower, max_min_macro_lower, max_min_macro))
-
-
-def check_switch_indentation(clean_lines, line_number, error):
- """Looks for indentation errors inside of switch statements.
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- error: The function to call with any errors found.
- """
-
- line = clean_lines.elided[line_number] # Get rid of comments and strings.
-
- switch_match = match(r'(?P<switch_indentation>\s*)switch\s*\(.+\)\s*{\s*$', line)
- if not switch_match:
- return
-
- switch_indentation = switch_match.group('switch_indentation')
- inner_indentation = switch_indentation + ' ' * 4
- line_offset = 0
- encountered_nested_switch = False
-
- for current_line in clean_lines.elided[line_number + 1:]:
- line_offset += 1
-
- # Skip not only empty lines but also those with preprocessor directives.
- if current_line.strip() == '' or current_line.startswith('#'):
- continue
-
- if match(r'\s*switch\s*\(.+\)\s*{\s*$', current_line):
- # Complexity alarm - another switch statement nested inside the one
- # that we're currently testing. We'll need to track the extent of
- # that inner switch if the upcoming label tests are still supposed
- # to work correctly. Let's not do that; instead, we'll finish
- # checking this line, and then leave it like that. Assuming the
- # indentation is done consistently (even if incorrectly), this will
- # still catch all indentation issues in practice.
- encountered_nested_switch = True
-
- current_indentation_match = match(r'(?P<indentation>\s*)(?P<remaining_line>.*)$', current_line);
- current_indentation = current_indentation_match.group('indentation')
- remaining_line = current_indentation_match.group('remaining_line')
-
- # End the check at the end of the switch statement.
- if remaining_line.startswith('}') and current_indentation == switch_indentation:
- break
- # Case and default branches should not be indented. The regexp also
- # catches single-line cases like "default: break;" but does not trigger
- # on stuff like "Document::Foo();".
- elif match(r'(default|case\s+.*)\s*:([^:].*)?$', remaining_line):
- if current_indentation != switch_indentation:
- error(line_number + line_offset, 'whitespace/indent', 4,
- 'A case label should not be indented, but line up with its switch statement.')
- # Don't throw an error for multiple badly indented labels,
- # one should be enough to figure out the problem.
- break
- # We ignore goto labels at the very beginning of a line.
- elif match(r'\w+\s*:\s*$', remaining_line):
- continue
- # It's not a goto label, so check if it's indented at least as far as
- # the switch statement plus one more level of indentation.
- elif not current_indentation.startswith(inner_indentation):
- error(line_number + line_offset, 'whitespace/indent', 4,
- 'Non-label code inside switch statements should be indented.')
- # Don't throw an error for multiple badly indented statements,
- # one should be enough to figure out the problem.
- break
-
- if encountered_nested_switch:
- break
-
-
-def check_braces(clean_lines, line_number, error):
- """Looks for misplaced braces (e.g. at the end of line).
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- error: The function to call with any errors found.
- """
-
- line = clean_lines.elided[line_number] # Get rid of comments and strings.
-
- if match(r'\s*{\s*$', line):
- # We allow an open brace to start a line in the case where someone
- # is using braces for function definition or in a block to
- # explicitly create a new scope, which is commonly used to control
- # the lifetime of stack-allocated variables. We don't detect this
- # perfectly: we just don't complain if the last non-whitespace
- # character on the previous non-blank line is ';', ':', '{', '}',
- # ')', or ') const' and doesn't begin with 'if|for|while|switch|else'.
- # We also allow '#' for #endif and '=' for array initialization.
- previous_line = get_previous_non_blank_line(clean_lines, line_number)[0]
- if ((not search(r'[;:}{)=]\s*$|\)\s*const\s*$', previous_line)
- or search(r'\b(if|for|foreach|while|switch|else)\b', previous_line))
- and previous_line.find('#') < 0):
- error(line_number, 'whitespace/braces', 4,
- 'This { should be at the end of the previous line')
- elif (search(r'\)\s*(const\s*)?{\s*$', line)
- and line.count('(') == line.count(')')
- and not search(r'\b(if|for|foreach|while|switch)\b', line)
- and not match(r'\s+[A-Z_][A-Z_0-9]+\b', line)):
- error(line_number, 'whitespace/braces', 4,
- 'Place brace on its own line for function definitions.')
-
- if (match(r'\s*}\s*(else\s*({\s*)?)?$', line) and line_number > 1):
- # We check if a closed brace has started a line to see if a
- # one line control statement was previous.
- previous_line = clean_lines.elided[line_number - 2]
- if (previous_line.find('{') > 0 and previous_line.find('}') < 0
- and search(r'\b(if|for|foreach|while|else)\b', previous_line)):
- error(line_number, 'whitespace/braces', 4,
- 'One line control clauses should not use braces.')
-
- # An else clause should be on the same line as the preceding closing brace.
- if match(r'\s*else\s*', line):
- previous_line = get_previous_non_blank_line(clean_lines, line_number)[0]
- if match(r'\s*}\s*$', previous_line):
- error(line_number, 'whitespace/newline', 4,
- 'An else should appear on the same line as the preceding }')
-
- # Likewise, an else should never have the else clause on the same line
- if search(r'\belse [^\s{]', line) and not search(r'\belse if\b', line):
- error(line_number, 'whitespace/newline', 4,
- 'Else clause should never be on same line as else (use 2 lines)')
-
- # In the same way, a do/while should never be on one line
- if match(r'\s*do [^\s{]', line):
- error(line_number, 'whitespace/newline', 4,
- 'do/while clauses should not be on a single line')
-
- # Braces shouldn't be followed by a ; unless they're defining a struct
- # or initializing an array.
- # We can't tell in general, but we can for some common cases.
- previous_line_number = line_number
- while True:
- (previous_line, previous_line_number) = get_previous_non_blank_line(clean_lines, previous_line_number)
- if match(r'\s+{.*}\s*;', line) and not previous_line.count(';'):
- line = previous_line + line
- else:
- break
- if (search(r'{.*}\s*;', line)
- and line.count('{') == line.count('}')
- and not search(r'struct|class|enum|\s*=\s*{', line)):
- error(line_number, 'readability/braces', 4,
- "You don't need a ; after a }")
-
-
-def check_exit_statement_simplifications(clean_lines, line_number, error):
- """Looks for else or else-if statements that should be written as an
- if statement when the prior if concludes with a return, break, continue or
- goto statement.
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- error: The function to call with any errors found.
- """
-
- line = clean_lines.elided[line_number] # Get rid of comments and strings.
-
- else_match = match(r'(?P<else_indentation>\s*)(\}\s*)?else(\s+if\s*\(|(?P<else>\s*(\{\s*)?\Z))', line)
- if not else_match:
- return
-
- else_indentation = else_match.group('else_indentation')
- inner_indentation = else_indentation + ' ' * 4
-
- previous_lines = clean_lines.elided[:line_number]
- previous_lines.reverse()
- line_offset = 0
- encountered_exit_statement = False
-
- for current_line in previous_lines:
- line_offset -= 1
-
- # Skip not only empty lines but also those with preprocessor directives
- # and goto labels.
- if current_line.strip() == '' or current_line.startswith('#') or match(r'\w+\s*:\s*$', current_line):
- continue
-
- # Skip lines with closing braces on the original indentation level.
- # Even though the styleguide says they should be on the same line as
- # the "else if" statement, we also want to check for instances where
- # the current code does not comply with the coding style. Thus, ignore
- # these lines and proceed to the line before that.
- if current_line == else_indentation + '}':
- continue
-
- current_indentation_match = match(r'(?P<indentation>\s*)(?P<remaining_line>.*)$', current_line);
- current_indentation = current_indentation_match.group('indentation')
- remaining_line = current_indentation_match.group('remaining_line')
-
- # As we're going up the lines, the first real statement to encounter
- # has to be an exit statement (return, break, continue or goto) -
- # otherwise, this check doesn't apply.
- if not encountered_exit_statement:
- # We only want to find exit statements if they are on exactly
- # the same level of indentation as expected from the code inside
- # the block. If the indentation doesn't strictly match then we
- # might have a nested if or something, which must be ignored.
- if current_indentation != inner_indentation:
- break
- if match(r'(return(\W+.*)|(break|continue)\s*;|goto\s*\w+;)$', remaining_line):
- encountered_exit_statement = True
- continue
- break
-
- # When code execution reaches this point, we've found an exit statement
- # as last statement of the previous block. Now we only need to make
- # sure that the block belongs to an "if", then we can throw an error.
-
- # Skip lines with opening braces on the original indentation level,
- # similar to the closing braces check above. ("if (condition)\n{")
- if current_line == else_indentation + '{':
- continue
-
- # Skip everything that's further indented than our "else" or "else if".
- if current_indentation.startswith(else_indentation) and current_indentation != else_indentation:
- continue
-
- # So we've got a line with same (or less) indentation. Is it an "if"?
- # If yes: throw an error. If no: don't throw an error.
- # Whatever the outcome, this is the end of our loop.
- if match(r'if\s*\(', remaining_line):
- if else_match.start('else') != -1:
- error(line_number + line_offset, 'readability/control_flow', 4,
- 'An else statement can be removed when the prior "if" '
- 'concludes with a return, break, continue or goto statement.')
- else:
- error(line_number + line_offset, 'readability/control_flow', 4,
- 'An else if statement should be written as an if statement '
- 'when the prior "if" concludes with a return, break, '
- 'continue or goto statement.')
- break
-
-
-def replaceable_check(operator, macro, line):
- """Determine whether a basic CHECK can be replaced with a more specific one.
-
- For example suggest using CHECK_EQ instead of CHECK(a == b) and
- similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE.
-
- Args:
- operator: The C++ operator used in the CHECK.
- macro: The CHECK or EXPECT macro being called.
- line: The current source line.
-
- Returns:
- True if the CHECK can be replaced with a more specific one.
- """
-
- # This matches decimal and hex integers, strings, and chars (in that order).
- match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')'
-
- # Expression to match two sides of the operator with something that
- # looks like a literal, since CHECK(x == iterator) won't compile.
- # This means we can't catch all the cases where a more specific
- # CHECK is possible, but it's less annoying than dealing with
- # extraneous warnings.
- match_this = (r'\s*' + macro + r'\((\s*' +
- match_constant + r'\s*' + operator + r'[^<>].*|'
- r'.*[^<>]' + operator + r'\s*' + match_constant +
- r'\s*\))')
-
- # Don't complain about CHECK(x == NULL) or similar because
- # CHECK_EQ(x, NULL) won't compile (requires a cast).
- # Also, don't complain about more complex boolean expressions
- # involving && or || such as CHECK(a == b || c == d).
- return match(match_this, line) and not search(r'NULL|&&|\|\|', line)
-
-
-def check_check(clean_lines, line_number, error):
- """Checks the use of CHECK and EXPECT macros.
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- error: The function to call with any errors found.
- """
-
- # Decide the set of replacement macros that should be suggested
- raw_lines = clean_lines.raw_lines
- current_macro = ''
- for macro in _CHECK_MACROS:
- if raw_lines[line_number].find(macro) >= 0:
- current_macro = macro
- break
- if not current_macro:
- # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT'
- return
-
- line = clean_lines.elided[line_number] # get rid of comments and strings
-
- # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc.
- for operator in ['==', '!=', '>=', '>', '<=', '<']:
- if replaceable_check(operator, current_macro, line):
- error(line_number, 'readability/check', 2,
- 'Consider using %s instead of %s(a %s b)' % (
- _CHECK_REPLACEMENT[current_macro][operator],
- current_macro, operator))
- break
-
-
-def check_for_comparisons_to_zero(clean_lines, line_number, error):
- # Get the line without comments and strings.
- line = clean_lines.elided[line_number]
-
- # Include NULL here so that users don't have to convert NULL to 0 first and then get this error.
- if search(r'[=!]=\s*(NULL|0|true|false)\W', line) or search(r'\W(NULL|0|true|false)\s*[=!]=', line):
- error(line_number, 'readability/comparison_to_zero', 5,
- 'Tests for true/false, null/non-null, and zero/non-zero should all be done without equality comparisons.')
-
-
-def check_for_null(file_extension, clean_lines, line_number, error):
- # This check doesn't apply to C or Objective-C implementation files.
- if is_c_or_objective_c(file_extension):
- return
-
- line = clean_lines.elided[line_number]
-
- # Don't warn about NULL usage in g_*(). See Bug 32858 and 39372.
- if search(r'\bg(_[a-z]+)+\b', line):
- return
-
- # Don't warn about NULL usage in gst_*_many(). See Bug 39740
- if search(r'\bgst_\w+_many\b', line):
- return
-
- # Don't warn about NULL usage in g_str{join,concat}(). See Bug 34834
- if search(r'\bg_str(join|concat)\b', line):
- return
-
- # Don't warn about NULL usage in gdk_pixbuf_save_to_*{join,concat}(). See Bug 43090.
- if search(r'\bgdk_pixbuf_save_to\w+\b', line):
- return
-
- if search(r'\bNULL\b', line):
- error(line_number, 'readability/null', 5, 'Use 0 instead of NULL.')
- return
-
- line = clean_lines.raw_lines[line_number]
- # See if NULL occurs in any comments in the line. If the search for NULL using the raw line
- # matches, then do the check with strings collapsed to avoid giving errors for
- # NULLs occurring in strings.
- if search(r'\bNULL\b', line) and search(r'\bNULL\b', CleansedLines.collapse_strings(line)):
- error(line_number, 'readability/null', 4, 'Use 0 instead of NULL.')
-
-def get_line_width(line):
- """Determines the width of the line in column positions.
-
- Args:
- line: A string, which may be a Unicode string.
-
- Returns:
- The width of the line in column positions, accounting for Unicode
- combining characters and wide characters.
- """
- if isinstance(line, unicode):
- width = 0
- for c in unicodedata.normalize('NFC', line):
- if unicodedata.east_asian_width(c) in ('W', 'F'):
- width += 2
- elif not unicodedata.combining(c):
- width += 1
- return width
- return len(line)
-
-
-def check_style(clean_lines, line_number, file_extension, class_state, file_state, error):
- """Checks rules from the 'C++ style rules' section of cppguide.html.
-
- Most of these rules are hard to test (naming, comment style), but we
- do what we can. In particular we check for 4-space indents, line lengths,
- tab usage, spaces inside code, etc.
-
- Args:
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- file_extension: The extension (without the dot) of the filename.
- class_state: A _ClassState instance which maintains information about
- the current stack of nested class declarations being parsed.
- file_state: A _FileState instance which maintains information about
- the state of things in the file.
- error: The function to call with any errors found.
- """
-
- raw_lines = clean_lines.raw_lines
- line = raw_lines[line_number]
-
- if line.find('\t') != -1:
- error(line_number, 'whitespace/tab', 1,
- 'Tab found; better to use spaces')
-
- # One or three blank spaces at the beginning of the line is weird; it's
- # hard to reconcile that with 4-space indents.
- # NOTE: here are the conditions rob pike used for his tests. Mine aren't
- # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces
- # if(RLENGTH > 20) complain = 0;
- # if(match($0, " +(error|private|public|protected):")) complain = 0;
- # if(match(prev, "&& *$")) complain = 0;
- # if(match(prev, "\\|\\| *$")) complain = 0;
- # if(match(prev, "[\",=><] *$")) complain = 0;
- # if(match($0, " <<")) complain = 0;
- # if(match(prev, " +for \\(")) complain = 0;
- # if(prevodd && match(prevprev, " +for \\(")) complain = 0;
- initial_spaces = 0
- cleansed_line = clean_lines.elided[line_number]
- while initial_spaces < len(line) and line[initial_spaces] == ' ':
- initial_spaces += 1
- if line and line[-1].isspace():
- error(line_number, 'whitespace/end_of_line', 4,
- 'Line ends in whitespace. Consider deleting these extra spaces.')
- # There are certain situations we allow one space, notably for labels
- elif ((initial_spaces >= 1 and initial_spaces <= 3)
- and not match(r'\s*\w+\s*:\s*$', cleansed_line)):
- error(line_number, 'whitespace/indent', 3,
- 'Weird number of spaces at line-start. '
- 'Are you using a 4-space indent?')
- # Labels should always be indented at least one space.
- elif not initial_spaces and line[:2] != '//':
- label_match = match(r'(?P<label>[^:]+):\s*$', line)
-
- if label_match:
- label = label_match.group('label')
- # Only throw errors for stuff that is definitely not a goto label,
- # because goto labels can in fact occur at the start of the line.
- if label in ['public', 'private', 'protected'] or label.find(' ') != -1:
- error(line_number, 'whitespace/labels', 4,
- 'Labels should always be indented at least one space. '
- 'If this is a member-initializer list in a constructor, '
- 'the colon should be on the line after the definition header.')
-
- if (cleansed_line.count(';') > 1
- # for loops are allowed two ;'s (and may run over two lines).
- and cleansed_line.find('for') == -1
- and (get_previous_non_blank_line(clean_lines, line_number)[0].find('for') == -1
- or get_previous_non_blank_line(clean_lines, line_number)[0].find(';') != -1)
- # It's ok to have many commands in a switch case that fits in 1 line
- and not ((cleansed_line.find('case ') != -1
- or cleansed_line.find('default:') != -1)
- and cleansed_line.find('break;') != -1)
- # Also it's ok to have many commands in trivial single-line accessors in class definitions.
- and not (match(r'.*\(.*\).*{.*.}', line)
- and class_state.classinfo_stack
- and line.count('{') == line.count('}'))
- and not cleansed_line.startswith('#define ')):
- error(line_number, 'whitespace/newline', 4,
- 'More than one command on the same line')
-
- if cleansed_line.strip().endswith('||') or cleansed_line.strip().endswith('&&'):
- error(line_number, 'whitespace/operators', 4,
- 'Boolean expressions that span multiple lines should have their '
- 'operators on the left side of the line instead of the right side.')
-
- # Some more style checks
- check_namespace_indentation(clean_lines, line_number, file_extension, file_state, error)
- check_using_std(file_extension, clean_lines, line_number, error)
- check_max_min_macros(file_extension, clean_lines, line_number, error)
- check_switch_indentation(clean_lines, line_number, error)
- check_braces(clean_lines, line_number, error)
- check_exit_statement_simplifications(clean_lines, line_number, error)
- check_spacing(file_extension, clean_lines, line_number, error)
- check_check(clean_lines, line_number, error)
- check_for_comparisons_to_zero(clean_lines, line_number, error)
- check_for_null(file_extension, clean_lines, line_number, error)
-
-
-_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"')
-_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$')
-# Matches the first component of a filename delimited by -s and _s. That is:
-# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo'
-# _RE_FIRST_COMPONENT.match('foo.cpp').group(0) == 'foo'
-# _RE_FIRST_COMPONENT.match('foo-bar_baz.cpp').group(0) == 'foo'
-# _RE_FIRST_COMPONENT.match('foo_bar-baz.cpp').group(0) == 'foo'
-_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+')
-
-
-def _drop_common_suffixes(filename):
- """Drops common suffixes like _test.cpp or -inl.h from filename.
-
- For example:
- >>> _drop_common_suffixes('foo/foo-inl.h')
- 'foo/foo'
- >>> _drop_common_suffixes('foo/bar/foo.cpp')
- 'foo/bar/foo'
- >>> _drop_common_suffixes('foo/foo_internal.h')
- 'foo/foo'
- >>> _drop_common_suffixes('foo/foo_unusualinternal.h')
- 'foo/foo_unusualinternal'
-
- Args:
- filename: The input filename.
-
- Returns:
- The filename with the common suffix removed.
- """
- for suffix in ('test.cpp', 'regtest.cpp', 'unittest.cpp',
- 'inl.h', 'impl.h', 'internal.h'):
- if (filename.endswith(suffix) and len(filename) > len(suffix)
- and filename[-len(suffix) - 1] in ('-', '_')):
- return filename[:-len(suffix) - 1]
- return os.path.splitext(filename)[0]
-
-
-def _classify_include(filename, include, is_system, include_state):
- """Figures out what kind of header 'include' is.
-
- Args:
- filename: The current file cpp_style is running over.
- include: The path to a #included file.
- is_system: True if the #include used <> rather than "".
- include_state: An _IncludeState instance in which the headers are inserted.
-
- Returns:
- One of the _XXX_HEADER constants.
-
- For example:
- >>> _classify_include('foo.cpp', 'config.h', False)
- _CONFIG_HEADER
- >>> _classify_include('foo.cpp', 'foo.h', False)
- _PRIMARY_HEADER
- >>> _classify_include('foo.cpp', 'bar.h', False)
- _OTHER_HEADER
- """
-
- # If it is a system header we know it is classified as _OTHER_HEADER.
- if is_system:
- return _OTHER_HEADER
-
- # If the include is named config.h then this is WebCore/config.h.
- if include == "config.h":
- return _CONFIG_HEADER
-
- # There cannot be primary includes in header files themselves. Only an
- # include exactly matches the header filename will be is flagged as
- # primary, so that it triggers the "don't include yourself" check.
- if filename.endswith('.h') and filename != include:
- return _OTHER_HEADER;
-
- # Qt's moc files do not follow the naming and ordering rules, so they should be skipped
- if include.startswith('moc_') and include.endswith('.cpp'):
- return _MOC_HEADER
-
- if include.endswith('.moc'):
- return _MOC_HEADER
-
- # If the target file basename starts with the include we're checking
- # then we consider it the primary header.
- target_base = FileInfo(filename).base_name()
- include_base = FileInfo(include).base_name()
-
- # If we haven't encountered a primary header, then be lenient in checking.
- if not include_state.visited_primary_section() and target_base.find(include_base) != -1:
- return _PRIMARY_HEADER
- # If we already encountered a primary header, perform a strict comparison.
- # In case the two filename bases are the same then the above lenient check
- # probably was a false positive.
- elif include_state.visited_primary_section() and target_base == include_base:
- if include == "ResourceHandleWin.h":
- # FIXME: Thus far, we've only seen one example of these, but if we
- # start to see more, please consider generalizing this check
- # somehow.
- return _OTHER_HEADER
- return _PRIMARY_HEADER
-
- return _OTHER_HEADER
-
-
-def check_include_line(filename, file_extension, clean_lines, line_number, include_state, error):
- """Check rules that are applicable to #include lines.
-
- Strings on #include lines are NOT removed from elided line, to make
- certain tasks easier. However, to prevent false positives, checks
- applicable to #include lines in CheckLanguage must be put here.
-
- Args:
- filename: The name of the current file.
- file_extension: The current file extension, without the leading dot.
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- include_state: An _IncludeState instance in which the headers are inserted.
- error: The function to call with any errors found.
- """
- # FIXME: For readability or as a possible optimization, consider
- # exiting early here by checking whether the "build/include"
- # category should be checked for the given filename. This
- # may involve having the error handler classes expose a
- # should_check() method, in addition to the usual __call__
- # method.
- line = clean_lines.lines[line_number]
-
- matched = _RE_PATTERN_INCLUDE.search(line)
- if not matched:
- return
-
- include = matched.group(2)
- is_system = (matched.group(1) == '<')
-
- # Look for any of the stream classes that are part of standard C++.
- if match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include):
- error(line_number, 'readability/streams', 3,
- 'Streams are highly discouraged.')
-
- # Look for specific includes to fix.
- if include.startswith('wtf/') and not is_system:
- error(line_number, 'build/include', 4,
- 'wtf includes should be <wtf/file.h> instead of "wtf/file.h".')
-
- duplicate_header = include in include_state
- if duplicate_header:
- error(line_number, 'build/include', 4,
- '"%s" already included at %s:%s' %
- (include, filename, include_state[include]))
- else:
- include_state[include] = line_number
-
- header_type = _classify_include(filename, include, is_system, include_state)
- include_state.header_types[line_number] = header_type
-
- # Only proceed if this isn't a duplicate header.
- if duplicate_header:
- return
-
- # We want to ensure that headers appear in the right order:
- # 1) for implementation files: config.h, primary header, blank line, alphabetically sorted
- # 2) for header files: alphabetically sorted
- # The include_state object keeps track of the last type seen
- # and complains if the header types are out of order or missing.
- error_message = include_state.check_next_include_order(header_type, file_extension == "h")
-
- # Check to make sure we have a blank line after primary header.
- if not error_message and header_type == _PRIMARY_HEADER:
- next_line = clean_lines.raw_lines[line_number + 1]
- if not is_blank_line(next_line):
- error(line_number, 'build/include_order', 4,
- 'You should add a blank line after implementation file\'s own header.')
-
- # Check to make sure all headers besides config.h and the primary header are
- # alphabetically sorted. Skip Qt's moc files.
- if not error_message and header_type == _OTHER_HEADER:
- previous_line_number = line_number - 1;
- previous_line = clean_lines.lines[previous_line_number]
- previous_match = _RE_PATTERN_INCLUDE.search(previous_line)
- while (not previous_match and previous_line_number > 0
- and not search(r'\A(#if|#ifdef|#ifndef|#else|#elif|#endif)', previous_line)):
- previous_line_number -= 1;
- previous_line = clean_lines.lines[previous_line_number]
- previous_match = _RE_PATTERN_INCLUDE.search(previous_line)
- if previous_match:
- previous_header_type = include_state.header_types[previous_line_number]
- if previous_header_type == _OTHER_HEADER and previous_line.strip() > line.strip():
- error(line_number, 'build/include_order', 4,
- 'Alphabetical sorting problem.')
-
- if error_message:
- if file_extension == 'h':
- error(line_number, 'build/include_order', 4,
- '%s Should be: alphabetically sorted.' %
- error_message)
- else:
- error(line_number, 'build/include_order', 4,
- '%s Should be: config.h, primary header, blank line, and then alphabetically sorted.' %
- error_message)
-
-
-def check_language(filename, clean_lines, line_number, file_extension, include_state,
- error):
- """Checks rules from the 'C++ language rules' section of cppguide.html.
-
- Some of these rules are hard to test (function overloading, using
- uint32 inappropriately), but we do the best we can.
-
- Args:
- filename: The name of the current file.
- clean_lines: A CleansedLines instance containing the file.
- line_number: The number of the line to check.
- file_extension: The extension (without the dot) of the filename.
- include_state: An _IncludeState instance in which the headers are inserted.
- error: The function to call with any errors found.
- """
- # If the line is empty or consists of entirely a comment, no need to
- # check it.
- line = clean_lines.elided[line_number]
- if not line:
- return
-
- matched = _RE_PATTERN_INCLUDE.search(line)
- if matched:
- check_include_line(filename, file_extension, clean_lines, line_number, include_state, error)
- return
-
- # FIXME: figure out if they're using default arguments in fn proto.
-
- # Check to see if they're using an conversion function cast.
- # I just try to capture the most common basic types, though there are more.
- # Parameterless conversion functions, such as bool(), are allowed as they are
- # probably a member operator declaration or default constructor.
- matched = search(
- r'\b(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line)
- if matched:
- # gMock methods are defined using some variant of MOCK_METHODx(name, type)
- # where type may be float(), int(string), etc. Without context they are
- # virtually indistinguishable from int(x) casts.
- if not match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line):
- error(line_number, 'readability/casting', 4,
- 'Using deprecated casting style. '
- 'Use static_cast<%s>(...) instead' %
- matched.group(1))
-
- check_c_style_cast(line_number, line, clean_lines.raw_lines[line_number],
- 'static_cast',
- r'\((int|float|double|bool|char|u?int(16|32|64))\)',
- error)
- # This doesn't catch all cases. Consider (const char * const)"hello".
- check_c_style_cast(line_number, line, clean_lines.raw_lines[line_number],
- 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error)
-
- # In addition, we look for people taking the address of a cast. This
- # is dangerous -- casts can assign to temporaries, so the pointer doesn't
- # point where you think.
- if search(
- r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line):
- error(line_number, 'runtime/casting', 4,
- ('Are you taking an address of a cast? '
- 'This is dangerous: could be a temp var. '
- 'Take the address before doing the cast, rather than after'))
-
- # Check for people declaring static/global STL strings at the top level.
- # This is dangerous because the C++ language does not guarantee that
- # globals with constructors are initialized before the first access.
- matched = match(
- r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)',
- line)
- # Make sure it's not a function.
- # Function template specialization looks like: "string foo<Type>(...".
- # Class template definitions look like: "string Foo<Type>::Method(...".
- if matched and not match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)',
- matched.group(3)):
- error(line_number, 'runtime/string', 4,
- 'For a static/global string constant, use a C style string instead: '
- '"%schar %s[]".' %
- (matched.group(1), matched.group(2)))
-
- # Check that we're not using RTTI outside of testing code.
- if search(r'\bdynamic_cast<', line):
- error(line_number, 'runtime/rtti', 5,
- 'Do not use dynamic_cast<>. If you need to cast within a class '
- "hierarchy, use static_cast<> to upcast. Google doesn't support "
- 'RTTI.')
-
- if search(r'\b([A-Za-z0-9_]*_)\(\1\)', line):
- error(line_number, 'runtime/init', 4,
- 'You seem to be initializing a member variable with itself.')
-
- if file_extension == 'h':
- # FIXME: check that 1-arg constructors are explicit.
- # How to tell it's a constructor?
- # (handled in check_for_non_standard_constructs for now)
- pass
-
- # Check if people are using the verboten C basic types. The only exception
- # we regularly allow is "unsigned short port" for port.
- if search(r'\bshort port\b', line):
- if not search(r'\bunsigned short port\b', line):
- error(line_number, 'runtime/int', 4,
- 'Use "unsigned short" for ports, not "short"')
-
- # When snprintf is used, the second argument shouldn't be a literal.
- matched = search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line)
- if matched:
- error(line_number, 'runtime/printf', 3,
- 'If you can, use sizeof(%s) instead of %s as the 2nd arg '
- 'to snprintf.' % (matched.group(1), matched.group(2)))
-
- # Check if some verboten C functions are being used.
- if search(r'\bsprintf\b', line):
- error(line_number, 'runtime/printf', 5,
- 'Never use sprintf. Use snprintf instead.')
- matched = search(r'\b(strcpy|strcat)\b', line)
- if matched:
- error(line_number, 'runtime/printf', 4,
- 'Almost always, snprintf is better than %s' % matched.group(1))
-
- if search(r'\bsscanf\b', line):
- error(line_number, 'runtime/printf', 1,
- 'sscanf can be ok, but is slow and can overflow buffers.')
-
- # Check for suspicious usage of "if" like
- # } if (a == b) {
- if search(r'\}\s*if\s*\(', line):
- error(line_number, 'readability/braces', 4,
- 'Did you mean "else if"? If not, start a new line for "if".')
-
- # Check for potential format string bugs like printf(foo).
- # We constrain the pattern not to pick things like DocidForPrintf(foo).
- # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str())
- matched = re.search(r'\b((?:string)?printf)\s*\(([\w.\->()]+)\)', line, re.I)
- if matched:
- error(line_number, 'runtime/printf', 4,
- 'Potential format string bug. Do %s("%%s", %s) instead.'
- % (matched.group(1), matched.group(2)))
-
- # Check for potential memset bugs like memset(buf, sizeof(buf), 0).
- matched = search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line)
- if matched and not match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", matched.group(2)):
- error(line_number, 'runtime/memset', 4,
- 'Did you mean "memset(%s, 0, %s)"?'
- % (matched.group(1), matched.group(2)))
-
- # Detect variable-length arrays.
- matched = match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line)
- if (matched and matched.group(2) != 'return' and matched.group(2) != 'delete' and
- matched.group(3).find(']') == -1):
- # Split the size using space and arithmetic operators as delimiters.
- # If any of the resulting tokens are not compile time constants then
- # report the error.
- tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', matched.group(3))
- is_const = True
- skip_next = False
- for tok in tokens:
- if skip_next:
- skip_next = False
- continue
-
- if search(r'sizeof\(.+\)', tok):
- continue
- if search(r'arraysize\(\w+\)', tok):
- continue
-
- tok = tok.lstrip('(')
- tok = tok.rstrip(')')
- if not tok:
- continue
- if match(r'\d+', tok):
- continue
- if match(r'0[xX][0-9a-fA-F]+', tok):
- continue
- if match(r'k[A-Z0-9]\w*', tok):
- continue
- if match(r'(.+::)?k[A-Z0-9]\w*', tok):
- continue
- if match(r'(.+::)?[A-Z][A-Z0-9_]*', tok):
- continue
- # A catch all for tricky sizeof cases, including 'sizeof expression',
- # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)'
- # requires skipping the next token becasue we split on ' ' and '*'.
- if tok.startswith('sizeof'):
- skip_next = True
- continue
- is_const = False
- break
- if not is_const:
- error(line_number, 'runtime/arrays', 1,
- 'Do not use variable-length arrays. Use an appropriately named '
- "('k' followed by CamelCase) compile-time constant for the size.")
-
- # Check for use of unnamed namespaces in header files. Registration
- # macros are typically OK, so we allow use of "namespace {" on lines
- # that end with backslashes.
- if (file_extension == 'h'
- and search(r'\bnamespace\s*{', line)
- and line[-1] != '\\'):
- error(line_number, 'build/namespaces', 4,
- 'Do not use unnamed namespaces in header files. See '
- 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces'
- ' for more information.')
-
- check_identifier_name_in_declaration(filename, line_number, line, error)
-
-
-def check_identifier_name_in_declaration(filename, line_number, line, error):
- """Checks if identifier names contain any underscores.
-
- As identifiers in libraries we are using have a bunch of
- underscores, we only warn about the declarations of identifiers
- and don't check use of identifiers.
-
- Args:
- filename: The name of the current file.
- line_number: The number of the line to check.
- line: The line of code to check.
- error: The function to call with any errors found.
- """
- # We don't check a return statement.
- if match(r'\s*(return|delete)\b', line):
- return
-
- # Basically, a declaration is a type name followed by whitespaces
- # followed by an identifier. The type name can be complicated
- # due to type adjectives and templates. We remove them first to
- # simplify the process to find declarations of identifiers.
-
- # Convert "long long", "long double", and "long long int" to
- # simple types, but don't remove simple "long".
- line = sub(r'long (long )?(?=long|double|int)', '', line)
- # Convert unsigned/signed types to simple types, too.
- line = sub(r'(unsigned|signed) (?=char|short|int|long)', '', line)
- line = sub(r'\b(inline|using|static|const|volatile|auto|register|extern|typedef|restrict|struct|class|virtual)(?=\W)', '', line)
-
- # Remove "new" and "new (expr)" to simplify, too.
- line = sub(r'new\s*(\([^)]*\))?', '', line)
-
- # Remove all template parameters by removing matching < and >.
- # Loop until no templates are removed to remove nested templates.
- while True:
- line, number_of_replacements = subn(r'<([\w\s:]|::)+\s*[*&]*\s*>', '', line)
- if not number_of_replacements:
- break
-
- # Declarations of local variables can be in condition expressions
- # of control flow statements (e.g., "if (RenderObject* p = o->parent())").
- # We remove the keywords and the first parenthesis.
- #
- # Declarations in "while", "if", and "switch" are different from
- # other declarations in two aspects:
- #
- # - There can be only one declaration between the parentheses.
- # (i.e., you cannot write "if (int i = 0, j = 1) {}")
- # - The variable must be initialized.
- # (i.e., you cannot write "if (int i) {}")
- #
- # and we will need different treatments for them.
- line = sub(r'^\s*for\s*\(', '', line)
- line, control_statement = subn(r'^\s*(while|else if|if|switch)\s*\(', '', line)
-
- # Detect variable and functions.
- type_regexp = r'\w([\w]|\s*[*&]\s*|::)+'
- identifier_regexp = r'(?P<identifier>[\w:]+)'
- maybe_bitfield_regexp = r'(:\s*\d+\s*)?'
- character_after_identifier_regexp = r'(?P<character_after_identifier>[[;()=,])(?!=)'
- declaration_without_type_regexp = r'\s*' + identifier_regexp + r'\s*' + maybe_bitfield_regexp + character_after_identifier_regexp
- declaration_with_type_regexp = r'\s*' + type_regexp + r'\s' + declaration_without_type_regexp
- is_function_arguments = False
- number_of_identifiers = 0
- while True:
- # If we are seeing the first identifier or arguments of a
- # function, there should be a type name before an identifier.
- if not number_of_identifiers or is_function_arguments:
- declaration_regexp = declaration_with_type_regexp
- else:
- declaration_regexp = declaration_without_type_regexp
-
- matched = match(declaration_regexp, line)
- if not matched:
- return
- identifier = matched.group('identifier')
- character_after_identifier = matched.group('character_after_identifier')
-
- # If we removed a non-for-control statement, the character after
- # the identifier should be '='. With this rule, we can avoid
- # warning for cases like "if (val & INT_MAX) {".
- if control_statement and character_after_identifier != '=':
- return
-
- is_function_arguments = is_function_arguments or character_after_identifier == '('
-
- # Remove "m_" and "s_" to allow them.
- modified_identifier = sub(r'(^|(?<=::))[ms]_', '', identifier)
- if modified_identifier.find('_') >= 0:
- # Various exceptions to the rule: JavaScript op codes functions, const_iterator.
- if (not (filename.find('JavaScriptCore') >= 0 and modified_identifier.find('op_') >= 0)
- and not modified_identifier.startswith('tst_')
- and not modified_identifier.startswith('webkit_dom_object_')
- and not modified_identifier.startswith('NPN_')
- and not modified_identifier.startswith('NPP_')
- and not modified_identifier.startswith('NP_')
- and not modified_identifier.startswith('qt_')
- and not modified_identifier.startswith('cairo_')
- and not modified_identifier.find('::qt_') >= 0
- and not modified_identifier == "const_iterator"
- and not modified_identifier == "vm_throw"):
- error(line_number, 'readability/naming', 4, identifier + " is incorrectly named. Don't use underscores in your identifier names.")
-
- # Check for variables named 'l', these are too easy to confuse with '1' in some fonts
- if modified_identifier == 'l':
- error(line_number, 'readability/naming', 4, identifier + " is incorrectly named. Don't use the single letter 'l' as an identifier name.")
-
- # There can be only one declaration in non-for-control statements.
- if control_statement:
- return
- # We should continue checking if this is a function
- # declaration because we need to check its arguments.
- # Also, we need to check multiple declarations.
- if character_after_identifier != '(' and character_after_identifier != ',':
- return
-
- number_of_identifiers += 1
- line = line[matched.end():]
-
-def check_c_style_cast(line_number, line, raw_line, cast_type, pattern,
- error):
- """Checks for a C-style cast by looking for the pattern.
-
- This also handles sizeof(type) warnings, due to similarity of content.
-
- Args:
- line_number: The number of the line to check.
- line: The line of code to check.
- raw_line: The raw line of code to check, with comments.
- cast_type: The string for the C++ cast to recommend. This is either
- reinterpret_cast or static_cast, depending.
- pattern: The regular expression used to find C-style casts.
- error: The function to call with any errors found.
- """
- matched = search(pattern, line)
- if not matched:
- return
-
- # e.g., sizeof(int)
- sizeof_match = match(r'.*sizeof\s*$', line[0:matched.start(1) - 1])
- if sizeof_match:
- error(line_number, 'runtime/sizeof', 1,
- 'Using sizeof(type). Use sizeof(varname) instead if possible')
- return
-
- remainder = line[matched.end(0):]
-
- # The close paren is for function pointers as arguments to a function.
- # eg, void foo(void (*bar)(int));
- # The semicolon check is a more basic function check; also possibly a
- # function pointer typedef.
- # eg, void foo(int); or void foo(int) const;
- # The equals check is for function pointer assignment.
- # eg, void *(*foo)(int) = ...
- #
- # Right now, this will only catch cases where there's a single argument, and
- # it's unnamed. It should probably be expanded to check for multiple
- # arguments with some unnamed.
- function_match = match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)))', remainder)
- if function_match:
- if (not function_match.group(3)
- or function_match.group(3) == ';'
- or raw_line.find('/*') < 0):
- error(line_number, 'readability/function', 3,
- 'All parameters should be named in a function')
- return
-
- # At this point, all that should be left is actual casts.
- error(line_number, 'readability/casting', 4,
- 'Using C-style cast. Use %s<%s>(...) instead' %
- (cast_type, matched.group(1)))
-
-
-_HEADERS_CONTAINING_TEMPLATES = (
- ('<deque>', ('deque',)),
- ('<functional>', ('unary_function', 'binary_function',
- 'plus', 'minus', 'multiplies', 'divides', 'modulus',
- 'negate',
- 'equal_to', 'not_equal_to', 'greater', 'less',
- 'greater_equal', 'less_equal',
- 'logical_and', 'logical_or', 'logical_not',
- 'unary_negate', 'not1', 'binary_negate', 'not2',
- 'bind1st', 'bind2nd',
- 'pointer_to_unary_function',
- 'pointer_to_binary_function',
- 'ptr_fun',
- 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t',
- 'mem_fun_ref_t',
- 'const_mem_fun_t', 'const_mem_fun1_t',
- 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t',
- 'mem_fun_ref',
- )),
- ('<limits>', ('numeric_limits',)),
- ('<list>', ('list',)),
- ('<map>', ('map', 'multimap',)),
- ('<memory>', ('allocator',)),
- ('<queue>', ('queue', 'priority_queue',)),
- ('<set>', ('set', 'multiset',)),
- ('<stack>', ('stack',)),
- ('<string>', ('char_traits', 'basic_string',)),
- ('<utility>', ('pair',)),
- ('<vector>', ('vector',)),
-
- # gcc extensions.
- # Note: std::hash is their hash, ::hash is our hash
- ('<hash_map>', ('hash_map', 'hash_multimap',)),
- ('<hash_set>', ('hash_set', 'hash_multiset',)),
- ('<slist>', ('slist',)),
- )
-
-_HEADERS_ACCEPTED_BUT_NOT_PROMOTED = {
- # We can trust with reasonable confidence that map gives us pair<>, too.
- 'pair<>': ('map', 'multimap', 'hash_map', 'hash_multimap')
-}
-
-_RE_PATTERN_STRING = re.compile(r'\bstring\b')
-
-_re_pattern_algorithm_header = []
-for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap',
- 'transform'):
- # Match max<type>(..., ...), max(..., ...), but not foo->max, foo.max or
- # type::max().
- _re_pattern_algorithm_header.append(
- (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'),
- _template,
- '<algorithm>'))
-
-_re_pattern_templates = []
-for _header, _templates in _HEADERS_CONTAINING_TEMPLATES:
- for _template in _templates:
- _re_pattern_templates.append(
- (re.compile(r'(\<|\b)' + _template + r'\s*\<'),
- _template + '<>',
- _header))
-
-
-def files_belong_to_same_module(filename_cpp, filename_h):
- """Check if these two filenames belong to the same module.
-
- The concept of a 'module' here is a as follows:
- foo.h, foo-inl.h, foo.cpp, foo_test.cpp and foo_unittest.cpp belong to the
- same 'module' if they are in the same directory.
- some/path/public/xyzzy and some/path/internal/xyzzy are also considered
- to belong to the same module here.
-
- If the filename_cpp contains a longer path than the filename_h, for example,
- '/absolute/path/to/base/sysinfo.cpp', and this file would include
- 'base/sysinfo.h', this function also produces the prefix needed to open the
- header. This is used by the caller of this function to more robustly open the
- header file. We don't have access to the real include paths in this context,
- so we need this guesswork here.
-
- Known bugs: tools/base/bar.cpp and base/bar.h belong to the same module
- according to this implementation. Because of this, this function gives
- some false positives. This should be sufficiently rare in practice.
-
- Args:
- filename_cpp: is the path for the .cpp file
- filename_h: is the path for the header path
-
- Returns:
- Tuple with a bool and a string:
- bool: True if filename_cpp and filename_h belong to the same module.
- string: the additional prefix needed to open the header file.
- """
-
- if not filename_cpp.endswith('.cpp'):
- return (False, '')
- filename_cpp = filename_cpp[:-len('.cpp')]
- if filename_cpp.endswith('_unittest'):
- filename_cpp = filename_cpp[:-len('_unittest')]
- elif filename_cpp.endswith('_test'):
- filename_cpp = filename_cpp[:-len('_test')]
- filename_cpp = filename_cpp.replace('/public/', '/')
- filename_cpp = filename_cpp.replace('/internal/', '/')
-
- if not filename_h.endswith('.h'):
- return (False, '')
- filename_h = filename_h[:-len('.h')]
- if filename_h.endswith('-inl'):
- filename_h = filename_h[:-len('-inl')]
- filename_h = filename_h.replace('/public/', '/')
- filename_h = filename_h.replace('/internal/', '/')
-
- files_belong_to_same_module = filename_cpp.endswith(filename_h)
- common_path = ''
- if files_belong_to_same_module:
- common_path = filename_cpp[:-len(filename_h)]
- return files_belong_to_same_module, common_path
-
-
-def update_include_state(filename, include_state, io=codecs):
- """Fill up the include_state with new includes found from the file.
-
- Args:
- filename: the name of the header to read.
- include_state: an _IncludeState instance in which the headers are inserted.
- io: The io factory to use to read the file. Provided for testability.
-
- Returns:
- True if a header was succesfully added. False otherwise.
- """
- header_file = None
- try:
- header_file = io.open(filename, 'r', 'utf8', 'replace')
- except IOError:
- return False
- line_number = 0
- for line in header_file:
- line_number += 1
- clean_line = cleanse_comments(line)
- matched = _RE_PATTERN_INCLUDE.search(clean_line)
- if matched:
- include = matched.group(2)
- # The value formatting is cute, but not really used right now.
- # What matters here is that the key is in include_state.
- include_state.setdefault(include, '%s:%d' % (filename, line_number))
- return True
-
-
-def check_for_include_what_you_use(filename, clean_lines, include_state, error,
- io=codecs):
- """Reports for missing stl includes.
-
- This function will output warnings to make sure you are including the headers
- necessary for the stl containers and functions that you use. We only give one
- reason to include a header. For example, if you use both equal_to<> and
- less<> in a .h file, only one (the latter in the file) of these will be
- reported as a reason to include the <functional>.
-
- Args:
- filename: The name of the current file.
- clean_lines: A CleansedLines instance containing the file.
- include_state: An _IncludeState instance.
- error: The function to call with any errors found.
- io: The IO factory to use to read the header file. Provided for unittest
- injection.
- """
- required = {} # A map of header name to line_number and the template entity.
- # Example of required: { '<functional>': (1219, 'less<>') }
-
- for line_number in xrange(clean_lines.num_lines()):
- line = clean_lines.elided[line_number]
- if not line or line[0] == '#':
- continue
-
- # String is special -- it is a non-templatized type in STL.
- if _RE_PATTERN_STRING.search(line):
- required['<string>'] = (line_number, 'string')
-
- for pattern, template, header in _re_pattern_algorithm_header:
- if pattern.search(line):
- required[header] = (line_number, template)
-
- # The following function is just a speed up, no semantics are changed.
- if not '<' in line: # Reduces the cpu time usage by skipping lines.
- continue
-
- for pattern, template, header in _re_pattern_templates:
- if pattern.search(line):
- required[header] = (line_number, template)
-
- # The policy is that if you #include something in foo.h you don't need to
- # include it again in foo.cpp. Here, we will look at possible includes.
- # Let's copy the include_state so it is only messed up within this function.
- include_state = include_state.copy()
-
- # Did we find the header for this file (if any) and succesfully load it?
- header_found = False
-
- # Use the absolute path so that matching works properly.
- abs_filename = os.path.abspath(filename)
-
- # For Emacs's flymake.
- # If cpp_style is invoked from Emacs's flymake, a temporary file is generated
- # by flymake and that file name might end with '_flymake.cpp'. In that case,
- # restore original file name here so that the corresponding header file can be
- # found.
- # e.g. If the file name is 'foo_flymake.cpp', we should search for 'foo.h'
- # instead of 'foo_flymake.h'
- abs_filename = re.sub(r'_flymake\.cpp$', '.cpp', abs_filename)
-
- # include_state is modified during iteration, so we iterate over a copy of
- # the keys.
- for header in include_state.keys(): #NOLINT
- (same_module, common_path) = files_belong_to_same_module(abs_filename, header)
- fullpath = common_path + header
- if same_module and update_include_state(fullpath, include_state, io):
- header_found = True
-
- # If we can't find the header file for a .cpp, assume it's because we don't
- # know where to look. In that case we'll give up as we're not sure they
- # didn't include it in the .h file.
- # FIXME: Do a better job of finding .h files so we are confident that
- # not having the .h file means there isn't one.
- if filename.endswith('.cpp') and not header_found:
- return
-
- # All the lines have been processed, report the errors found.
- for required_header_unstripped in required:
- template = required[required_header_unstripped][1]
- if template in _HEADERS_ACCEPTED_BUT_NOT_PROMOTED:
- headers = _HEADERS_ACCEPTED_BUT_NOT_PROMOTED[template]
- if [True for header in headers if header in include_state]:
- continue
- if required_header_unstripped.strip('<>"') not in include_state:
- error(required[required_header_unstripped][0],
- 'build/include_what_you_use', 4,
- 'Add #include ' + required_header_unstripped + ' for ' + template)
-
-
-def process_line(filename, file_extension,
- clean_lines, line, include_state, function_state,
- class_state, file_state, error):
- """Processes a single line in the file.
-
- Args:
- filename: Filename of the file that is being processed.
- file_extension: The extension (dot not included) of the file.
- clean_lines: An array of strings, each representing a line of the file,
- with comments stripped.
- line: Number of line being processed.
- include_state: An _IncludeState instance in which the headers are inserted.
- function_state: A _FunctionState instance which counts function lines, etc.
- class_state: A _ClassState instance which maintains information about
- the current stack of nested class declarations being parsed.
- file_state: A _FileState instance which maintains information about
- the state of things in the file.
- error: A callable to which errors are reported, which takes arguments:
- line number, error level, and message
-
- """
- raw_lines = clean_lines.raw_lines
- detect_functions(clean_lines, line, function_state, error)
- check_for_function_lengths(clean_lines, line, function_state, error)
- if search(r'\bNOLINT\b', raw_lines[line]): # ignore nolint lines
- return
- check_pass_ptr_usage(clean_lines, line, function_state, error)
- check_for_multiline_comments_and_strings(clean_lines, line, error)
- check_style(clean_lines, line, file_extension, class_state, file_state, error)
- check_language(filename, clean_lines, line, file_extension, include_state,
- error)
- check_for_non_standard_constructs(clean_lines, line, class_state, error)
- check_posix_threading(clean_lines, line, error)
- check_invalid_increment(clean_lines, line, error)
-
-
-def _process_lines(filename, file_extension, lines, error, min_confidence):
- """Performs lint checks and reports any errors to the given error function.
-
- Args:
- filename: Filename of the file that is being processed.
- file_extension: The extension (dot not included) of the file.
- lines: An array of strings, each representing a line of the file, with the
- last element being empty if the file is termined with a newline.
- error: A callable to which errors are reported, which takes 4 arguments:
- """
- lines = (['// marker so line numbers and indices both start at 1'] + lines +
- ['// marker so line numbers end in a known way'])
-
- include_state = _IncludeState()
- function_state = _FunctionState(min_confidence)
- class_state = _ClassState()
- file_state = _FileState()
-
- check_for_copyright(lines, error)
-
- if file_extension == 'h':
- check_for_header_guard(filename, lines, error)
-
- remove_multi_line_comments(lines, error)
- clean_lines = CleansedLines(lines)
- for line in xrange(clean_lines.num_lines()):
- process_line(filename, file_extension, clean_lines, line,
- include_state, function_state, class_state, file_state, error)
- class_state.check_finished(error)
-
- check_for_include_what_you_use(filename, clean_lines, include_state, error)
-
- # We check here rather than inside process_line so that we see raw
- # lines rather than "cleaned" lines.
- check_for_unicode_replacement_characters(lines, error)
-
- check_for_new_line_at_eof(lines, error)
-
-
-class CppChecker(object):
-
- """Processes C++ lines for checking style."""
-
- # This list is used to--
- #
- # (1) generate an explicit list of all possible categories,
- # (2) unit test that all checked categories have valid names, and
- # (3) unit test that all categories are getting unit tested.
- #
- categories = set([
- 'build/class',
- 'build/deprecated',
- 'build/endif_comment',
- 'build/forward_decl',
- 'build/header_guard',
- 'build/include',
- 'build/include_order',
- 'build/include_what_you_use',
- 'build/namespaces',
- 'build/printf_format',
- 'build/storage_class',
- 'build/using_std',
- 'legal/copyright',
- 'readability/braces',
- 'readability/casting',
- 'readability/check',
- 'readability/comparison_to_zero',
- 'readability/constructors',
- 'readability/control_flow',
- 'readability/fn_size',
- 'readability/function',
- 'readability/multiline_comment',
- 'readability/multiline_string',
- 'readability/naming',
- 'readability/null',
- 'readability/pass_ptr',
- 'readability/streams',
- 'readability/todo',
- 'readability/utf8',
- 'runtime/arrays',
- 'runtime/casting',
- 'runtime/explicit',
- 'runtime/init',
- 'runtime/int',
- 'runtime/invalid_increment',
- 'runtime/max_min_macros',
- 'runtime/memset',
- 'runtime/printf',
- 'runtime/printf_format',
- 'runtime/references',
- 'runtime/rtti',
- 'runtime/sizeof',
- 'runtime/string',
- 'runtime/threadsafe_fn',
- 'runtime/virtual',
- 'whitespace/blank_line',
- 'whitespace/braces',
- 'whitespace/comma',
- 'whitespace/comments',
- 'whitespace/declaration',
- 'whitespace/end_of_line',
- 'whitespace/ending_newline',
- 'whitespace/indent',
- 'whitespace/labels',
- 'whitespace/line_length',
- 'whitespace/newline',
- 'whitespace/operators',
- 'whitespace/parens',
- 'whitespace/semicolon',
- 'whitespace/tab',
- 'whitespace/todo',
- ])
-
- def __init__(self, file_path, file_extension, handle_style_error,
- min_confidence):
- """Create a CppChecker instance.
-
- Args:
- file_extension: A string that is the file extension, without
- the leading dot.
-
- """
- self.file_extension = file_extension
- self.file_path = file_path
- self.handle_style_error = handle_style_error
- self.min_confidence = min_confidence
-
- # Useful for unit testing.
- def __eq__(self, other):
- """Return whether this CppChecker instance is equal to another."""
- if self.file_extension != other.file_extension:
- return False
- if self.file_path != other.file_path:
- return False
- if self.handle_style_error != other.handle_style_error:
- return False
- if self.min_confidence != other.min_confidence:
- return False
-
- return True
-
- # Useful for unit testing.
- def __ne__(self, other):
- # Python does not automatically deduce __ne__() from __eq__().
- return not self.__eq__(other)
-
- def check(self, lines):
- _process_lines(self.file_path, self.file_extension, lines,
- self.handle_style_error, self.min_confidence)
-
-
-# FIXME: Remove this function (requires refactoring unit tests).
-def process_file_data(filename, file_extension, lines, error, min_confidence):
- checker = CppChecker(filename, file_extension, error, min_confidence)
- checker.check(lines)
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py b/WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py
deleted file mode 100644
index 13b053c..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py
+++ /dev/null
@@ -1,3923 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8; -*-
-#
-# Copyright (C) 2009 Google Inc. All rights reserved.
-# Copyright (C) 2009 Torch Mobile Inc.
-# Copyright (C) 2009 Apple Inc. All rights reserved.
-# 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:
-#
-# * 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 test for cpp_style.py."""
-
-# FIXME: Add a good test that tests UpdateIncludeState.
-
-import codecs
-import os
-import random
-import re
-import unittest
-import cpp as cpp_style
-from cpp import CppChecker
-
-# This class works as an error collector and replaces cpp_style.Error
-# function for the unit tests. We also verify each category we see
-# is in STYLE_CATEGORIES, to help keep that list up to date.
-class ErrorCollector:
- _all_style_categories = CppChecker.categories
- # This is a list including all categories seen in any unit test.
- _seen_style_categories = {}
-
- def __init__(self, assert_fn):
- """assert_fn: a function to call when we notice a problem."""
- self._assert_fn = assert_fn
- self._errors = []
-
- def __call__(self, unused_linenum, category, confidence, message):
- self._assert_fn(category in self._all_style_categories,
- 'Message "%s" has category "%s",'
- ' which is not in STYLE_CATEGORIES' % (message, category))
- self._seen_style_categories[category] = 1
- self._errors.append('%s [%s] [%d]' % (message, category, confidence))
-
- def results(self):
- if len(self._errors) < 2:
- return ''.join(self._errors) # Most tests expect to have a string.
- else:
- return self._errors # Let's give a list if there is more than one.
-
- def result_list(self):
- return self._errors
-
- def verify_all_categories_are_seen(self):
- """Fails if there's a category in _all_style_categories - _seen_style_categories.
-
- This should only be called after all tests are run, so
- _seen_style_categories has had a chance to fully populate. Since
- this isn't called from within the normal unittest framework, we
- can't use the normal unittest assert macros. Instead we just exit
- when we see an error. Good thing this test is always run last!
- """
- for category in self._all_style_categories:
- if category not in self._seen_style_categories:
- import sys
- sys.exit('FATAL ERROR: There are no tests for category "%s"' % category)
-
- def remove_if_present(self, substr):
- for (index, error) in enumerate(self._errors):
- if error.find(substr) != -1:
- self._errors = self._errors[0:index] + self._errors[(index + 1):]
- break
-
-
-# This class is a lame mock of codecs. We do not verify filename, mode, or
-# encoding, but for the current use case it is not needed.
-class MockIo:
- def __init__(self, mock_file):
- self.mock_file = mock_file
-
- def open(self, unused_filename, unused_mode, unused_encoding, _): # NOLINT
- # (lint doesn't like open as a method name)
- return self.mock_file
-
-
-class CppFunctionsTest(unittest.TestCase):
-
- """Supports testing functions that do not need CppStyleTestBase."""
-
- def test_is_c_or_objective_c(self):
- self.assertTrue(cpp_style.is_c_or_objective_c("c"))
- self.assertTrue(cpp_style.is_c_or_objective_c("m"))
- self.assertFalse(cpp_style.is_c_or_objective_c("cpp"))
-
-
-class CppStyleTestBase(unittest.TestCase):
- """Provides some useful helper functions for cpp_style tests.
-
- Attributes:
- min_confidence: An integer that is the current minimum confidence
- level for the tests.
-
- """
-
- # FIXME: Refactor the unit tests so the confidence level is passed
- # explicitly, just like it is in the real code.
- min_confidence = 1;
-
- # Helper function to avoid needing to explicitly pass confidence
- # in all the unit test calls to cpp_style.process_file_data().
- def process_file_data(self, filename, file_extension, lines, error):
- """Call cpp_style.process_file_data() with the min_confidence."""
- return cpp_style.process_file_data(filename, file_extension, lines,
- error, self.min_confidence)
-
- # Perform lint on single line of input and return the error message.
- def perform_single_line_lint(self, code, file_name):
- error_collector = ErrorCollector(self.assert_)
- lines = code.split('\n')
- cpp_style.remove_multi_line_comments(lines, error_collector)
- clean_lines = cpp_style.CleansedLines(lines)
- include_state = cpp_style._IncludeState()
- function_state = cpp_style._FunctionState(self.min_confidence)
- ext = file_name[file_name.rfind('.') + 1:]
- class_state = cpp_style._ClassState()
- file_state = cpp_style._FileState()
- cpp_style.process_line(file_name, ext, clean_lines, 0,
- include_state, function_state,
- class_state, file_state, error_collector)
- # Single-line lint tests are allowed to fail the 'unlintable function'
- # check.
- error_collector.remove_if_present(
- 'Lint failed to find start of function body.')
- return error_collector.results()
-
- # Perform lint over multiple lines and return the error message.
- def perform_multi_line_lint(self, code, file_extension):
- error_collector = ErrorCollector(self.assert_)
- lines = code.split('\n')
- cpp_style.remove_multi_line_comments(lines, error_collector)
- lines = cpp_style.CleansedLines(lines)
- class_state = cpp_style._ClassState()
- file_state = cpp_style._FileState()
- for i in xrange(lines.num_lines()):
- cpp_style.check_style(lines, i, file_extension, class_state, file_state, error_collector)
- cpp_style.check_for_non_standard_constructs(lines, i, class_state,
- error_collector)
- class_state.check_finished(error_collector)
- return error_collector.results()
-
- # Similar to perform_multi_line_lint, but calls check_language instead of
- # check_for_non_standard_constructs
- def perform_language_rules_check(self, file_name, code):
- error_collector = ErrorCollector(self.assert_)
- include_state = cpp_style._IncludeState()
- lines = code.split('\n')
- cpp_style.remove_multi_line_comments(lines, error_collector)
- lines = cpp_style.CleansedLines(lines)
- ext = file_name[file_name.rfind('.') + 1:]
- for i in xrange(lines.num_lines()):
- cpp_style.check_language(file_name, lines, i, ext, include_state,
- error_collector)
- return error_collector.results()
-
- def perform_function_lengths_check(self, code):
- """Perform Lint function length check on block of code and return warnings.
-
- Builds up an array of lines corresponding to the code and strips comments
- using cpp_style functions.
-
- Establishes an error collector and invokes the function length checking
- function following cpp_style's pattern.
-
- Args:
- code: C++ source code expected to generate a warning message.
-
- Returns:
- The accumulated errors.
- """
- error_collector = ErrorCollector(self.assert_)
- function_state = cpp_style._FunctionState(self.min_confidence)
- lines = code.split('\n')
- cpp_style.remove_multi_line_comments(lines, error_collector)
- lines = cpp_style.CleansedLines(lines)
- for i in xrange(lines.num_lines()):
- cpp_style.detect_functions(lines, i,
- function_state, error_collector)
- cpp_style.check_for_function_lengths(lines, i,
- function_state, error_collector)
- return error_collector.results()
-
- # Similar to perform_function_lengths_check, but calls check_pass_ptr_usage
- # instead of check_for_function_lengths.
- def perform_pass_ptr_check(self, code):
- error_collector = ErrorCollector(self.assert_)
- function_state = cpp_style._FunctionState(self.min_confidence)
- lines = code.split('\n')
- cpp_style.remove_multi_line_comments(lines, error_collector)
- lines = cpp_style.CleansedLines(lines)
- for i in xrange(lines.num_lines()):
- cpp_style.detect_functions(lines, i,
- function_state, error_collector)
- cpp_style.check_pass_ptr_usage(lines, i,
- function_state, error_collector)
- return error_collector.results()
-
- def perform_include_what_you_use(self, code, filename='foo.h', io=codecs):
- # First, build up the include state.
- error_collector = ErrorCollector(self.assert_)
- include_state = cpp_style._IncludeState()
- lines = code.split('\n')
- cpp_style.remove_multi_line_comments(lines, error_collector)
- lines = cpp_style.CleansedLines(lines)
- file_extension = filename[filename.rfind('.') + 1:]
- for i in xrange(lines.num_lines()):
- cpp_style.check_language(filename, lines, i, file_extension, include_state,
- error_collector)
- # We could clear the error_collector here, but this should
- # also be fine, since our IncludeWhatYouUse unittests do not
- # have language problems.
-
- # Second, look for missing includes.
- cpp_style.check_for_include_what_you_use(filename, lines, include_state,
- error_collector, io)
- return error_collector.results()
-
- # Perform lint and compare the error message with "expected_message".
- def assert_lint(self, code, expected_message, file_name='foo.cpp'):
- self.assertEquals(expected_message, self.perform_single_line_lint(code, file_name))
-
- def assert_lint_one_of_many_errors_re(self, code, expected_message_re, file_name='foo.cpp'):
- messages = self.perform_single_line_lint(code, file_name)
- for message in messages:
- if re.search(expected_message_re, message):
- return
-
- self.assertEquals(expected_message_re, messages)
-
- def assert_multi_line_lint(self, code, expected_message, file_name='foo.h'):
- file_extension = file_name[file_name.rfind('.') + 1:]
- self.assertEquals(expected_message, self.perform_multi_line_lint(code, file_extension))
-
- def assert_multi_line_lint_re(self, code, expected_message_re, file_name='foo.h'):
- file_extension = file_name[file_name.rfind('.') + 1:]
- message = self.perform_multi_line_lint(code, file_extension)
- if not re.search(expected_message_re, message):
- self.fail('Message was:\n' + message + 'Expected match to "' + expected_message_re + '"')
-
- def assert_language_rules_check(self, file_name, code, expected_message):
- self.assertEquals(expected_message,
- self.perform_language_rules_check(file_name, code))
-
- def assert_include_what_you_use(self, code, expected_message):
- self.assertEquals(expected_message,
- self.perform_include_what_you_use(code))
-
- def assert_blank_lines_check(self, lines, start_errors, end_errors):
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data('foo.cpp', 'cpp', lines, error_collector)
- self.assertEquals(
- start_errors,
- error_collector.results().count(
- 'Blank line at the start of a code block. Is this needed?'
- ' [whitespace/blank_line] [2]'))
- self.assertEquals(
- end_errors,
- error_collector.results().count(
- 'Blank line at the end of a code block. Is this needed?'
- ' [whitespace/blank_line] [3]'))
-
-
-class CppStyleTest(CppStyleTestBase):
-
- # Test get line width.
- def test_get_line_width(self):
- self.assertEquals(0, cpp_style.get_line_width(''))
- self.assertEquals(10, cpp_style.get_line_width(u'x' * 10))
- self.assertEquals(16, cpp_style.get_line_width(u'都|道|府|県|支庁'))
-
- def test_find_next_multi_line_comment_start(self):
- self.assertEquals(1, cpp_style.find_next_multi_line_comment_start([''], 0))
-
- lines = ['a', 'b', '/* c']
- self.assertEquals(2, cpp_style.find_next_multi_line_comment_start(lines, 0))
-
- lines = ['char a[] = "/*";'] # not recognized as comment.
- self.assertEquals(1, cpp_style.find_next_multi_line_comment_start(lines, 0))
-
- def test_find_next_multi_line_comment_end(self):
- self.assertEquals(1, cpp_style.find_next_multi_line_comment_end([''], 0))
- lines = ['a', 'b', ' c */']
- self.assertEquals(2, cpp_style.find_next_multi_line_comment_end(lines, 0))
-
- def test_remove_multi_line_comments_from_range(self):
- lines = ['a', ' /* comment ', ' * still comment', ' comment */ ', 'b']
- cpp_style.remove_multi_line_comments_from_range(lines, 1, 4)
- self.assertEquals(['a', '// dummy', '// dummy', '// dummy', 'b'], lines)
-
- def test_spaces_at_end_of_line(self):
- self.assert_lint(
- '// Hello there ',
- 'Line ends in whitespace. Consider deleting these extra spaces.'
- ' [whitespace/end_of_line] [4]')
-
- # Test C-style cast cases.
- def test_cstyle_cast(self):
- self.assert_lint(
- 'int a = (int)1.0;',
- 'Using C-style cast. Use static_cast<int>(...) instead'
- ' [readability/casting] [4]')
- self.assert_lint(
- 'int *a = (int *)DEFINED_VALUE;',
- 'Using C-style cast. Use reinterpret_cast<int *>(...) instead'
- ' [readability/casting] [4]', 'foo.c')
- self.assert_lint(
- 'uint16 a = (uint16)1.0;',
- 'Using C-style cast. Use static_cast<uint16>(...) instead'
- ' [readability/casting] [4]')
- self.assert_lint(
- 'int32 a = (int32)1.0;',
- 'Using C-style cast. Use static_cast<int32>(...) instead'
- ' [readability/casting] [4]')
- self.assert_lint(
- 'uint64 a = (uint64)1.0;',
- 'Using C-style cast. Use static_cast<uint64>(...) instead'
- ' [readability/casting] [4]')
-
- # Test taking address of casts (runtime/casting)
- def test_runtime_casting(self):
- self.assert_lint(
- 'int* x = &static_cast<int*>(foo);',
- 'Are you taking an address of a cast? '
- 'This is dangerous: could be a temp var. '
- 'Take the address before doing the cast, rather than after'
- ' [runtime/casting] [4]')
-
- self.assert_lint(
- 'int* x = &dynamic_cast<int *>(foo);',
- ['Are you taking an address of a cast? '
- 'This is dangerous: could be a temp var. '
- 'Take the address before doing the cast, rather than after'
- ' [runtime/casting] [4]',
- 'Do not use dynamic_cast<>. If you need to cast within a class '
- 'hierarchy, use static_cast<> to upcast. Google doesn\'t support '
- 'RTTI. [runtime/rtti] [5]'])
-
- self.assert_lint(
- 'int* x = &reinterpret_cast<int *>(foo);',
- 'Are you taking an address of a cast? '
- 'This is dangerous: could be a temp var. '
- 'Take the address before doing the cast, rather than after'
- ' [runtime/casting] [4]')
-
- # It's OK to cast an address.
- self.assert_lint(
- 'int* x = reinterpret_cast<int *>(&foo);',
- '')
-
- def test_runtime_selfinit(self):
- self.assert_lint(
- 'Foo::Foo(Bar r, Bel l) : r_(r_), l_(l_) { }',
- 'You seem to be initializing a member variable with itself.'
- ' [runtime/init] [4]')
- self.assert_lint(
- 'Foo::Foo(Bar r, Bel l) : r_(r), l_(l) { }',
- '')
- self.assert_lint(
- 'Foo::Foo(Bar r) : r_(r), l_(r_), ll_(l_) { }',
- '')
-
- def test_runtime_rtti(self):
- statement = 'int* x = dynamic_cast<int*>(&foo);'
- error_message = (
- 'Do not use dynamic_cast<>. If you need to cast within a class '
- 'hierarchy, use static_cast<> to upcast. Google doesn\'t support '
- 'RTTI. [runtime/rtti] [5]')
- # dynamic_cast is disallowed in most files.
- self.assert_language_rules_check('foo.cpp', statement, error_message)
- self.assert_language_rules_check('foo.h', statement, error_message)
-
- # We cannot test this functionality because of difference of
- # function definitions. Anyway, we may never enable this.
- #
- # # Test for unnamed arguments in a method.
- # def test_check_for_unnamed_params(self):
- # message = ('All parameters should be named in a function'
- # ' [readability/function] [3]')
- # self.assert_lint('virtual void A(int*) const;', message)
- # self.assert_lint('virtual void B(void (*fn)(int*));', message)
- # self.assert_lint('virtual void C(int*);', message)
- # self.assert_lint('void *(*f)(void *) = x;', message)
- # self.assert_lint('void Method(char*) {', message)
- # self.assert_lint('void Method(char*);', message)
- # self.assert_lint('void Method(char* /*x*/);', message)
- # self.assert_lint('typedef void (*Method)(int32);', message)
- # self.assert_lint('static void operator delete[](void*) throw();', message)
- #
- # self.assert_lint('virtual void D(int* p);', '')
- # self.assert_lint('void operator delete(void* x) throw();', '')
- # self.assert_lint('void Method(char* x)\n{', '')
- # self.assert_lint('void Method(char* /*x*/)\n{', '')
- # self.assert_lint('void Method(char* x);', '')
- # self.assert_lint('typedef void (*Method)(int32 x);', '')
- # self.assert_lint('static void operator delete[](void* x) throw();', '')
- # self.assert_lint('static void operator delete[](void* /*x*/) throw();', '')
- #
- # # This one should technically warn, but doesn't because the function
- # # pointer is confusing.
- # self.assert_lint('virtual void E(void (*fn)(int* p));', '')
-
- # Test deprecated casts such as int(d)
- def test_deprecated_cast(self):
- self.assert_lint(
- 'int a = int(2.2);',
- 'Using deprecated casting style. '
- 'Use static_cast<int>(...) instead'
- ' [readability/casting] [4]')
- # Checks for false positives...
- self.assert_lint(
- 'int a = int(); // Constructor, o.k.',
- '')
- self.assert_lint(
- 'X::X() : a(int()) {} // default Constructor, o.k.',
- '')
- self.assert_lint(
- 'operator bool(); // Conversion operator, o.k.',
- '')
-
- # The second parameter to a gMock method definition is a function signature
- # that often looks like a bad cast but should not picked up by lint.
- def test_mock_method(self):
- self.assert_lint(
- 'MOCK_METHOD0(method, int());',
- '')
- self.assert_lint(
- 'MOCK_CONST_METHOD1(method, float(string));',
- '')
- self.assert_lint(
- 'MOCK_CONST_METHOD2_T(method, double(float, float));',
- '')
-
- # Test sizeof(type) cases.
- def test_sizeof_type(self):
- self.assert_lint(
- 'sizeof(int);',
- 'Using sizeof(type). Use sizeof(varname) instead if possible'
- ' [runtime/sizeof] [1]')
- self.assert_lint(
- 'sizeof(int *);',
- 'Using sizeof(type). Use sizeof(varname) instead if possible'
- ' [runtime/sizeof] [1]')
-
- # Test typedef cases. There was a bug that cpp_style misidentified
- # typedef for pointer to function as C-style cast and produced
- # false-positive error messages.
- def test_typedef_for_pointer_to_function(self):
- self.assert_lint(
- 'typedef void (*Func)(int x);',
- '')
- self.assert_lint(
- 'typedef void (*Func)(int *x);',
- '')
- self.assert_lint(
- 'typedef void Func(int x);',
- '')
- self.assert_lint(
- 'typedef void Func(int *x);',
- '')
-
- def test_include_what_you_use_no_implementation_files(self):
- code = 'std::vector<int> foo;'
- self.assertEquals('Add #include <vector> for vector<>'
- ' [build/include_what_you_use] [4]',
- self.perform_include_what_you_use(code, 'foo.h'))
- self.assertEquals('',
- self.perform_include_what_you_use(code, 'foo.cpp'))
-
- def test_include_what_you_use(self):
- self.assert_include_what_you_use(
- '''#include <vector>
- std::vector<int> foo;
- ''',
- '')
- self.assert_include_what_you_use(
- '''#include <map>
- std::pair<int,int> foo;
- ''',
- '')
- self.assert_include_what_you_use(
- '''#include <multimap>
- std::pair<int,int> foo;
- ''',
- '')
- self.assert_include_what_you_use(
- '''#include <hash_map>
- std::pair<int,int> foo;
- ''',
- '')
- self.assert_include_what_you_use(
- '''#include <utility>
- std::pair<int,int> foo;
- ''',
- '')
- self.assert_include_what_you_use(
- '''#include <vector>
- DECLARE_string(foobar);
- ''',
- '')
- self.assert_include_what_you_use(
- '''#include <vector>
- DEFINE_string(foobar, "", "");
- ''',
- '')
- self.assert_include_what_you_use(
- '''#include <vector>
- std::pair<int,int> foo;
- ''',
- 'Add #include <utility> for pair<>'
- ' [build/include_what_you_use] [4]')
- self.assert_include_what_you_use(
- '''#include "base/foobar.h"
- std::vector<int> foo;
- ''',
- 'Add #include <vector> for vector<>'
- ' [build/include_what_you_use] [4]')
- self.assert_include_what_you_use(
- '''#include <vector>
- std::set<int> foo;
- ''',
- 'Add #include <set> for set<>'
- ' [build/include_what_you_use] [4]')
- self.assert_include_what_you_use(
- '''#include "base/foobar.h"
- hash_map<int, int> foobar;
- ''',
- 'Add #include <hash_map> for hash_map<>'
- ' [build/include_what_you_use] [4]')
- self.assert_include_what_you_use(
- '''#include "base/foobar.h"
- bool foobar = std::less<int>(0,1);
- ''',
- 'Add #include <functional> for less<>'
- ' [build/include_what_you_use] [4]')
- self.assert_include_what_you_use(
- '''#include "base/foobar.h"
- bool foobar = min<int>(0,1);
- ''',
- 'Add #include <algorithm> for min [build/include_what_you_use] [4]')
- self.assert_include_what_you_use(
- 'void a(const string &foobar);',
- 'Add #include <string> for string [build/include_what_you_use] [4]')
- self.assert_include_what_you_use(
- '''#include "base/foobar.h"
- bool foobar = swap(0,1);
- ''',
- 'Add #include <algorithm> for swap [build/include_what_you_use] [4]')
- self.assert_include_what_you_use(
- '''#include "base/foobar.h"
- bool foobar = transform(a.begin(), a.end(), b.start(), Foo);
- ''',
- 'Add #include <algorithm> for transform '
- '[build/include_what_you_use] [4]')
- self.assert_include_what_you_use(
- '''#include "base/foobar.h"
- bool foobar = min_element(a.begin(), a.end());
- ''',
- 'Add #include <algorithm> for min_element '
- '[build/include_what_you_use] [4]')
- self.assert_include_what_you_use(
- '''foo->swap(0,1);
- foo.swap(0,1);
- ''',
- '')
- self.assert_include_what_you_use(
- '''#include <string>
- void a(const std::multimap<int,string> &foobar);
- ''',
- 'Add #include <map> for multimap<>'
- ' [build/include_what_you_use] [4]')
- self.assert_include_what_you_use(
- '''#include <queue>
- void a(const std::priority_queue<int> &foobar);
- ''',
- '')
- self.assert_include_what_you_use(
- '''#include "base/basictypes.h"
- #include "base/port.h"
- #include <assert.h>
- #include <string>
- #include <vector>
- vector<string> hajoa;''', '')
- self.assert_include_what_you_use(
- '''#include <string>
- int i = numeric_limits<int>::max()
- ''',
- 'Add #include <limits> for numeric_limits<>'
- ' [build/include_what_you_use] [4]')
- self.assert_include_what_you_use(
- '''#include <limits>
- int i = numeric_limits<int>::max()
- ''',
- '')
-
- # Test the UpdateIncludeState code path.
- mock_header_contents = ['#include "blah/foo.h"', '#include "blah/bar.h"']
- message = self.perform_include_what_you_use(
- '#include "config.h"\n'
- '#include "blah/a.h"\n',
- filename='blah/a.cpp',
- io=MockIo(mock_header_contents))
- self.assertEquals(message, '')
-
- mock_header_contents = ['#include <set>']
- message = self.perform_include_what_you_use(
- '''#include "config.h"
- #include "blah/a.h"
-
- std::set<int> foo;''',
- filename='blah/a.cpp',
- io=MockIo(mock_header_contents))
- self.assertEquals(message, '')
-
- # If there's just a .cpp and the header can't be found then it's ok.
- message = self.perform_include_what_you_use(
- '''#include "config.h"
- #include "blah/a.h"
-
- std::set<int> foo;''',
- filename='blah/a.cpp')
- self.assertEquals(message, '')
-
- # Make sure we find the headers with relative paths.
- mock_header_contents = ['']
- message = self.perform_include_what_you_use(
- '''#include "config.h"
- #include "%s/a.h"
-
- std::set<int> foo;''' % os.path.basename(os.getcwd()),
- filename='a.cpp',
- io=MockIo(mock_header_contents))
- self.assertEquals(message, 'Add #include <set> for set<> '
- '[build/include_what_you_use] [4]')
-
- def test_files_belong_to_same_module(self):
- f = cpp_style.files_belong_to_same_module
- self.assertEquals((True, ''), f('a.cpp', 'a.h'))
- self.assertEquals((True, ''), f('base/google.cpp', 'base/google.h'))
- self.assertEquals((True, ''), f('base/google_test.cpp', 'base/google.h'))
- self.assertEquals((True, ''),
- f('base/google_unittest.cpp', 'base/google.h'))
- self.assertEquals((True, ''),
- f('base/internal/google_unittest.cpp',
- 'base/public/google.h'))
- self.assertEquals((True, 'xxx/yyy/'),
- f('xxx/yyy/base/internal/google_unittest.cpp',
- 'base/public/google.h'))
- self.assertEquals((True, 'xxx/yyy/'),
- f('xxx/yyy/base/google_unittest.cpp',
- 'base/public/google.h'))
- self.assertEquals((True, ''),
- f('base/google_unittest.cpp', 'base/google-inl.h'))
- self.assertEquals((True, '/home/build/google3/'),
- f('/home/build/google3/base/google.cpp', 'base/google.h'))
-
- self.assertEquals((False, ''),
- f('/home/build/google3/base/google.cpp', 'basu/google.h'))
- self.assertEquals((False, ''), f('a.cpp', 'b.h'))
-
- def test_cleanse_line(self):
- self.assertEquals('int foo = 0; ',
- cpp_style.cleanse_comments('int foo = 0; // danger!'))
- self.assertEquals('int o = 0;',
- cpp_style.cleanse_comments('int /* foo */ o = 0;'))
- self.assertEquals('foo(int a, int b);',
- cpp_style.cleanse_comments('foo(int a /* abc */, int b);'))
- self.assertEqual('f(a, b);',
- cpp_style.cleanse_comments('f(a, /* name */ b);'))
- self.assertEqual('f(a, b);',
- cpp_style.cleanse_comments('f(a /* name */, b);'))
- self.assertEqual('f(a, b);',
- cpp_style.cleanse_comments('f(a, /* name */b);'))
-
- def test_multi_line_comments(self):
- # missing explicit is bad
- self.assert_multi_line_lint(
- r'''int a = 0;
- /* multi-liner
- class Foo {
- Foo(int f); // should cause a lint warning in code
- }
- */ ''',
- '')
- self.assert_multi_line_lint(
- r'''/* int a = 0; multi-liner
- static const int b = 0;''',
- 'Could not find end of multi-line comment'
- ' [readability/multiline_comment] [5]')
- self.assert_multi_line_lint(r''' /* multi-line comment''',
- 'Could not find end of multi-line comment'
- ' [readability/multiline_comment] [5]')
- self.assert_multi_line_lint(r''' // /* comment, but not multi-line''', '')
-
- def test_multiline_strings(self):
- multiline_string_error_message = (
- 'Multi-line string ("...") found. This lint script doesn\'t '
- 'do well with such strings, and may give bogus warnings. They\'re '
- 'ugly and unnecessary, and you should use concatenation instead".'
- ' [readability/multiline_string] [5]')
-
- file_path = 'mydir/foo.cpp'
-
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data(file_path, 'cpp',
- ['const char* str = "This is a\\',
- ' multiline string.";'],
- error_collector)
- self.assertEquals(
- 2, # One per line.
- error_collector.result_list().count(multiline_string_error_message))
-
- # Test non-explicit single-argument constructors
- def test_explicit_single_argument_constructors(self):
- # missing explicit is bad
- self.assert_multi_line_lint(
- '''class Foo {
- Foo(int f);
- };''',
- 'Single-argument constructors should be marked explicit.'
- ' [runtime/explicit] [5]')
- # missing explicit is bad, even with whitespace
- self.assert_multi_line_lint(
- '''class Foo {
- Foo (int f);
- };''',
- ['Extra space before ( in function call [whitespace/parens] [4]',
- 'Single-argument constructors should be marked explicit.'
- ' [runtime/explicit] [5]'])
- # missing explicit, with distracting comment, is still bad
- self.assert_multi_line_lint(
- '''class Foo {
- Foo(int f); // simpler than Foo(blargh, blarg)
- };''',
- 'Single-argument constructors should be marked explicit.'
- ' [runtime/explicit] [5]')
- # missing explicit, with qualified classname
- self.assert_multi_line_lint(
- '''class Qualifier::AnotherOne::Foo {
- Foo(int f);
- };''',
- 'Single-argument constructors should be marked explicit.'
- ' [runtime/explicit] [5]')
- # structs are caught as well.
- self.assert_multi_line_lint(
- '''struct Foo {
- Foo(int f);
- };''',
- 'Single-argument constructors should be marked explicit.'
- ' [runtime/explicit] [5]')
- # Templatized classes are caught as well.
- self.assert_multi_line_lint(
- '''template<typename T> class Foo {
- Foo(int f);
- };''',
- 'Single-argument constructors should be marked explicit.'
- ' [runtime/explicit] [5]')
- # proper style is okay
- self.assert_multi_line_lint(
- '''class Foo {
- explicit Foo(int f);
- };''',
- '')
- # two argument constructor is okay
- self.assert_multi_line_lint(
- '''class Foo {
- Foo(int f, int b);
- };''',
- '')
- # two argument constructor, across two lines, is okay
- self.assert_multi_line_lint(
- '''class Foo {
- Foo(int f,
- int b);
- };''',
- '')
- # non-constructor (but similar name), is okay
- self.assert_multi_line_lint(
- '''class Foo {
- aFoo(int f);
- };''',
- '')
- # constructor with void argument is okay
- self.assert_multi_line_lint(
- '''class Foo {
- Foo(void);
- };''',
- '')
- # single argument method is okay
- self.assert_multi_line_lint(
- '''class Foo {
- Bar(int b);
- };''',
- '')
- # comments should be ignored
- self.assert_multi_line_lint(
- '''class Foo {
- // Foo(int f);
- };''',
- '')
- # single argument function following class definition is okay
- # (okay, it's not actually valid, but we don't want a false positive)
- self.assert_multi_line_lint(
- '''class Foo {
- Foo(int f, int b);
- };
- Foo(int f);''',
- '')
- # single argument function is okay
- self.assert_multi_line_lint(
- '''static Foo(int f);''',
- '')
- # single argument copy constructor is okay.
- self.assert_multi_line_lint(
- '''class Foo {
- Foo(const Foo&);
- };''',
- '')
- self.assert_multi_line_lint(
- '''class Foo {
- Foo(Foo&);
- };''',
- '')
-
- def test_slash_star_comment_on_single_line(self):
- self.assert_multi_line_lint(
- '''/* static */ Foo(int f);''',
- '')
- self.assert_multi_line_lint(
- '''/*/ static */ Foo(int f);''',
- '')
- self.assert_multi_line_lint(
- '''/*/ static Foo(int f);''',
- 'Could not find end of multi-line comment'
- ' [readability/multiline_comment] [5]')
- self.assert_multi_line_lint(
- ''' /*/ static Foo(int f);''',
- 'Could not find end of multi-line comment'
- ' [readability/multiline_comment] [5]')
- self.assert_multi_line_lint(
- ''' /**/ static Foo(int f);''',
- '')
-
- # Test suspicious usage of "if" like this:
- # if (a == b) {
- # DoSomething();
- # } if (a == c) { // Should be "else if".
- # DoSomething(); // This gets called twice if a == b && a == c.
- # }
- def test_suspicious_usage_of_if(self):
- self.assert_lint(
- ' if (a == b) {',
- '')
- self.assert_lint(
- ' } if (a == b) {',
- 'Did you mean "else if"? If not, start a new line for "if".'
- ' [readability/braces] [4]')
-
- # Test suspicious usage of memset. Specifically, a 0
- # as the final argument is almost certainly an error.
- def test_suspicious_usage_of_memset(self):
- # Normal use is okay.
- self.assert_lint(
- ' memset(buf, 0, sizeof(buf))',
- '')
-
- # A 0 as the final argument is almost certainly an error.
- self.assert_lint(
- ' memset(buf, sizeof(buf), 0)',
- 'Did you mean "memset(buf, 0, sizeof(buf))"?'
- ' [runtime/memset] [4]')
- self.assert_lint(
- ' memset(buf, xsize * ysize, 0)',
- 'Did you mean "memset(buf, 0, xsize * ysize)"?'
- ' [runtime/memset] [4]')
-
- # There is legitimate test code that uses this form.
- # This is okay since the second argument is a literal.
- self.assert_lint(
- " memset(buf, 'y', 0)",
- '')
- self.assert_lint(
- ' memset(buf, 4, 0)',
- '')
- self.assert_lint(
- ' memset(buf, -1, 0)',
- '')
- self.assert_lint(
- ' memset(buf, 0xF1, 0)',
- '')
- self.assert_lint(
- ' memset(buf, 0xcd, 0)',
- '')
-
- def test_check_posix_threading(self):
- self.assert_lint('sctime_r()', '')
- self.assert_lint('strtok_r()', '')
- self.assert_lint(' strtok_r(foo, ba, r)', '')
- self.assert_lint('brand()', '')
- self.assert_lint('_rand()', '')
- self.assert_lint('.rand()', '')
- self.assert_lint('>rand()', '')
- self.assert_lint('rand()',
- 'Consider using rand_r(...) instead of rand(...)'
- ' for improved thread safety.'
- ' [runtime/threadsafe_fn] [2]')
- self.assert_lint('strtok()',
- 'Consider using strtok_r(...) '
- 'instead of strtok(...)'
- ' for improved thread safety.'
- ' [runtime/threadsafe_fn] [2]')
-
- # Test potential format string bugs like printf(foo).
- def test_format_strings(self):
- self.assert_lint('printf("foo")', '')
- self.assert_lint('printf("foo: %s", foo)', '')
- self.assert_lint('DocidForPrintf(docid)', '') # Should not trigger.
- self.assert_lint(
- 'printf(foo)',
- 'Potential format string bug. Do printf("%s", foo) instead.'
- ' [runtime/printf] [4]')
- self.assert_lint(
- 'printf(foo.c_str())',
- 'Potential format string bug. '
- 'Do printf("%s", foo.c_str()) instead.'
- ' [runtime/printf] [4]')
- self.assert_lint(
- 'printf(foo->c_str())',
- 'Potential format string bug. '
- 'Do printf("%s", foo->c_str()) instead.'
- ' [runtime/printf] [4]')
- self.assert_lint(
- 'StringPrintf(foo)',
- 'Potential format string bug. Do StringPrintf("%s", foo) instead.'
- ''
- ' [runtime/printf] [4]')
-
- # Variable-length arrays are not permitted.
- def test_variable_length_array_detection(self):
- errmsg = ('Do not use variable-length arrays. Use an appropriately named '
- "('k' followed by CamelCase) compile-time constant for the size."
- ' [runtime/arrays] [1]')
-
- self.assert_lint('int a[any_old_variable];', errmsg)
- self.assert_lint('int doublesize[some_var * 2];', errmsg)
- self.assert_lint('int a[afunction()];', errmsg)
- self.assert_lint('int a[function(kMaxFooBars)];', errmsg)
- self.assert_lint('bool aList[items_->size()];', errmsg)
- self.assert_lint('namespace::Type buffer[len+1];', errmsg)
-
- self.assert_lint('int a[64];', '')
- self.assert_lint('int a[0xFF];', '')
- self.assert_lint('int first[256], second[256];', '')
- self.assert_lint('int arrayName[kCompileTimeConstant];', '')
- self.assert_lint('char buf[somenamespace::kBufSize];', '')
- self.assert_lint('int arrayName[ALL_CAPS];', '')
- self.assert_lint('AClass array1[foo::bar::ALL_CAPS];', '')
- self.assert_lint('int a[kMaxStrLen + 1];', '')
- self.assert_lint('int a[sizeof(foo)];', '')
- self.assert_lint('int a[sizeof(*foo)];', '')
- self.assert_lint('int a[sizeof foo];', '')
- self.assert_lint('int a[sizeof(struct Foo)];', '')
- self.assert_lint('int a[128 - sizeof(const bar)];', '')
- self.assert_lint('int a[(sizeof(foo) * 4)];', '')
- self.assert_lint('int a[(arraysize(fixed_size_array)/2) << 1];', 'Missing spaces around / [whitespace/operators] [3]')
- self.assert_lint('delete a[some_var];', '')
- self.assert_lint('return a[some_var];', '')
-
- # Brace usage
- def test_braces(self):
- # Braces shouldn't be followed by a ; unless they're defining a struct
- # or initializing an array
- self.assert_lint('int a[3] = { 1, 2, 3 };', '')
- self.assert_lint(
- '''const int foo[] =
- {1, 2, 3 };''',
- '')
- # For single line, unmatched '}' with a ';' is ignored (not enough context)
- self.assert_multi_line_lint(
- '''int a[3] = { 1,
- 2,
- 3 };''',
- '')
- self.assert_multi_line_lint(
- '''int a[2][3] = { { 1, 2 },
- { 3, 4 } };''',
- '')
- self.assert_multi_line_lint(
- '''int a[2][3] =
- { { 1, 2 },
- { 3, 4 } };''',
- '')
-
- # CHECK/EXPECT_TRUE/EXPECT_FALSE replacements
- def test_check_check(self):
- self.assert_lint('CHECK(x == 42)',
- 'Consider using CHECK_EQ instead of CHECK(a == b)'
- ' [readability/check] [2]')
- self.assert_lint('CHECK(x != 42)',
- 'Consider using CHECK_NE instead of CHECK(a != b)'
- ' [readability/check] [2]')
- self.assert_lint('CHECK(x >= 42)',
- 'Consider using CHECK_GE instead of CHECK(a >= b)'
- ' [readability/check] [2]')
- self.assert_lint('CHECK(x > 42)',
- 'Consider using CHECK_GT instead of CHECK(a > b)'
- ' [readability/check] [2]')
- self.assert_lint('CHECK(x <= 42)',
- 'Consider using CHECK_LE instead of CHECK(a <= b)'
- ' [readability/check] [2]')
- self.assert_lint('CHECK(x < 42)',
- 'Consider using CHECK_LT instead of CHECK(a < b)'
- ' [readability/check] [2]')
-
- self.assert_lint('DCHECK(x == 42)',
- 'Consider using DCHECK_EQ instead of DCHECK(a == b)'
- ' [readability/check] [2]')
- self.assert_lint('DCHECK(x != 42)',
- 'Consider using DCHECK_NE instead of DCHECK(a != b)'
- ' [readability/check] [2]')
- self.assert_lint('DCHECK(x >= 42)',
- 'Consider using DCHECK_GE instead of DCHECK(a >= b)'
- ' [readability/check] [2]')
- self.assert_lint('DCHECK(x > 42)',
- 'Consider using DCHECK_GT instead of DCHECK(a > b)'
- ' [readability/check] [2]')
- self.assert_lint('DCHECK(x <= 42)',
- 'Consider using DCHECK_LE instead of DCHECK(a <= b)'
- ' [readability/check] [2]')
- self.assert_lint('DCHECK(x < 42)',
- 'Consider using DCHECK_LT instead of DCHECK(a < b)'
- ' [readability/check] [2]')
-
- self.assert_lint(
- 'EXPECT_TRUE("42" == x)',
- 'Consider using EXPECT_EQ instead of EXPECT_TRUE(a == b)'
- ' [readability/check] [2]')
- self.assert_lint(
- 'EXPECT_TRUE("42" != x)',
- 'Consider using EXPECT_NE instead of EXPECT_TRUE(a != b)'
- ' [readability/check] [2]')
- self.assert_lint(
- 'EXPECT_TRUE(+42 >= x)',
- 'Consider using EXPECT_GE instead of EXPECT_TRUE(a >= b)'
- ' [readability/check] [2]')
- self.assert_lint(
- 'EXPECT_TRUE_M(-42 > x)',
- 'Consider using EXPECT_GT_M instead of EXPECT_TRUE_M(a > b)'
- ' [readability/check] [2]')
- self.assert_lint(
- 'EXPECT_TRUE_M(42U <= x)',
- 'Consider using EXPECT_LE_M instead of EXPECT_TRUE_M(a <= b)'
- ' [readability/check] [2]')
- self.assert_lint(
- 'EXPECT_TRUE_M(42L < x)',
- 'Consider using EXPECT_LT_M instead of EXPECT_TRUE_M(a < b)'
- ' [readability/check] [2]')
-
- self.assert_lint(
- 'EXPECT_FALSE(x == 42)',
- 'Consider using EXPECT_NE instead of EXPECT_FALSE(a == b)'
- ' [readability/check] [2]')
- self.assert_lint(
- 'EXPECT_FALSE(x != 42)',
- 'Consider using EXPECT_EQ instead of EXPECT_FALSE(a != b)'
- ' [readability/check] [2]')
- self.assert_lint(
- 'EXPECT_FALSE(x >= 42)',
- 'Consider using EXPECT_LT instead of EXPECT_FALSE(a >= b)'
- ' [readability/check] [2]')
- self.assert_lint(
- 'ASSERT_FALSE(x > 42)',
- 'Consider using ASSERT_LE instead of ASSERT_FALSE(a > b)'
- ' [readability/check] [2]')
- self.assert_lint(
- 'ASSERT_FALSE(x <= 42)',
- 'Consider using ASSERT_GT instead of ASSERT_FALSE(a <= b)'
- ' [readability/check] [2]')
- self.assert_lint(
- 'ASSERT_FALSE_M(x < 42)',
- 'Consider using ASSERT_GE_M instead of ASSERT_FALSE_M(a < b)'
- ' [readability/check] [2]')
-
- self.assert_lint('CHECK(some_iterator == obj.end())', '')
- self.assert_lint('EXPECT_TRUE(some_iterator == obj.end())', '')
- self.assert_lint('EXPECT_FALSE(some_iterator == obj.end())', '')
-
- self.assert_lint('CHECK(CreateTestFile(dir, (1 << 20)));', '')
- self.assert_lint('CHECK(CreateTestFile(dir, (1 >> 20)));', '')
-
- self.assert_lint('CHECK(x<42)',
- ['Missing spaces around <'
- ' [whitespace/operators] [3]',
- 'Consider using CHECK_LT instead of CHECK(a < b)'
- ' [readability/check] [2]'])
- self.assert_lint('CHECK(x>42)',
- 'Consider using CHECK_GT instead of CHECK(a > b)'
- ' [readability/check] [2]')
-
- self.assert_lint(
- ' EXPECT_TRUE(42 < x) // Random comment.',
- 'Consider using EXPECT_LT instead of EXPECT_TRUE(a < b)'
- ' [readability/check] [2]')
- self.assert_lint(
- 'EXPECT_TRUE( 42 < x )',
- ['Extra space after ( in function call'
- ' [whitespace/parens] [4]',
- 'Consider using EXPECT_LT instead of EXPECT_TRUE(a < b)'
- ' [readability/check] [2]'])
- self.assert_lint(
- 'CHECK("foo" == "foo")',
- 'Consider using CHECK_EQ instead of CHECK(a == b)'
- ' [readability/check] [2]')
-
- self.assert_lint('CHECK_EQ("foo", "foo")', '')
-
- def test_brace_at_begin_of_line(self):
- self.assert_lint('{',
- 'This { should be at the end of the previous line'
- ' [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- '#endif\n'
- '{\n'
- '}\n',
- '')
- self.assert_multi_line_lint(
- 'if (condition) {',
- '')
- self.assert_multi_line_lint(
- ' MACRO1(macroArg) {',
- '')
- self.assert_multi_line_lint(
- 'ACCESSOR_GETTER(MessageEventPorts) {',
- 'Place brace on its own line for function definitions. [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- 'int foo() {',
- 'Place brace on its own line for function definitions. [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- 'int foo() const {',
- 'Place brace on its own line for function definitions. [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- 'int foo() const\n'
- '{\n'
- '}\n',
- '')
- self.assert_multi_line_lint(
- 'if (condition\n'
- ' && condition2\n'
- ' && condition3) {\n'
- '}\n',
- '')
-
- def test_mismatching_spaces_in_parens(self):
- self.assert_lint('if (foo ) {', 'Extra space before ) in if'
- ' [whitespace/parens] [5]')
- self.assert_lint('switch ( foo) {', 'Extra space after ( in switch'
- ' [whitespace/parens] [5]')
- self.assert_lint('for (foo; ba; bar ) {', 'Extra space before ) in for'
- ' [whitespace/parens] [5]')
- self.assert_lint('for ((foo); (ba); (bar) ) {', 'Extra space before ) in for'
- ' [whitespace/parens] [5]')
- self.assert_lint('for (; foo; bar) {', '')
- self.assert_lint('for (; (foo); (bar)) {', '')
- self.assert_lint('for ( ; foo; bar) {', '')
- self.assert_lint('for ( ; (foo); (bar)) {', '')
- self.assert_lint('for ( ; foo; bar ) {', 'Extra space before ) in for'
- ' [whitespace/parens] [5]')
- self.assert_lint('for ( ; (foo); (bar) ) {', 'Extra space before ) in for'
- ' [whitespace/parens] [5]')
- self.assert_lint('for (foo; bar; ) {', '')
- self.assert_lint('for ((foo); (bar); ) {', '')
- self.assert_lint('foreach (foo, foos ) {', 'Extra space before ) in foreach'
- ' [whitespace/parens] [5]')
- self.assert_lint('foreach ( foo, foos) {', 'Extra space after ( in foreach'
- ' [whitespace/parens] [5]')
- self.assert_lint('while ( foo) {', 'Extra space after ( in while'
- ' [whitespace/parens] [5]')
-
- def test_spacing_for_fncall(self):
- self.assert_lint('if (foo) {', '')
- self.assert_lint('for (foo;bar;baz) {', '')
- self.assert_lint('foreach (foo, foos) {', '')
- self.assert_lint('while (foo) {', '')
- self.assert_lint('switch (foo) {', '')
- self.assert_lint('new (RenderArena()) RenderInline(document())', '')
- self.assert_lint('foo( bar)', 'Extra space after ( in function call'
- ' [whitespace/parens] [4]')
- self.assert_lint('foobar( \\', '')
- self.assert_lint('foobar( \\', '')
- self.assert_lint('( a + b)', 'Extra space after ('
- ' [whitespace/parens] [2]')
- self.assert_lint('((a+b))', '')
- self.assert_lint('foo (foo)', 'Extra space before ( in function call'
- ' [whitespace/parens] [4]')
- self.assert_lint('typedef foo (*foo)(foo)', '')
- self.assert_lint('typedef foo (*foo12bar_)(foo)', '')
- self.assert_lint('typedef foo (Foo::*bar)(foo)', '')
- self.assert_lint('foo (Foo::*bar)(',
- 'Extra space before ( in function call'
- ' [whitespace/parens] [4]')
- self.assert_lint('typedef foo (Foo::*bar)(', '')
- self.assert_lint('(foo)(bar)', '')
- self.assert_lint('Foo (*foo)(bar)', '')
- self.assert_lint('Foo (*foo)(Bar bar,', '')
- self.assert_lint('char (*p)[sizeof(foo)] = &foo', '')
- self.assert_lint('char (&ref)[sizeof(foo)] = &foo', '')
- self.assert_lint('const char32 (*table[])[6];', '')
-
- def test_spacing_before_braces(self):
- self.assert_lint('if (foo){', 'Missing space before {'
- ' [whitespace/braces] [5]')
- self.assert_lint('for{', 'Missing space before {'
- ' [whitespace/braces] [5]')
- self.assert_lint('for {', '')
- self.assert_lint('EXPECT_DEBUG_DEATH({', '')
-
- def test_spacing_around_else(self):
- self.assert_lint('}else {', 'Missing space before else'
- ' [whitespace/braces] [5]')
- self.assert_lint('} else{', 'Missing space before {'
- ' [whitespace/braces] [5]')
- self.assert_lint('} else {', '')
- self.assert_lint('} else if', '')
-
- def test_spacing_for_binary_ops(self):
- self.assert_lint('if (foo<=bar) {', 'Missing spaces around <='
- ' [whitespace/operators] [3]')
- self.assert_lint('if (foo<bar) {', 'Missing spaces around <'
- ' [whitespace/operators] [3]')
- self.assert_lint('if (foo<bar->baz) {', 'Missing spaces around <'
- ' [whitespace/operators] [3]')
- self.assert_lint('if (foo<bar->bar) {', 'Missing spaces around <'
- ' [whitespace/operators] [3]')
- self.assert_lint('typedef hash_map<Foo, Bar', 'Missing spaces around <'
- ' [whitespace/operators] [3]')
- self.assert_lint('typedef hash_map<FoooooType, BaaaaarType,', '')
- self.assert_lint('a<Foo> t+=b;', 'Missing spaces around +='
- ' [whitespace/operators] [3]')
- self.assert_lint('a<Foo> t-=b;', 'Missing spaces around -='
- ' [whitespace/operators] [3]')
- self.assert_lint('a<Foo*> t*=b;', 'Missing spaces around *='
- ' [whitespace/operators] [3]')
- self.assert_lint('a<Foo*> t/=b;', 'Missing spaces around /='
- ' [whitespace/operators] [3]')
- self.assert_lint('a<Foo*> t|=b;', 'Missing spaces around |='
- ' [whitespace/operators] [3]')
- self.assert_lint('a<Foo*> t&=b;', 'Missing spaces around &='
- ' [whitespace/operators] [3]')
- self.assert_lint('a<Foo*> t<<=b;', 'Missing spaces around <<='
- ' [whitespace/operators] [3]')
- self.assert_lint('a<Foo*> t>>=b;', 'Missing spaces around >>='
- ' [whitespace/operators] [3]')
- self.assert_lint('a<Foo*> t>>=&b|c;', 'Missing spaces around >>='
- ' [whitespace/operators] [3]')
- self.assert_lint('a<Foo*> t<<=*b/c;', 'Missing spaces around <<='
- ' [whitespace/operators] [3]')
- self.assert_lint('a<Foo> t -= b;', '')
- self.assert_lint('a<Foo> t += b;', '')
- self.assert_lint('a<Foo*> t *= b;', '')
- self.assert_lint('a<Foo*> t /= b;', '')
- self.assert_lint('a<Foo*> t |= b;', '')
- self.assert_lint('a<Foo*> t &= b;', '')
- self.assert_lint('a<Foo*> t <<= b;', '')
- self.assert_lint('a<Foo*> t >>= b;', '')
- self.assert_lint('a<Foo*> t >>= &b|c;', 'Missing spaces around |'
- ' [whitespace/operators] [3]')
- self.assert_lint('a<Foo*> t <<= *b/c;', 'Missing spaces around /'
- ' [whitespace/operators] [3]')
- self.assert_lint('a<Foo*> t <<= b/c; //Test', [
- 'Should have a space between // and comment '
- '[whitespace/comments] [4]', 'Missing'
- ' spaces around / [whitespace/operators] [3]'])
- self.assert_lint('a<Foo*> t <<= b||c; //Test', ['One space before end'
- ' of line comments [whitespace/comments] [5]',
- 'Should have a space between // and comment '
- '[whitespace/comments] [4]',
- 'Missing spaces around || [whitespace/operators] [3]'])
- self.assert_lint('a<Foo*> t <<= b&&c; // Test', 'Missing spaces around'
- ' && [whitespace/operators] [3]')
- self.assert_lint('a<Foo*> t <<= b&&&c; // Test', 'Missing spaces around'
- ' && [whitespace/operators] [3]')
- self.assert_lint('a<Foo*> t <<= b&&*c; // Test', 'Missing spaces around'
- ' && [whitespace/operators] [3]')
- self.assert_lint('a<Foo*> t <<= b && *c; // Test', '')
- self.assert_lint('a<Foo*> t <<= b && &c; // Test', '')
- self.assert_lint('a<Foo*> t <<= b || &c; /*Test', 'Complex multi-line '
- '/*...*/-style comment found. Lint may give bogus '
- 'warnings. Consider replacing these with //-style'
- ' comments, with #if 0...#endif, or with more clearly'
- ' structured multi-line comments. [readability/multiline_comment] [5]')
- self.assert_lint('a<Foo&> t <<= &b | &c;', '')
- self.assert_lint('a<Foo*> t <<= &b & &c; // Test', '')
- self.assert_lint('a<Foo*> t <<= *b / &c; // Test', '')
- self.assert_lint('if (a=b == 1)', 'Missing spaces around = [whitespace/operators] [4]')
- self.assert_lint('a = 1<<20', 'Missing spaces around << [whitespace/operators] [3]')
- self.assert_lint('if (a = b == 1)', '')
- self.assert_lint('a = 1 << 20', '')
- self.assert_multi_line_lint('#include "config.h"\n#include <sys/io.h>\n',
- '')
- self.assert_multi_line_lint('#include "config.h"\n#import <foo/bar.h>\n',
- '')
-
- def test_operator_methods(self):
- self.assert_lint('String operator+(const String&, const String&);', '')
- self.assert_lint('bool operator==(const String&, const String&);', '')
- self.assert_lint('String& operator-=(const String&, const String&);', '')
- self.assert_lint('String& operator+=(const String&, const String&);', '')
- self.assert_lint('String& operator*=(const String&, const String&);', '')
- self.assert_lint('String& operator%=(const String&, const String&);', '')
- self.assert_lint('String& operator&=(const String&, const String&);', '')
- self.assert_lint('String& operator<<=(const String&, const String&);', '')
- self.assert_lint('String& operator>>=(const String&, const String&);', '')
- self.assert_lint('String& operator|=(const String&, const String&);', '')
- self.assert_lint('String& operator^=(const String&, const String&);', '')
-
- def test_spacing_before_last_semicolon(self):
- self.assert_lint('call_function() ;',
- 'Extra space before last semicolon. If this should be an '
- 'empty statement, use { } instead.'
- ' [whitespace/semicolon] [5]')
- self.assert_lint('while (true) ;',
- 'Extra space before last semicolon. If this should be an '
- 'empty statement, use { } instead.'
- ' [whitespace/semicolon] [5]')
- self.assert_lint('default:;',
- 'Semicolon defining empty statement. Use { } instead.'
- ' [whitespace/semicolon] [5]')
- self.assert_lint(' ;',
- 'Line contains only semicolon. If this should be an empty '
- 'statement, use { } instead.'
- ' [whitespace/semicolon] [5]')
- self.assert_lint('for (int i = 0; ;', '')
-
- # Static or global STL strings.
- def test_static_or_global_stlstrings(self):
- self.assert_lint('string foo;',
- 'For a static/global string constant, use a C style '
- 'string instead: "char foo[]".'
- ' [runtime/string] [4]')
- self.assert_lint('string kFoo = "hello"; // English',
- 'For a static/global string constant, use a C style '
- 'string instead: "char kFoo[]".'
- ' [runtime/string] [4]')
- self.assert_lint('static string foo;',
- 'For a static/global string constant, use a C style '
- 'string instead: "static char foo[]".'
- ' [runtime/string] [4]')
- self.assert_lint('static const string foo;',
- 'For a static/global string constant, use a C style '
- 'string instead: "static const char foo[]".'
- ' [runtime/string] [4]')
- self.assert_lint('string Foo::bar;',
- 'For a static/global string constant, use a C style '
- 'string instead: "char Foo::bar[]".'
- ' [runtime/string] [4]')
- # Rare case.
- self.assert_lint('string foo("foobar");',
- 'For a static/global string constant, use a C style '
- 'string instead: "char foo[]".'
- ' [runtime/string] [4]')
- # Should not catch local or member variables.
- self.assert_lint(' string foo', '')
- # Should not catch functions.
- self.assert_lint('string EmptyString() { return ""; }', '')
- self.assert_lint('string EmptyString () { return ""; }', '')
- self.assert_lint('string VeryLongNameFunctionSometimesEndsWith(\n'
- ' VeryLongNameType very_long_name_variable) {}', '')
- self.assert_lint('template<>\n'
- 'string FunctionTemplateSpecialization<SomeType>(\n'
- ' int x) { return ""; }', '')
- self.assert_lint('template<>\n'
- 'string FunctionTemplateSpecialization<vector<A::B>* >(\n'
- ' int x) { return ""; }', '')
-
- # should not catch methods of template classes.
- self.assert_lint('string Class<Type>::Method() const\n'
- '{\n'
- ' return "";\n'
- '}\n', '')
- self.assert_lint('string Class<Type>::Method(\n'
- ' int arg) const\n'
- '{\n'
- ' return "";\n'
- '}\n', '')
-
- def test_no_spaces_in_function_calls(self):
- self.assert_lint('TellStory(1, 3);',
- '')
- self.assert_lint('TellStory(1, 3 );',
- 'Extra space before )'
- ' [whitespace/parens] [2]')
- self.assert_lint('TellStory(1 /* wolf */, 3 /* pigs */);',
- '')
- self.assert_multi_line_lint('#endif\n );',
- '')
-
- def test_two_spaces_between_code_and_comments(self):
- self.assert_lint('} // namespace foo',
- '')
- self.assert_lint('}// namespace foo',
- 'One space before end of line comments'
- ' [whitespace/comments] [5]')
- self.assert_lint('printf("foo"); // Outside quotes.',
- '')
- self.assert_lint('int i = 0; // Having one space is fine.','')
- self.assert_lint('int i = 0; // Having two spaces is bad.',
- 'One space before end of line comments'
- ' [whitespace/comments] [5]')
- self.assert_lint('int i = 0; // Having three spaces is bad.',
- 'One space before end of line comments'
- ' [whitespace/comments] [5]')
- self.assert_lint('// Top level comment', '')
- self.assert_lint(' // Line starts with four spaces.', '')
- self.assert_lint('foo();\n'
- '{ // A scope is opening.', '')
- self.assert_lint(' foo();\n'
- ' { // An indented scope is opening.', '')
- self.assert_lint('if (foo) { // not a pure scope',
- '')
- self.assert_lint('printf("// In quotes.")', '')
- self.assert_lint('printf("\\"%s // In quotes.")', '')
- self.assert_lint('printf("%s", "// In quotes.")', '')
-
- def test_space_after_comment_marker(self):
- self.assert_lint('//', '')
- self.assert_lint('//x', 'Should have a space between // and comment'
- ' [whitespace/comments] [4]')
- self.assert_lint('// x', '')
- self.assert_lint('//----', '')
- self.assert_lint('//====', '')
- self.assert_lint('//////', '')
- self.assert_lint('////// x', '')
- self.assert_lint('/// x', '')
- self.assert_lint('////x', 'Should have a space between // and comment'
- ' [whitespace/comments] [4]')
-
- def test_newline_at_eof(self):
- def do_test(self, data, is_missing_eof):
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data('foo.cpp', 'cpp', data.split('\n'),
- error_collector)
- # The warning appears only once.
- self.assertEquals(
- int(is_missing_eof),
- error_collector.results().count(
- 'Could not find a newline character at the end of the file.'
- ' [whitespace/ending_newline] [5]'))
-
- do_test(self, '// Newline\n// at EOF\n', False)
- do_test(self, '// No newline\n// at EOF', True)
-
- def test_invalid_utf8(self):
- def do_test(self, raw_bytes, has_invalid_utf8):
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data('foo.cpp', 'cpp',
- unicode(raw_bytes, 'utf8', 'replace').split('\n'),
- error_collector)
- # The warning appears only once.
- self.assertEquals(
- int(has_invalid_utf8),
- error_collector.results().count(
- 'Line contains invalid UTF-8'
- ' (or Unicode replacement character).'
- ' [readability/utf8] [5]'))
-
- do_test(self, 'Hello world\n', False)
- do_test(self, '\xe9\x8e\xbd\n', False)
- do_test(self, '\xe9x\x8e\xbd\n', True)
- # This is the encoding of the replacement character itself (which
- # you can see by evaluating codecs.getencoder('utf8')(u'\ufffd')).
- do_test(self, '\xef\xbf\xbd\n', True)
-
- def test_is_blank_line(self):
- self.assert_(cpp_style.is_blank_line(''))
- self.assert_(cpp_style.is_blank_line(' '))
- self.assert_(cpp_style.is_blank_line(' \t\r\n'))
- self.assert_(not cpp_style.is_blank_line('int a;'))
- self.assert_(not cpp_style.is_blank_line('{'))
-
- def test_blank_lines_check(self):
- self.assert_blank_lines_check(['{\n', '\n', '\n', '}\n'], 1, 1)
- self.assert_blank_lines_check([' if (foo) {\n', '\n', ' }\n'], 1, 1)
- self.assert_blank_lines_check(
- ['\n', '// {\n', '\n', '\n', '// Comment\n', '{\n', '}\n'], 0, 0)
- self.assert_blank_lines_check(['\n', 'run("{");\n', '\n'], 0, 0)
- self.assert_blank_lines_check(['\n', ' if (foo) { return 0; }\n', '\n'], 0, 0)
-
- def test_allow_blank_line_before_closing_namespace(self):
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data('foo.cpp', 'cpp',
- ['namespace {', '', '} // namespace'],
- error_collector)
- self.assertEquals(0, error_collector.results().count(
- 'Blank line at the end of a code block. Is this needed?'
- ' [whitespace/blank_line] [3]'))
-
- def test_allow_blank_line_before_if_else_chain(self):
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data('foo.cpp', 'cpp',
- ['if (hoge) {',
- '', # No warning
- '} else if (piyo) {',
- '', # No warning
- '} else if (piyopiyo) {',
- ' hoge = true;', # No warning
- '} else {',
- '', # Warning on this line
- '}'],
- error_collector)
- self.assertEquals(1, error_collector.results().count(
- 'Blank line at the end of a code block. Is this needed?'
- ' [whitespace/blank_line] [3]'))
-
- def test_else_on_same_line_as_closing_braces(self):
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data('foo.cpp', 'cpp',
- ['if (hoge) {',
- '',
- '}',
- ' else {' # Warning on this line
- '',
- '}'],
- error_collector)
- self.assertEquals(1, error_collector.results().count(
- 'An else should appear on the same line as the preceding }'
- ' [whitespace/newline] [4]'))
-
- def test_else_clause_not_on_same_line_as_else(self):
- self.assert_lint(' else DoSomethingElse();',
- 'Else clause should never be on same line as else '
- '(use 2 lines) [whitespace/newline] [4]')
- self.assert_lint(' else ifDoSomethingElse();',
- 'Else clause should never be on same line as else '
- '(use 2 lines) [whitespace/newline] [4]')
- self.assert_lint(' else if (blah) {', '')
- self.assert_lint(' variable_ends_in_else = true;', '')
-
- def test_comma(self):
- self.assert_lint('a = f(1,2);',
- 'Missing space after , [whitespace/comma] [3]')
- self.assert_lint('int tmp=a,a=b,b=tmp;',
- ['Missing spaces around = [whitespace/operators] [4]',
- 'Missing space after , [whitespace/comma] [3]'])
- self.assert_lint('f(a, /* name */ b);', '')
- self.assert_lint('f(a, /* name */b);', '')
-
- def test_declaration(self):
- self.assert_lint('int a;', '')
- self.assert_lint('int a;', 'Extra space between int and a [whitespace/declaration] [3]')
- self.assert_lint('int* a;', 'Extra space between int* and a [whitespace/declaration] [3]')
- self.assert_lint('else if { }', '')
- self.assert_lint('else if { }', 'Extra space between else and if [whitespace/declaration] [3]')
-
- def test_pointer_reference_marker_location(self):
- self.assert_lint('int* b;', '', 'foo.cpp')
- self.assert_lint('int *b;',
- 'Declaration has space between type name and * in int *b [whitespace/declaration] [3]',
- 'foo.cpp')
- self.assert_lint('return *b;', '', 'foo.cpp')
- self.assert_lint('delete *b;', '', 'foo.cpp')
- self.assert_lint('int *b;', '', 'foo.c')
- self.assert_lint('int* b;',
- 'Declaration has space between * and variable name in int* b [whitespace/declaration] [3]',
- 'foo.c')
- self.assert_lint('int& b;', '', 'foo.cpp')
- self.assert_lint('int &b;',
- 'Declaration has space between type name and & in int &b [whitespace/declaration] [3]',
- 'foo.cpp')
- self.assert_lint('return &b;', '', 'foo.cpp')
-
- def test_indent(self):
- self.assert_lint('static int noindent;', '')
- self.assert_lint(' int fourSpaceIndent;', '')
- self.assert_lint(' int oneSpaceIndent;',
- 'Weird number of spaces at line-start. '
- 'Are you using a 4-space indent? [whitespace/indent] [3]')
- self.assert_lint(' int threeSpaceIndent;',
- 'Weird number of spaces at line-start. '
- 'Are you using a 4-space indent? [whitespace/indent] [3]')
- self.assert_lint(' char* oneSpaceIndent = "public:";',
- 'Weird number of spaces at line-start. '
- 'Are you using a 4-space indent? [whitespace/indent] [3]')
- self.assert_lint(' public:', '')
- self.assert_lint(' public:', '')
- self.assert_lint(' public:', '')
-
- def test_label(self):
- self.assert_lint('public:',
- 'Labels should always be indented at least one space. '
- 'If this is a member-initializer list in a constructor, '
- 'the colon should be on the line after the definition '
- 'header. [whitespace/labels] [4]')
- self.assert_lint(' public:', '')
- self.assert_lint(' public:', '')
- self.assert_lint(' public:', '')
- self.assert_lint(' public:', '')
- self.assert_lint(' public:', '')
-
- def test_not_alabel(self):
- self.assert_lint('MyVeryLongNamespace::MyVeryLongClassName::', '')
-
- def test_tab(self):
- self.assert_lint('\tint a;',
- 'Tab found; better to use spaces [whitespace/tab] [1]')
- self.assert_lint('int a = 5;\t// set a to 5',
- 'Tab found; better to use spaces [whitespace/tab] [1]')
-
- def test_unnamed_namespaces_in_headers(self):
- self.assert_language_rules_check(
- 'foo.h', 'namespace {',
- 'Do not use unnamed namespaces in header files. See'
- ' http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces'
- ' for more information. [build/namespaces] [4]')
- # namespace registration macros are OK.
- self.assert_language_rules_check('foo.h', 'namespace { \\', '')
- # named namespaces are OK.
- self.assert_language_rules_check('foo.h', 'namespace foo {', '')
- self.assert_language_rules_check('foo.h', 'namespace foonamespace {', '')
- self.assert_language_rules_check('foo.cpp', 'namespace {', '')
- self.assert_language_rules_check('foo.cpp', 'namespace foo {', '')
-
- def test_build_class(self):
- # Test that the linter can parse to the end of class definitions,
- # and that it will report when it can't.
- # Use multi-line linter because it performs the ClassState check.
- self.assert_multi_line_lint(
- 'class Foo {',
- 'Failed to find complete declaration of class Foo'
- ' [build/class] [5]')
- # Don't warn on forward declarations of various types.
- self.assert_multi_line_lint(
- 'class Foo;',
- '')
- self.assert_multi_line_lint(
- '''struct Foo*
- foo = NewFoo();''',
- '')
- # Here is an example where the linter gets confused, even though
- # the code doesn't violate the style guide.
- self.assert_multi_line_lint(
- '''class Foo
- #ifdef DERIVE_FROM_GOO
- : public Goo {
- #else
- : public Hoo {
- #endif
- };''',
- 'Failed to find complete declaration of class Foo'
- ' [build/class] [5]')
-
- def test_build_end_comment(self):
- # The crosstool compiler we currently use will fail to compile the
- # code in this test, so we might consider removing the lint check.
- self.assert_lint('#endif Not a comment',
- 'Uncommented text after #endif is non-standard.'
- ' Use a comment.'
- ' [build/endif_comment] [5]')
-
- def test_build_forward_decl(self):
- # The crosstool compiler we currently use will fail to compile the
- # code in this test, so we might consider removing the lint check.
- self.assert_lint('class Foo::Goo;',
- 'Inner-style forward declarations are invalid.'
- ' Remove this line.'
- ' [build/forward_decl] [5]')
-
- def test_build_header_guard(self):
- file_path = 'mydir/Foo.h'
-
- # We can't rely on our internal stuff to get a sane path on the open source
- # side of things, so just parse out the suggested header guard. This
- # doesn't allow us to test the suggested header guard, but it does let us
- # test all the other header tests.
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data(file_path, 'h', [], error_collector)
- expected_guard = ''
- matcher = re.compile(
- 'No \#ifndef header guard found\, suggested CPP variable is\: ([A-Za-z_0-9]+) ')
- for error in error_collector.result_list():
- matches = matcher.match(error)
- if matches:
- expected_guard = matches.group(1)
- break
-
- # Make sure we extracted something for our header guard.
- self.assertNotEqual(expected_guard, '')
-
- # Wrong guard
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data(file_path, 'h',
- ['#ifndef FOO_H', '#define FOO_H'], error_collector)
- self.assertEquals(
- 1,
- error_collector.result_list().count(
- '#ifndef header guard has wrong style, please use: %s'
- ' [build/header_guard] [5]' % expected_guard),
- error_collector.result_list())
-
- # No define
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data(file_path, 'h',
- ['#ifndef %s' % expected_guard], error_collector)
- self.assertEquals(
- 1,
- error_collector.result_list().count(
- 'No #ifndef header guard found, suggested CPP variable is: %s'
- ' [build/header_guard] [5]' % expected_guard),
- error_collector.result_list())
-
- # Mismatched define
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data(file_path, 'h',
- ['#ifndef %s' % expected_guard,
- '#define FOO_H'],
- error_collector)
- self.assertEquals(
- 1,
- error_collector.result_list().count(
- 'No #ifndef header guard found, suggested CPP variable is: %s'
- ' [build/header_guard] [5]' % expected_guard),
- error_collector.result_list())
-
- # No header guard errors
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data(file_path, 'h',
- ['#ifndef %s' % expected_guard,
- '#define %s' % expected_guard,
- '#endif // %s' % expected_guard],
- error_collector)
- for line in error_collector.result_list():
- if line.find('build/header_guard') != -1:
- self.fail('Unexpected error: %s' % line)
-
- # Completely incorrect header guard
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data(file_path, 'h',
- ['#ifndef FOO',
- '#define FOO',
- '#endif // FOO'],
- error_collector)
- self.assertEquals(
- 1,
- error_collector.result_list().count(
- '#ifndef header guard has wrong style, please use: %s'
- ' [build/header_guard] [5]' % expected_guard),
- error_collector.result_list())
-
- # Special case for flymake
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data('mydir/Foo_flymake.h', 'h',
- ['#ifndef %s' % expected_guard,
- '#define %s' % expected_guard,
- '#endif // %s' % expected_guard],
- error_collector)
- for line in error_collector.result_list():
- if line.find('build/header_guard') != -1:
- self.fail('Unexpected error: %s' % line)
-
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data('mydir/Foo_flymake.h', 'h', [], error_collector)
- self.assertEquals(
- 1,
- error_collector.result_list().count(
- 'No #ifndef header guard found, suggested CPP variable is: %s'
- ' [build/header_guard] [5]' % expected_guard),
- error_collector.result_list())
-
- def test_build_printf_format(self):
- self.assert_lint(
- r'printf("\%%d", value);',
- '%, [, (, and { are undefined character escapes. Unescape them.'
- ' [build/printf_format] [3]')
-
- self.assert_lint(
- r'snprintf(buffer, sizeof(buffer), "\[%d", value);',
- '%, [, (, and { are undefined character escapes. Unescape them.'
- ' [build/printf_format] [3]')
-
- self.assert_lint(
- r'fprintf(file, "\(%d", value);',
- '%, [, (, and { are undefined character escapes. Unescape them.'
- ' [build/printf_format] [3]')
-
- self.assert_lint(
- r'vsnprintf(buffer, sizeof(buffer), "\\\{%d", ap);',
- '%, [, (, and { are undefined character escapes. Unescape them.'
- ' [build/printf_format] [3]')
-
- # Don't warn if double-slash precedes the symbol
- self.assert_lint(r'printf("\\%%%d", value);',
- '')
-
- def test_runtime_printf_format(self):
- self.assert_lint(
- r'fprintf(file, "%q", value);',
- '%q in format strings is deprecated. Use %ll instead.'
- ' [runtime/printf_format] [3]')
-
- self.assert_lint(
- r'aprintf(file, "The number is %12q", value);',
- '%q in format strings is deprecated. Use %ll instead.'
- ' [runtime/printf_format] [3]')
-
- self.assert_lint(
- r'printf(file, "The number is" "%-12q", value);',
- '%q in format strings is deprecated. Use %ll instead.'
- ' [runtime/printf_format] [3]')
-
- self.assert_lint(
- r'printf(file, "The number is" "%+12q", value);',
- '%q in format strings is deprecated. Use %ll instead.'
- ' [runtime/printf_format] [3]')
-
- self.assert_lint(
- r'printf(file, "The number is" "% 12q", value);',
- '%q in format strings is deprecated. Use %ll instead.'
- ' [runtime/printf_format] [3]')
-
- self.assert_lint(
- r'snprintf(file, "Never mix %d and %1$d parmaeters!", value);',
- '%N$ formats are unconventional. Try rewriting to avoid them.'
- ' [runtime/printf_format] [2]')
-
- def assert_lintLogCodeOnError(self, code, expected_message):
- # Special assert_lint which logs the input code on error.
- result = self.perform_single_line_lint(code, 'foo.cpp')
- if result != expected_message:
- self.fail('For code: "%s"\nGot: "%s"\nExpected: "%s"'
- % (code, result, expected_message))
-
- def test_build_storage_class(self):
- qualifiers = [None, 'const', 'volatile']
- signs = [None, 'signed', 'unsigned']
- types = ['void', 'char', 'int', 'float', 'double',
- 'schar', 'int8', 'uint8', 'int16', 'uint16',
- 'int32', 'uint32', 'int64', 'uint64']
- storage_classes = ['auto', 'extern', 'register', 'static', 'typedef']
-
- build_storage_class_error_message = (
- 'Storage class (static, extern, typedef, etc) should be first.'
- ' [build/storage_class] [5]')
-
- # Some explicit cases. Legal in C++, deprecated in C99.
- self.assert_lint('const int static foo = 5;',
- build_storage_class_error_message)
-
- self.assert_lint('char static foo;',
- build_storage_class_error_message)
-
- self.assert_lint('double const static foo = 2.0;',
- build_storage_class_error_message)
-
- self.assert_lint('uint64 typedef unsignedLongLong;',
- build_storage_class_error_message)
-
- self.assert_lint('int register foo = 0;',
- build_storage_class_error_message)
-
- # Since there are a very large number of possibilities, randomly
- # construct declarations.
- # Make sure that the declaration is logged if there's an error.
- # Seed generator with an integer for absolute reproducibility.
- random.seed(25)
- for unused_i in range(10):
- # Build up random list of non-storage-class declaration specs.
- other_decl_specs = [random.choice(qualifiers), random.choice(signs),
- random.choice(types)]
- # remove None
- other_decl_specs = filter(lambda x: x is not None, other_decl_specs)
-
- # shuffle
- random.shuffle(other_decl_specs)
-
- # insert storage class after the first
- storage_class = random.choice(storage_classes)
- insertion_point = random.randint(1, len(other_decl_specs))
- decl_specs = (other_decl_specs[0:insertion_point]
- + [storage_class]
- + other_decl_specs[insertion_point:])
-
- self.assert_lintLogCodeOnError(
- ' '.join(decl_specs) + ';',
- build_storage_class_error_message)
-
- # but no error if storage class is first
- self.assert_lintLogCodeOnError(
- storage_class + ' ' + ' '.join(other_decl_specs),
- '')
-
- def test_legal_copyright(self):
- legal_copyright_message = (
- 'No copyright message found. '
- 'You should have a line: "Copyright [year] <Copyright Owner>"'
- ' [legal/copyright] [5]')
-
- copyright_line = '// Copyright 2008 Google Inc. All Rights Reserved.'
-
- file_path = 'mydir/googleclient/foo.cpp'
-
- # There should be a copyright message in the first 10 lines
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data(file_path, 'cpp', [], error_collector)
- self.assertEquals(
- 1,
- error_collector.result_list().count(legal_copyright_message))
-
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data(
- file_path, 'cpp',
- ['' for unused_i in range(10)] + [copyright_line],
- error_collector)
- self.assertEquals(
- 1,
- error_collector.result_list().count(legal_copyright_message))
-
- # Test that warning isn't issued if Copyright line appears early enough.
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data(file_path, 'cpp', [copyright_line], error_collector)
- for message in error_collector.result_list():
- if message.find('legal/copyright') != -1:
- self.fail('Unexpected error: %s' % message)
-
- error_collector = ErrorCollector(self.assert_)
- self.process_file_data(
- file_path, 'cpp',
- ['' for unused_i in range(9)] + [copyright_line],
- error_collector)
- for message in error_collector.result_list():
- if message.find('legal/copyright') != -1:
- self.fail('Unexpected error: %s' % message)
-
- def test_invalid_increment(self):
- self.assert_lint('*count++;',
- 'Changing pointer instead of value (or unused value of '
- 'operator*). [runtime/invalid_increment] [5]')
-
-
-class CleansedLinesTest(unittest.TestCase):
- def test_init(self):
- lines = ['Line 1',
- 'Line 2',
- 'Line 3 // Comment test',
- 'Line 4 "foo"']
-
- clean_lines = cpp_style.CleansedLines(lines)
- self.assertEquals(lines, clean_lines.raw_lines)
- self.assertEquals(4, clean_lines.num_lines())
-
- self.assertEquals(['Line 1',
- 'Line 2',
- 'Line 3 ',
- 'Line 4 "foo"'],
- clean_lines.lines)
-
- self.assertEquals(['Line 1',
- 'Line 2',
- 'Line 3 ',
- 'Line 4 ""'],
- clean_lines.elided)
-
- def test_init_empty(self):
- clean_lines = cpp_style.CleansedLines([])
- self.assertEquals([], clean_lines.raw_lines)
- self.assertEquals(0, clean_lines.num_lines())
-
- def test_collapse_strings(self):
- collapse = cpp_style.CleansedLines.collapse_strings
- self.assertEquals('""', collapse('""')) # "" (empty)
- self.assertEquals('"""', collapse('"""')) # """ (bad)
- self.assertEquals('""', collapse('"xyz"')) # "xyz" (string)
- self.assertEquals('""', collapse('"\\\""')) # "\"" (string)
- self.assertEquals('""', collapse('"\'"')) # "'" (string)
- self.assertEquals('"\"', collapse('"\"')) # "\" (bad)
- self.assertEquals('""', collapse('"\\\\"')) # "\\" (string)
- self.assertEquals('"', collapse('"\\\\\\"')) # "\\\" (bad)
- self.assertEquals('""', collapse('"\\\\\\\\"')) # "\\\\" (string)
-
- self.assertEquals('\'\'', collapse('\'\'')) # '' (empty)
- self.assertEquals('\'\'', collapse('\'a\'')) # 'a' (char)
- self.assertEquals('\'\'', collapse('\'\\\'\'')) # '\'' (char)
- self.assertEquals('\'', collapse('\'\\\'')) # '\' (bad)
- self.assertEquals('', collapse('\\012')) # '\012' (char)
- self.assertEquals('', collapse('\\xfF0')) # '\xfF0' (char)
- self.assertEquals('', collapse('\\n')) # '\n' (char)
- self.assertEquals('\#', collapse('\\#')) # '\#' (bad)
-
- self.assertEquals('StringReplace(body, "", "");',
- collapse('StringReplace(body, "\\\\", "\\\\\\\\");'))
- self.assertEquals('\'\' ""',
- collapse('\'"\' "foo"'))
-
-
-class OrderOfIncludesTest(CppStyleTestBase):
- def setUp(self):
- self.include_state = cpp_style._IncludeState()
-
- # Cheat os.path.abspath called in FileInfo class.
- self.os_path_abspath_orig = os.path.abspath
- os.path.abspath = lambda value: value
-
- def tearDown(self):
- os.path.abspath = self.os_path_abspath_orig
-
- def test_try_drop_common_suffixes(self):
- self.assertEqual('foo/foo', cpp_style._drop_common_suffixes('foo/foo-inl.h'))
- self.assertEqual('foo/bar/foo',
- cpp_style._drop_common_suffixes('foo/bar/foo_inl.h'))
- self.assertEqual('foo/foo', cpp_style._drop_common_suffixes('foo/foo.cpp'))
- self.assertEqual('foo/foo_unusualinternal',
- cpp_style._drop_common_suffixes('foo/foo_unusualinternal.h'))
- self.assertEqual('',
- cpp_style._drop_common_suffixes('_test.cpp'))
- self.assertEqual('test',
- cpp_style._drop_common_suffixes('test.cpp'))
-
-
-class OrderOfIncludesTest(CppStyleTestBase):
- def setUp(self):
- self.include_state = cpp_style._IncludeState()
-
- # Cheat os.path.abspath called in FileInfo class.
- self.os_path_abspath_orig = os.path.abspath
- os.path.abspath = lambda value: value
-
- def tearDown(self):
- os.path.abspath = self.os_path_abspath_orig
-
- def test_check_next_include_order__no_config(self):
- self.assertEqual('Header file should not contain WebCore config.h.',
- self.include_state.check_next_include_order(cpp_style._CONFIG_HEADER, True))
-
- def test_check_next_include_order__no_self(self):
- self.assertEqual('Header file should not contain itself.',
- self.include_state.check_next_include_order(cpp_style._PRIMARY_HEADER, True))
- # Test actual code to make sure that header types are correctly assigned.
- self.assert_language_rules_check('Foo.h',
- '#include "Foo.h"\n',
- 'Header file should not contain itself. Should be: alphabetically sorted.'
- ' [build/include_order] [4]')
- self.assert_language_rules_check('FooBar.h',
- '#include "Foo.h"\n',
- '')
-
- def test_check_next_include_order__likely_then_config(self):
- self.assertEqual('Found header this file implements before WebCore config.h.',
- self.include_state.check_next_include_order(cpp_style._PRIMARY_HEADER, False))
- self.assertEqual('Found WebCore config.h after a header this file implements.',
- self.include_state.check_next_include_order(cpp_style._CONFIG_HEADER, False))
-
- def test_check_next_include_order__other_then_config(self):
- self.assertEqual('Found other header before WebCore config.h.',
- self.include_state.check_next_include_order(cpp_style._OTHER_HEADER, False))
- self.assertEqual('Found WebCore config.h after other header.',
- self.include_state.check_next_include_order(cpp_style._CONFIG_HEADER, False))
-
- def test_check_next_include_order__config_then_other_then_likely(self):
- self.assertEqual('', self.include_state.check_next_include_order(cpp_style._CONFIG_HEADER, False))
- self.assertEqual('Found other header before a header this file implements.',
- self.include_state.check_next_include_order(cpp_style._OTHER_HEADER, False))
- self.assertEqual('Found header this file implements after other header.',
- self.include_state.check_next_include_order(cpp_style._PRIMARY_HEADER, False))
-
- def test_check_alphabetical_include_order(self):
- self.assert_language_rules_check('foo.h',
- '#include "a.h"\n'
- '#include "c.h"\n'
- '#include "b.h"\n',
- 'Alphabetical sorting problem. [build/include_order] [4]')
-
- self.assert_language_rules_check('foo.h',
- '#include "a.h"\n'
- '#include "b.h"\n'
- '#include "c.h"\n',
- '')
-
- self.assert_language_rules_check('foo.h',
- '#include <assert.h>\n'
- '#include "bar.h"\n',
- 'Alphabetical sorting problem. [build/include_order] [4]')
-
- self.assert_language_rules_check('foo.h',
- '#include "bar.h"\n'
- '#include <assert.h>\n',
- '')
-
- def test_check_line_break_after_own_header(self):
- self.assert_language_rules_check('foo.cpp',
- '#include "config.h"\n'
- '#include "foo.h"\n'
- '#include "bar.h"\n',
- 'You should add a blank line after implementation file\'s own header. [build/include_order] [4]')
-
- self.assert_language_rules_check('foo.cpp',
- '#include "config.h"\n'
- '#include "foo.h"\n'
- '\n'
- '#include "bar.h"\n',
- '')
-
- def test_check_preprocessor_in_include_section(self):
- self.assert_language_rules_check('foo.cpp',
- '#include "config.h"\n'
- '#include "foo.h"\n'
- '\n'
- '#ifdef BAZ\n'
- '#include "baz.h"\n'
- '#else\n'
- '#include "foobar.h"\n'
- '#endif"\n'
- '#include "bar.h"\n', # No flag because previous is in preprocessor section
- '')
-
- self.assert_language_rules_check('foo.cpp',
- '#include "config.h"\n'
- '#include "foo.h"\n'
- '\n'
- '#ifdef BAZ\n'
- '#include "baz.h"\n'
- '#endif"\n'
- '#include "bar.h"\n'
- '#include "a.h"\n', # Should still flag this.
- 'Alphabetical sorting problem. [build/include_order] [4]')
-
- self.assert_language_rules_check('foo.cpp',
- '#include "config.h"\n'
- '#include "foo.h"\n'
- '\n'
- '#ifdef BAZ\n'
- '#include "baz.h"\n'
- '#include "bar.h"\n' #Should still flag this
- '#endif"\n',
- 'Alphabetical sorting problem. [build/include_order] [4]')
-
- self.assert_language_rules_check('foo.cpp',
- '#include "config.h"\n'
- '#include "foo.h"\n'
- '\n'
- '#ifdef BAZ\n'
- '#include "baz.h"\n'
- '#endif"\n'
- '#ifdef FOOBAR\n'
- '#include "foobar.h"\n'
- '#endif"\n'
- '#include "bar.h"\n'
- '#include "a.h"\n', # Should still flag this.
- 'Alphabetical sorting problem. [build/include_order] [4]')
-
- # Check that after an already included error, the sorting rules still work.
- self.assert_language_rules_check('foo.cpp',
- '#include "config.h"\n'
- '#include "foo.h"\n'
- '\n'
- '#include "foo.h"\n'
- '#include "g.h"\n',
- '"foo.h" already included at foo.cpp:1 [build/include] [4]')
-
- def test_check_wtf_includes(self):
- self.assert_language_rules_check('foo.cpp',
- '#include "config.h"\n'
- '#include "foo.h"\n'
- '\n'
- '#include <wtf/Assertions.h>\n',
- '')
- self.assert_language_rules_check('foo.cpp',
- '#include "config.h"\n'
- '#include "foo.h"\n'
- '\n'
- '#include "wtf/Assertions.h"\n',
- 'wtf includes should be <wtf/file.h> instead of "wtf/file.h".'
- ' [build/include] [4]')
-
- def test_classify_include(self):
- classify_include = cpp_style._classify_include
- include_state = cpp_style._IncludeState()
- self.assertEqual(cpp_style._CONFIG_HEADER,
- classify_include('foo/foo.cpp',
- 'config.h',
- False, include_state))
- self.assertEqual(cpp_style._PRIMARY_HEADER,
- classify_include('foo/internal/foo.cpp',
- 'foo/public/foo.h',
- False, include_state))
- self.assertEqual(cpp_style._PRIMARY_HEADER,
- classify_include('foo/internal/foo.cpp',
- 'foo/other/public/foo.h',
- False, include_state))
- self.assertEqual(cpp_style._OTHER_HEADER,
- classify_include('foo/internal/foo.cpp',
- 'foo/other/public/foop.h',
- False, include_state))
- self.assertEqual(cpp_style._OTHER_HEADER,
- classify_include('foo/foo.cpp',
- 'string',
- True, include_state))
- self.assertEqual(cpp_style._PRIMARY_HEADER,
- classify_include('fooCustom.cpp',
- 'foo.h',
- False, include_state))
- self.assertEqual(cpp_style._PRIMARY_HEADER,
- classify_include('PrefixFooCustom.cpp',
- 'Foo.h',
- False, include_state))
- self.assertEqual(cpp_style._MOC_HEADER,
- classify_include('foo.cpp',
- 'foo.moc',
- False, include_state))
- self.assertEqual(cpp_style._MOC_HEADER,
- classify_include('foo.cpp',
- 'moc_foo.cpp',
- False, include_state))
- # Tricky example where both includes might be classified as primary.
- self.assert_language_rules_check('ScrollbarThemeWince.cpp',
- '#include "config.h"\n'
- '#include "ScrollbarThemeWince.h"\n'
- '\n'
- '#include "Scrollbar.h"\n',
- '')
- self.assert_language_rules_check('ScrollbarThemeWince.cpp',
- '#include "config.h"\n'
- '#include "Scrollbar.h"\n'
- '\n'
- '#include "ScrollbarThemeWince.h"\n',
- 'Found header this file implements after a header this file implements.'
- ' Should be: config.h, primary header, blank line, and then alphabetically sorted.'
- ' [build/include_order] [4]')
- self.assert_language_rules_check('ResourceHandleWin.cpp',
- '#include "config.h"\n'
- '#include "ResourceHandle.h"\n'
- '\n'
- '#include "ResourceHandleWin.h"\n',
- '')
-
- def test_try_drop_common_suffixes(self):
- self.assertEqual('foo/foo', cpp_style._drop_common_suffixes('foo/foo-inl.h'))
- self.assertEqual('foo/bar/foo',
- cpp_style._drop_common_suffixes('foo/bar/foo_inl.h'))
- self.assertEqual('foo/foo', cpp_style._drop_common_suffixes('foo/foo.cpp'))
- self.assertEqual('foo/foo_unusualinternal',
- cpp_style._drop_common_suffixes('foo/foo_unusualinternal.h'))
- self.assertEqual('',
- cpp_style._drop_common_suffixes('_test.cpp'))
- self.assertEqual('test',
- cpp_style._drop_common_suffixes('test.cpp'))
- self.assertEqual('test',
- cpp_style._drop_common_suffixes('test.cpp'))
-
-class CheckForFunctionLengthsTest(CppStyleTestBase):
- def setUp(self):
- # Reducing these thresholds for the tests speeds up tests significantly.
- self.old_normal_trigger = cpp_style._FunctionState._NORMAL_TRIGGER
- self.old_test_trigger = cpp_style._FunctionState._TEST_TRIGGER
-
- cpp_style._FunctionState._NORMAL_TRIGGER = 10
- cpp_style._FunctionState._TEST_TRIGGER = 25
-
- def tearDown(self):
- cpp_style._FunctionState._NORMAL_TRIGGER = self.old_normal_trigger
- cpp_style._FunctionState._TEST_TRIGGER = self.old_test_trigger
-
- # FIXME: Eliminate the need for this function.
- def set_min_confidence(self, min_confidence):
- """Set new test confidence and return old test confidence."""
- old_min_confidence = self.min_confidence
- self.min_confidence = min_confidence
- return old_min_confidence
-
- def assert_function_lengths_check(self, code, expected_message):
- """Check warnings for long function bodies are as expected.
-
- Args:
- code: C++ source code expected to generate a warning message.
- expected_message: Message expected to be generated by the C++ code.
- """
- self.assertEquals(expected_message,
- self.perform_function_lengths_check(code))
-
- def trigger_lines(self, error_level):
- """Return number of lines needed to trigger a function length warning.
-
- Args:
- error_level: --v setting for cpp_style.
-
- Returns:
- Number of lines needed to trigger a function length warning.
- """
- return cpp_style._FunctionState._NORMAL_TRIGGER * 2 ** error_level
-
- def trigger_test_lines(self, error_level):
- """Return number of lines needed to trigger a test function length warning.
-
- Args:
- error_level: --v setting for cpp_style.
-
- Returns:
- Number of lines needed to trigger a test function length warning.
- """
- return cpp_style._FunctionState._TEST_TRIGGER * 2 ** error_level
-
- def assert_function_length_check_definition(self, lines, error_level):
- """Generate long function definition and check warnings are as expected.
-
- Args:
- lines: Number of lines to generate.
- error_level: --v setting for cpp_style.
- """
- trigger_level = self.trigger_lines(self.min_confidence)
- self.assert_function_lengths_check(
- 'void test(int x)' + self.function_body(lines),
- ('Small and focused functions are preferred: '
- 'test() has %d non-comment lines '
- '(error triggered by exceeding %d lines).'
- ' [readability/fn_size] [%d]'
- % (lines, trigger_level, error_level)))
-
- def assert_function_length_check_definition_ok(self, lines):
- """Generate shorter function definition and check no warning is produced.
-
- Args:
- lines: Number of lines to generate.
- """
- self.assert_function_lengths_check(
- 'void test(int x)' + self.function_body(lines),
- '')
-
- def assert_function_length_check_at_error_level(self, error_level):
- """Generate and check function at the trigger level for --v setting.
-
- Args:
- error_level: --v setting for cpp_style.
- """
- self.assert_function_length_check_definition(self.trigger_lines(error_level),
- error_level)
-
- def assert_function_length_check_below_error_level(self, error_level):
- """Generate and check function just below the trigger level for --v setting.
-
- Args:
- error_level: --v setting for cpp_style.
- """
- self.assert_function_length_check_definition(self.trigger_lines(error_level) - 1,
- error_level - 1)
-
- def assert_function_length_check_above_error_level(self, error_level):
- """Generate and check function just above the trigger level for --v setting.
-
- Args:
- error_level: --v setting for cpp_style.
- """
- self.assert_function_length_check_definition(self.trigger_lines(error_level) + 1,
- error_level)
-
- def function_body(self, number_of_lines):
- return ' {\n' + ' this_is_just_a_test();\n' * number_of_lines + '}'
-
- def function_body_with_blank_lines(self, number_of_lines):
- return ' {\n' + ' this_is_just_a_test();\n\n' * number_of_lines + '}'
-
- def function_body_with_no_lints(self, number_of_lines):
- return ' {\n' + ' this_is_just_a_test(); // NOLINT\n' * number_of_lines + '}'
-
- # Test line length checks.
- def test_function_length_check_declaration(self):
- self.assert_function_lengths_check(
- 'void test();', # Not a function definition
- '')
-
- def test_function_length_check_declaration_with_block_following(self):
- self.assert_function_lengths_check(
- ('void test();\n'
- + self.function_body(66)), # Not a function definition
- '')
-
- def test_function_length_check_class_definition(self):
- self.assert_function_lengths_check( # Not a function definition
- 'class Test' + self.function_body(66) + ';',
- '')
-
- def test_function_length_check_trivial(self):
- self.assert_function_lengths_check(
- 'void test() {}', # Not counted
- '')
-
- def test_function_length_check_empty(self):
- self.assert_function_lengths_check(
- 'void test() {\n}',
- '')
-
- def test_function_length_check_definition_below_severity0(self):
- old_min_confidence = self.set_min_confidence(0)
- self.assert_function_length_check_definition_ok(self.trigger_lines(0) - 1)
- self.set_min_confidence(old_min_confidence)
-
- def test_function_length_check_definition_at_severity0(self):
- old_min_confidence = self.set_min_confidence(0)
- self.assert_function_length_check_definition_ok(self.trigger_lines(0))
- self.set_min_confidence(old_min_confidence)
-
- def test_function_length_check_definition_above_severity0(self):
- old_min_confidence = self.set_min_confidence(0)
- self.assert_function_length_check_above_error_level(0)
- self.set_min_confidence(old_min_confidence)
-
- def test_function_length_check_definition_below_severity1v0(self):
- old_min_confidence = self.set_min_confidence(0)
- self.assert_function_length_check_below_error_level(1)
- self.set_min_confidence(old_min_confidence)
-
- def test_function_length_check_definition_at_severity1v0(self):
- old_min_confidence = self.set_min_confidence(0)
- self.assert_function_length_check_at_error_level(1)
- self.set_min_confidence(old_min_confidence)
-
- def test_function_length_check_definition_below_severity1(self):
- self.assert_function_length_check_definition_ok(self.trigger_lines(1) - 1)
-
- def test_function_length_check_definition_at_severity1(self):
- self.assert_function_length_check_definition_ok(self.trigger_lines(1))
-
- def test_function_length_check_definition_above_severity1(self):
- self.assert_function_length_check_above_error_level(1)
-
- def test_function_length_check_definition_severity1_plus_indented(self):
- error_level = 1
- error_lines = self.trigger_lines(error_level) + 1
- trigger_level = self.trigger_lines(self.min_confidence)
- indent_spaces = ' '
- self.assert_function_lengths_check(
- re.sub(r'(?m)^(.)', indent_spaces + r'\1',
- 'void test_indent(int x)\n' + self.function_body(error_lines)),
- ('Small and focused functions are preferred: '
- 'test_indent() has %d non-comment lines '
- '(error triggered by exceeding %d lines).'
- ' [readability/fn_size] [%d]')
- % (error_lines, trigger_level, error_level))
-
- def test_function_length_check_definition_severity1_plus_blanks(self):
- error_level = 1
- error_lines = self.trigger_lines(error_level) + 1
- trigger_level = self.trigger_lines(self.min_confidence)
- self.assert_function_lengths_check(
- 'void test_blanks(int x)' + self.function_body(error_lines),
- ('Small and focused functions are preferred: '
- 'test_blanks() has %d non-comment lines '
- '(error triggered by exceeding %d lines).'
- ' [readability/fn_size] [%d]')
- % (error_lines, trigger_level, error_level))
-
- def test_function_length_check_complex_definition_severity1(self):
- error_level = 1
- error_lines = self.trigger_lines(error_level) + 1
- trigger_level = self.trigger_lines(self.min_confidence)
- self.assert_function_lengths_check(
- ('my_namespace::my_other_namespace::MyVeryLongTypeName<Type1, bool func(const Element*)>*\n'
- 'my_namespace::my_other_namespace<Type3, Type4>::~MyFunction<Type5<Type6, Type7> >(int arg1, char* arg2)'
- + self.function_body(error_lines)),
- ('Small and focused functions are preferred: '
- 'my_namespace::my_other_namespace<Type3, Type4>::~MyFunction<Type5<Type6, Type7> >()'
- ' has %d non-comment lines '
- '(error triggered by exceeding %d lines).'
- ' [readability/fn_size] [%d]')
- % (error_lines, trigger_level, error_level))
-
- def test_function_length_check_definition_severity1_for_test(self):
- error_level = 1
- error_lines = self.trigger_test_lines(error_level) + 1
- trigger_level = self.trigger_test_lines(self.min_confidence)
- self.assert_function_lengths_check(
- 'TEST_F(Test, Mutator)' + self.function_body(error_lines),
- ('Small and focused functions are preferred: '
- 'TEST_F(Test, Mutator) has %d non-comment lines '
- '(error triggered by exceeding %d lines).'
- ' [readability/fn_size] [%d]')
- % (error_lines, trigger_level, error_level))
-
- def test_function_length_check_definition_severity1_for_split_line_test(self):
- error_level = 1
- error_lines = self.trigger_test_lines(error_level) + 1
- trigger_level = self.trigger_test_lines(self.min_confidence)
- self.assert_function_lengths_check(
- ('TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,\n'
- ' FixGoogleUpdate_AllValues_MachineApp)' # note: 4 spaces
- + self.function_body(error_lines)),
- ('Small and focused functions are preferred: '
- 'TEST_F(GoogleUpdateRecoveryRegistryProtectedTest, ' # 1 space
- 'FixGoogleUpdate_AllValues_MachineApp) has %d non-comment lines '
- '(error triggered by exceeding %d lines).'
- ' [readability/fn_size] [%d]')
- % (error_lines, trigger_level, error_level))
-
- def test_function_length_check_definition_severity1_for_bad_test_doesnt_break(self):
- error_level = 1
- error_lines = self.trigger_test_lines(error_level) + 1
- trigger_level = self.trigger_test_lines(self.min_confidence)
- self.assert_function_lengths_check(
- ('TEST_F('
- + self.function_body(error_lines)),
- ('Small and focused functions are preferred: '
- 'TEST_F has %d non-comment lines '
- '(error triggered by exceeding %d lines).'
- ' [readability/fn_size] [%d]')
- % (error_lines, trigger_level, error_level))
-
- def test_function_length_check_definition_severity1_with_embedded_no_lints(self):
- error_level = 1
- error_lines = self.trigger_lines(error_level) + 1
- trigger_level = self.trigger_lines(self.min_confidence)
- self.assert_function_lengths_check(
- 'void test(int x)' + self.function_body_with_no_lints(error_lines),
- ('Small and focused functions are preferred: '
- 'test() has %d non-comment lines '
- '(error triggered by exceeding %d lines).'
- ' [readability/fn_size] [%d]')
- % (error_lines, trigger_level, error_level))
-
- def test_function_length_check_definition_severity1_with_no_lint(self):
- self.assert_function_lengths_check(
- ('void test(int x)' + self.function_body(self.trigger_lines(1))
- + ' // NOLINT -- long function'),
- '')
-
- def test_function_length_check_definition_below_severity2(self):
- self.assert_function_length_check_below_error_level(2)
-
- def test_function_length_check_definition_severity2(self):
- self.assert_function_length_check_at_error_level(2)
-
- def test_function_length_check_definition_above_severity2(self):
- self.assert_function_length_check_above_error_level(2)
-
- def test_function_length_check_definition_below_severity3(self):
- self.assert_function_length_check_below_error_level(3)
-
- def test_function_length_check_definition_severity3(self):
- self.assert_function_length_check_at_error_level(3)
-
- def test_function_length_check_definition_above_severity3(self):
- self.assert_function_length_check_above_error_level(3)
-
- def test_function_length_check_definition_below_severity4(self):
- self.assert_function_length_check_below_error_level(4)
-
- def test_function_length_check_definition_severity4(self):
- self.assert_function_length_check_at_error_level(4)
-
- def test_function_length_check_definition_above_severity4(self):
- self.assert_function_length_check_above_error_level(4)
-
- def test_function_length_check_definition_below_severity5(self):
- self.assert_function_length_check_below_error_level(5)
-
- def test_function_length_check_definition_at_severity5(self):
- self.assert_function_length_check_at_error_level(5)
-
- def test_function_length_check_definition_above_severity5(self):
- self.assert_function_length_check_above_error_level(5)
-
- def test_function_length_check_definition_huge_lines(self):
- # 5 is the limit
- self.assert_function_length_check_definition(self.trigger_lines(10), 5)
-
- def test_function_length_not_determinable(self):
- # Macro invocation without terminating semicolon.
- self.assert_function_lengths_check(
- 'MACRO(arg)',
- '')
-
- # Macro with underscores
- self.assert_function_lengths_check(
- 'MACRO_WITH_UNDERSCORES(arg1, arg2, arg3)',
- '')
-
- self.assert_function_lengths_check(
- 'NonMacro(arg)',
- 'Lint failed to find start of function body.'
- ' [readability/fn_size] [5]')
-
-
-class NoNonVirtualDestructorsTest(CppStyleTestBase):
-
- def test_no_error(self):
- self.assert_multi_line_lint(
- '''class Foo {
- virtual ~Foo();
- virtual void foo();
- };''',
- '')
-
- self.assert_multi_line_lint(
- '''class Foo {
- virtual inline ~Foo();
- virtual void foo();
- };''',
- '')
-
- self.assert_multi_line_lint(
- '''class Foo {
- inline virtual ~Foo();
- virtual void foo();
- };''',
- '')
-
- self.assert_multi_line_lint(
- '''class Foo::Goo {
- virtual ~Goo();
- virtual void goo();
- };''',
- '')
- self.assert_multi_line_lint(
- 'class Foo { void foo(); };',
- 'More than one command on the same line [whitespace/newline] [4]')
- self.assert_multi_line_lint(
- 'class MyClass {\n'
- ' int getIntValue() { ASSERT(m_ptr); return *m_ptr; }\n'
- '};\n',
- '')
- self.assert_multi_line_lint(
- 'class MyClass {\n'
- ' int getIntValue()\n'
- ' {\n'
- ' ASSERT(m_ptr); return *m_ptr;\n'
- ' }\n'
- '};\n',
- 'More than one command on the same line [whitespace/newline] [4]')
-
- self.assert_multi_line_lint(
- '''class Qualified::Goo : public Foo {
- virtual void goo();
- };''',
- '')
-
- self.assert_multi_line_lint(
- # Line-ending :
- '''class Goo :
- public Foo {
- virtual void goo();
- };''',
- 'Labels should always be indented at least one space. If this is a '
- 'member-initializer list in a constructor, the colon should be on the '
- 'line after the definition header. [whitespace/labels] [4]')
-
- def test_no_destructor_when_virtual_needed(self):
- self.assert_multi_line_lint_re(
- '''class Foo {
- virtual void foo();
- };''',
- 'The class Foo probably needs a virtual destructor')
-
- def test_destructor_non_virtual_when_virtual_needed(self):
- self.assert_multi_line_lint_re(
- '''class Foo {
- ~Foo();
- virtual void foo();
- };''',
- 'The class Foo probably needs a virtual destructor')
-
- def test_no_warn_when_derived(self):
- self.assert_multi_line_lint(
- '''class Foo : public Goo {
- virtual void foo();
- };''',
- '')
-
- def test_internal_braces(self):
- self.assert_multi_line_lint_re(
- '''class Foo {
- enum Goo {
- GOO
- };
- virtual void foo();
- };''',
- 'The class Foo probably needs a virtual destructor')
-
- def test_inner_class_needs_virtual_destructor(self):
- self.assert_multi_line_lint_re(
- '''class Foo {
- class Goo {
- virtual void goo();
- };
- };''',
- 'The class Goo probably needs a virtual destructor')
-
- def test_outer_class_needs_virtual_destructor(self):
- self.assert_multi_line_lint_re(
- '''class Foo {
- class Goo {
- };
- virtual void foo();
- };''',
- 'The class Foo probably needs a virtual destructor')
-
- def test_qualified_class_needs_virtual_destructor(self):
- self.assert_multi_line_lint_re(
- '''class Qualified::Foo {
- virtual void foo();
- };''',
- 'The class Qualified::Foo probably needs a virtual destructor')
-
- def test_multi_line_declaration_no_error(self):
- self.assert_multi_line_lint_re(
- '''class Foo
- : public Goo {
- virtual void foo();
- };''',
- '')
-
- def test_multi_line_declaration_with_error(self):
- self.assert_multi_line_lint(
- '''class Foo
- {
- virtual void foo();
- };''',
- ['This { should be at the end of the previous line '
- '[whitespace/braces] [4]',
- 'The class Foo probably needs a virtual destructor due to having '
- 'virtual method(s), one declared at line 2. [runtime/virtual] [4]'])
-
-
-class PassPtrTest(CppStyleTestBase):
- # For http://webkit.org/coding/RefPtr.html
-
- def assert_pass_ptr_check(self, code, expected_message):
- """Check warnings for Pass*Ptr are as expected.
-
- Args:
- code: C++ source code expected to generate a warning message.
- expected_message: Message expected to be generated by the C++ code.
- """
- self.assertEquals(expected_message,
- self.perform_pass_ptr_check(code))
-
- def test_pass_ref_ptr_in_function(self):
- # Local variables should never be PassRefPtr.
- self.assert_pass_ptr_check(
- 'int myFunction()\n'
- '{\n'
- ' PassRefPtr<Type1> variable = variable2;\n'
- '}',
- 'Local variables should never be PassRefPtr (see '
- 'http://webkit.org/coding/RefPtr.html). [readability/pass_ptr] [5]')
-
- def test_pass_own_ptr_in_function(self):
- # Local variables should never be PassRefPtr.
- self.assert_pass_ptr_check(
- 'int myFunction()\n'
- '{\n'
- ' PassOwnPtr<Type1> variable = variable2;\n'
- '}',
- 'Local variables should never be PassOwnPtr (see '
- 'http://webkit.org/coding/RefPtr.html). [readability/pass_ptr] [5]')
-
- def test_pass_other_type_ptr_in_function(self):
- # Local variables should never be PassRefPtr.
- self.assert_pass_ptr_check(
- 'int myFunction()\n'
- '{\n'
- ' PassOtherTypePtr<Type1> variable;\n'
- '}',
- 'Local variables should never be PassOtherTypePtr (see '
- 'http://webkit.org/coding/RefPtr.html). [readability/pass_ptr] [5]')
-
- def test_pass_ref_ptr_return_value(self):
- self.assert_pass_ptr_check(
- 'PassRefPtr<Type1>\n'
- 'myFunction(int)\n'
- '{\n'
- '}',
- '')
-
- def test_pass_ref_ptr_parameter_value(self):
- self.assert_pass_ptr_check(
- 'int myFunction(PassRefPtr<Type1>)\n'
- '{\n'
- '}',
- '')
-
-
-class WebKitStyleTest(CppStyleTestBase):
-
- # for http://webkit.org/coding/coding-style.html
- def test_indentation(self):
- # 1. Use spaces, not tabs. Tabs should only appear in files that
- # require them for semantic meaning, like Makefiles.
- self.assert_multi_line_lint(
- 'class Foo {\n'
- ' int goo;\n'
- '};',
- '')
- self.assert_multi_line_lint(
- 'class Foo {\n'
- '\tint goo;\n'
- '};',
- 'Tab found; better to use spaces [whitespace/tab] [1]')
-
- # 2. The indent size is 4 spaces.
- self.assert_multi_line_lint(
- 'class Foo {\n'
- ' int goo;\n'
- '};',
- '')
- self.assert_multi_line_lint(
- 'class Foo {\n'
- ' int goo;\n'
- '};',
- 'Weird number of spaces at line-start. Are you using a 4-space indent? [whitespace/indent] [3]')
- # FIXME: No tests for 8-spaces.
-
- # 3. In a header, code inside a namespace should not be indented.
- self.assert_multi_line_lint(
- 'namespace WebCore {\n\n'
- 'class Document {\n'
- ' int myVariable;\n'
- '};\n'
- '}',
- '',
- 'foo.h')
- self.assert_multi_line_lint(
- 'namespace OuterNamespace {\n'
- ' namespace InnerNamespace {\n'
- ' class Document {\n'
- '};\n'
- '};\n'
- '}',
- 'Code inside a namespace should not be indented. [whitespace/indent] [4]',
- 'foo.h')
- self.assert_multi_line_lint(
- 'namespace OuterNamespace {\n'
- ' class Document {\n'
- ' namespace InnerNamespace {\n'
- '};\n'
- '};\n'
- '}',
- 'Code inside a namespace should not be indented. [whitespace/indent] [4]',
- 'foo.h')
- self.assert_multi_line_lint(
- 'namespace WebCore {\n'
- '#if 0\n'
- ' class Document {\n'
- '};\n'
- '#endif\n'
- '}',
- 'Code inside a namespace should not be indented. [whitespace/indent] [4]',
- 'foo.h')
- self.assert_multi_line_lint(
- 'namespace WebCore {\n'
- 'class Document {\n'
- '};\n'
- '}',
- '',
- 'foo.h')
-
- # 4. In an implementation file (files with the extension .cpp, .c
- # or .mm), code inside a namespace should not be indented.
- self.assert_multi_line_lint(
- 'namespace WebCore {\n\n'
- 'Document::Foo()\n'
- ' : foo(bar)\n'
- ' , boo(far)\n'
- '{\n'
- ' stuff();\n'
- '}',
- '',
- 'foo.cpp')
- self.assert_multi_line_lint(
- 'namespace OuterNamespace {\n'
- 'namespace InnerNamespace {\n'
- 'Document::Foo() { }\n'
- ' void* p;\n'
- '}\n'
- '}\n',
- 'Code inside a namespace should not be indented. [whitespace/indent] [4]',
- 'foo.cpp')
- self.assert_multi_line_lint(
- 'namespace OuterNamespace {\n'
- 'namespace InnerNamespace {\n'
- 'Document::Foo() { }\n'
- '}\n'
- ' void* p;\n'
- '}\n',
- 'Code inside a namespace should not be indented. [whitespace/indent] [4]',
- 'foo.cpp')
- self.assert_multi_line_lint(
- 'namespace WebCore {\n\n'
- ' const char* foo = "start:;"\n'
- ' "dfsfsfs";\n'
- '}\n',
- 'Code inside a namespace should not be indented. [whitespace/indent] [4]',
- 'foo.cpp')
- self.assert_multi_line_lint(
- 'namespace WebCore {\n\n'
- 'const char* foo(void* a = ";", // ;\n'
- ' void* b);\n'
- ' void* p;\n'
- '}\n',
- 'Code inside a namespace should not be indented. [whitespace/indent] [4]',
- 'foo.cpp')
- self.assert_multi_line_lint(
- 'namespace WebCore {\n\n'
- 'const char* foo[] = {\n'
- ' "void* b);", // ;\n'
- ' "asfdf",\n'
- ' }\n'
- ' void* p;\n'
- '}\n',
- 'Code inside a namespace should not be indented. [whitespace/indent] [4]',
- 'foo.cpp')
- self.assert_multi_line_lint(
- 'namespace WebCore {\n\n'
- 'const char* foo[] = {\n'
- ' "void* b);", // }\n'
- ' "asfdf",\n'
- ' }\n'
- '}\n',
- '',
- 'foo.cpp')
- self.assert_multi_line_lint(
- ' namespace WebCore {\n\n'
- ' void Document::Foo()\n'
- ' {\n'
- 'start: // infinite loops are fun!\n'
- ' goto start;\n'
- ' }',
- 'namespace should never be indented. [whitespace/indent] [4]',
- 'foo.cpp')
- self.assert_multi_line_lint(
- 'namespace WebCore {\n'
- ' Document::Foo() { }\n'
- '}',
- 'Code inside a namespace should not be indented.'
- ' [whitespace/indent] [4]',
- 'foo.cpp')
- self.assert_multi_line_lint(
- 'namespace WebCore {\n'
- '#define abc(x) x; \\\n'
- ' x\n'
- '}',
- '',
- 'foo.cpp')
- self.assert_multi_line_lint(
- 'namespace WebCore {\n'
- '#define abc(x) x; \\\n'
- ' x\n'
- ' void* x;'
- '}',
- 'Code inside a namespace should not be indented. [whitespace/indent] [4]',
- 'foo.cpp')
-
- # 5. A case label should line up with its switch statement. The
- # case statement is indented.
- self.assert_multi_line_lint(
- ' switch (condition) {\n'
- ' case fooCondition:\n'
- ' case barCondition:\n'
- ' i++;\n'
- ' break;\n'
- ' default:\n'
- ' i--;\n'
- ' }\n',
- '')
- self.assert_multi_line_lint(
- ' switch (condition) {\n'
- ' case fooCondition:\n'
- ' switch (otherCondition) {\n'
- ' default:\n'
- ' return;\n'
- ' }\n'
- ' default:\n'
- ' i--;\n'
- ' }\n',
- '')
- self.assert_multi_line_lint(
- ' switch (condition) {\n'
- ' case fooCondition: break;\n'
- ' default: return;\n'
- ' }\n',
- '')
- self.assert_multi_line_lint(
- ' switch (condition) {\n'
- ' case fooCondition:\n'
- ' case barCondition:\n'
- ' i++;\n'
- ' break;\n'
- ' default:\n'
- ' i--;\n'
- ' }\n',
- 'A case label should not be indented, but line up with its switch statement.'
- ' [whitespace/indent] [4]')
- self.assert_multi_line_lint(
- ' switch (condition) {\n'
- ' case fooCondition:\n'
- ' break;\n'
- ' default:\n'
- ' i--;\n'
- ' }\n',
- 'A case label should not be indented, but line up with its switch statement.'
- ' [whitespace/indent] [4]')
- self.assert_multi_line_lint(
- ' switch (condition) {\n'
- ' case fooCondition:\n'
- ' case barCondition:\n'
- ' switch (otherCondition) {\n'
- ' default:\n'
- ' return;\n'
- ' }\n'
- ' default:\n'
- ' i--;\n'
- ' }\n',
- 'A case label should not be indented, but line up with its switch statement.'
- ' [whitespace/indent] [4]')
- self.assert_multi_line_lint(
- ' switch (condition) {\n'
- ' case fooCondition:\n'
- ' case barCondition:\n'
- ' i++;\n'
- ' break;\n\n'
- ' default:\n'
- ' i--;\n'
- ' }\n',
- 'Non-label code inside switch statements should be indented.'
- ' [whitespace/indent] [4]')
- self.assert_multi_line_lint(
- ' switch (condition) {\n'
- ' case fooCondition:\n'
- ' case barCondition:\n'
- ' switch (otherCondition) {\n'
- ' default:\n'
- ' return;\n'
- ' }\n'
- ' default:\n'
- ' i--;\n'
- ' }\n',
- 'Non-label code inside switch statements should be indented.'
- ' [whitespace/indent] [4]')
-
- # 6. Boolean expressions at the same nesting level that span
- # multiple lines should have their operators on the left side of
- # the line instead of the right side.
- self.assert_multi_line_lint(
- ' return attr->name() == srcAttr\n'
- ' || attr->name() == lowsrcAttr;\n',
- '')
- self.assert_multi_line_lint(
- ' return attr->name() == srcAttr ||\n'
- ' attr->name() == lowsrcAttr;\n',
- 'Boolean expressions that span multiple lines should have their '
- 'operators on the left side of the line instead of the right side.'
- ' [whitespace/operators] [4]')
-
- def test_spacing(self):
- # 1. Do not place spaces around unary operators.
- self.assert_multi_line_lint(
- 'i++;',
- '')
- self.assert_multi_line_lint(
- 'i ++;',
- 'Extra space for operator ++; [whitespace/operators] [4]')
-
- # 2. Do place spaces around binary and ternary operators.
- self.assert_multi_line_lint(
- 'y = m * x + b;',
- '')
- self.assert_multi_line_lint(
- 'f(a, b);',
- '')
- self.assert_multi_line_lint(
- 'c = a | b;',
- '')
- self.assert_multi_line_lint(
- 'return condition ? 1 : 0;',
- '')
- self.assert_multi_line_lint(
- 'y=m*x+b;',
- 'Missing spaces around = [whitespace/operators] [4]')
- self.assert_multi_line_lint(
- 'f(a,b);',
- 'Missing space after , [whitespace/comma] [3]')
- self.assert_multi_line_lint(
- 'c = a|b;',
- 'Missing spaces around | [whitespace/operators] [3]')
- # FIXME: We cannot catch this lint error.
- # self.assert_multi_line_lint(
- # 'return condition ? 1:0;',
- # '')
-
- # 3. Place spaces between control statements and their parentheses.
- self.assert_multi_line_lint(
- ' if (condition)\n'
- ' doIt();\n',
- '')
- self.assert_multi_line_lint(
- ' if(condition)\n'
- ' doIt();\n',
- 'Missing space before ( in if( [whitespace/parens] [5]')
-
- # 4. Do not place spaces between a function and its parentheses,
- # or between a parenthesis and its content.
- self.assert_multi_line_lint(
- 'f(a, b);',
- '')
- self.assert_multi_line_lint(
- 'f (a, b);',
- 'Extra space before ( in function call [whitespace/parens] [4]')
- self.assert_multi_line_lint(
- 'f( a, b );',
- ['Extra space after ( in function call [whitespace/parens] [4]',
- 'Extra space before ) [whitespace/parens] [2]'])
-
- def test_line_breaking(self):
- # 1. Each statement should get its own line.
- self.assert_multi_line_lint(
- ' x++;\n'
- ' y++;\n'
- ' if (condition);\n'
- ' doIt();\n',
- '')
- self.assert_multi_line_lint(
- ' if (condition) \\\n'
- ' doIt();\n',
- '')
- self.assert_multi_line_lint(
- ' x++; y++;',
- 'More than one command on the same line [whitespace/newline] [4]')
- self.assert_multi_line_lint(
- ' if (condition) doIt();\n',
- 'More than one command on the same line in if [whitespace/parens] [4]')
-
- # 2. An else statement should go on the same line as a preceding
- # close brace if one is present, else it should line up with the
- # if statement.
- self.assert_multi_line_lint(
- 'if (condition) {\n'
- ' doSomething();\n'
- ' doSomethingAgain();\n'
- '} else {\n'
- ' doSomethingElse();\n'
- ' doSomethingElseAgain();\n'
- '}\n',
- '')
- self.assert_multi_line_lint(
- 'if (condition)\n'
- ' doSomething();\n'
- 'else\n'
- ' doSomethingElse();\n',
- '')
- self.assert_multi_line_lint(
- 'if (condition)\n'
- ' doSomething();\n'
- 'else {\n'
- ' doSomethingElse();\n'
- ' doSomethingElseAgain();\n'
- '}\n',
- '')
- self.assert_multi_line_lint(
- '#define TEST_ASSERT(expression) do { if (!(expression)) { TestsController::shared().testFailed(__FILE__, __LINE__, #expression); return; } } while (0)\n',
- '')
- self.assert_multi_line_lint(
- '#define TEST_ASSERT(expression) do { if ( !(expression)) { TestsController::shared().testFailed(__FILE__, __LINE__, #expression); return; } } while (0)\n',
- 'Extra space after ( in if [whitespace/parens] [5]')
- # FIXME: currently we only check first conditional, so we cannot detect errors in next ones.
- # self.assert_multi_line_lint(
- # '#define TEST_ASSERT(expression) do { if (!(expression)) { TestsController::shared().testFailed(__FILE__, __LINE__, #expression); return; } } while (0 )\n',
- # 'Mismatching spaces inside () in if [whitespace/parens] [5]')
- self.assert_multi_line_lint(
- 'if (condition) {\n'
- ' doSomething();\n'
- ' doSomethingAgain();\n'
- '}\n'
- 'else {\n'
- ' doSomethingElse();\n'
- ' doSomethingElseAgain();\n'
- '}\n',
- 'An else should appear on the same line as the preceding } [whitespace/newline] [4]')
- self.assert_multi_line_lint(
- 'if (condition) doSomething(); else doSomethingElse();\n',
- ['More than one command on the same line [whitespace/newline] [4]',
- 'Else clause should never be on same line as else (use 2 lines) [whitespace/newline] [4]',
- 'More than one command on the same line in if [whitespace/parens] [4]'])
- self.assert_multi_line_lint(
- 'if (condition) doSomething(); else {\n'
- ' doSomethingElse();\n'
- '}\n',
- ['More than one command on the same line in if [whitespace/parens] [4]',
- 'One line control clauses should not use braces. [whitespace/braces] [4]'])
- self.assert_multi_line_lint(
- 'void func()\n'
- '{\n'
- ' while (condition) { }\n'
- ' return 0;\n'
- '}\n',
- '')
- self.assert_multi_line_lint(
- 'void func()\n'
- '{\n'
- ' for (i = 0; i < 42; i++) { foobar(); }\n'
- ' return 0;\n'
- '}\n',
- 'More than one command on the same line in for [whitespace/parens] [4]')
-
- # 3. An else if statement should be written as an if statement
- # when the prior if concludes with a return statement.
- self.assert_multi_line_lint(
- 'if (motivated) {\n'
- ' if (liquid)\n'
- ' return money;\n'
- '} else if (tired)\n'
- ' break;\n',
- '')
- self.assert_multi_line_lint(
- 'if (condition)\n'
- ' doSomething();\n'
- 'else if (otherCondition)\n'
- ' doSomethingElse();\n',
- '')
- self.assert_multi_line_lint(
- 'if (condition)\n'
- ' doSomething();\n'
- 'else\n'
- ' doSomethingElse();\n',
- '')
- self.assert_multi_line_lint(
- 'if (condition)\n'
- ' returnValue = foo;\n'
- 'else if (otherCondition)\n'
- ' returnValue = bar;\n',
- '')
- self.assert_multi_line_lint(
- 'if (condition)\n'
- ' returnValue = foo;\n'
- 'else\n'
- ' returnValue = bar;\n',
- '')
- self.assert_multi_line_lint(
- 'if (condition)\n'
- ' doSomething();\n'
- 'else if (liquid)\n'
- ' return money;\n'
- 'else if (broke)\n'
- ' return favor;\n'
- 'else\n'
- ' sleep(28800);\n',
- '')
- self.assert_multi_line_lint(
- 'if (liquid) {\n'
- ' prepare();\n'
- ' return money;\n'
- '} else if (greedy) {\n'
- ' keep();\n'
- ' return nothing;\n'
- '}\n',
- 'An else if statement should be written as an if statement when the '
- 'prior "if" concludes with a return, break, continue or goto statement.'
- ' [readability/control_flow] [4]')
- self.assert_multi_line_lint(
- ' if (stupid) {\n'
- 'infiniteLoop:\n'
- ' goto infiniteLoop;\n'
- ' } else if (evil)\n'
- ' goto hell;\n',
- 'An else if statement should be written as an if statement when the '
- 'prior "if" concludes with a return, break, continue or goto statement.'
- ' [readability/control_flow] [4]')
- self.assert_multi_line_lint(
- 'if (liquid)\n'
- '{\n'
- ' prepare();\n'
- ' return money;\n'
- '}\n'
- 'else if (greedy)\n'
- ' keep();\n',
- ['This { should be at the end of the previous line [whitespace/braces] [4]',
- 'An else should appear on the same line as the preceding } [whitespace/newline] [4]',
- 'An else if statement should be written as an if statement when the '
- 'prior "if" concludes with a return, break, continue or goto statement.'
- ' [readability/control_flow] [4]'])
- self.assert_multi_line_lint(
- 'if (gone)\n'
- ' return;\n'
- 'else if (here)\n'
- ' go();\n',
- 'An else if statement should be written as an if statement when the '
- 'prior "if" concludes with a return, break, continue or goto statement.'
- ' [readability/control_flow] [4]')
- self.assert_multi_line_lint(
- 'if (gone)\n'
- ' return;\n'
- 'else\n'
- ' go();\n',
- 'An else statement can be removed when the prior "if" concludes '
- 'with a return, break, continue or goto statement.'
- ' [readability/control_flow] [4]')
- self.assert_multi_line_lint(
- 'if (motivated) {\n'
- ' prepare();\n'
- ' continue;\n'
- '} else {\n'
- ' cleanUp();\n'
- ' break;\n'
- '}\n',
- 'An else statement can be removed when the prior "if" concludes '
- 'with a return, break, continue or goto statement.'
- ' [readability/control_flow] [4]')
- self.assert_multi_line_lint(
- 'if (tired)\n'
- ' break;\n'
- 'else {\n'
- ' prepare();\n'
- ' continue;\n'
- '}\n',
- 'An else statement can be removed when the prior "if" concludes '
- 'with a return, break, continue or goto statement.'
- ' [readability/control_flow] [4]')
-
- def test_braces(self):
- # 1. Function definitions: place each brace on its own line.
- self.assert_multi_line_lint(
- 'int main()\n'
- '{\n'
- ' doSomething();\n'
- '}\n',
- '')
- self.assert_multi_line_lint(
- 'int main() {\n'
- ' doSomething();\n'
- '}\n',
- 'Place brace on its own line for function definitions. [whitespace/braces] [4]')
-
- # 2. Other braces: place the open brace on the line preceding the
- # code block; place the close brace on its own line.
- self.assert_multi_line_lint(
- 'class MyClass {\n'
- ' int foo;\n'
- '};\n',
- '')
- self.assert_multi_line_lint(
- 'namespace WebCore {\n'
- 'int foo;\n'
- '};\n',
- '')
- self.assert_multi_line_lint(
- 'for (int i = 0; i < 10; i++) {\n'
- ' DoSomething();\n'
- '};\n',
- '')
- self.assert_multi_line_lint(
- 'class MyClass\n'
- '{\n'
- ' int foo;\n'
- '};\n',
- 'This { should be at the end of the previous line [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- 'if (condition)\n'
- '{\n'
- ' int foo;\n'
- '}\n',
- 'This { should be at the end of the previous line [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- 'for (int i = 0; i < 10; i++)\n'
- '{\n'
- ' int foo;\n'
- '}\n',
- 'This { should be at the end of the previous line [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- 'while (true)\n'
- '{\n'
- ' int foo;\n'
- '}\n',
- 'This { should be at the end of the previous line [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- 'foreach (Foo* foo, foos)\n'
- '{\n'
- ' int bar;\n'
- '}\n',
- 'This { should be at the end of the previous line [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- 'switch (type)\n'
- '{\n'
- 'case foo: return;\n'
- '}\n',
- 'This { should be at the end of the previous line [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- 'if (condition)\n'
- '{\n'
- ' int foo;\n'
- '}\n',
- 'This { should be at the end of the previous line [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- 'for (int i = 0; i < 10; i++)\n'
- '{\n'
- ' int foo;\n'
- '}\n',
- 'This { should be at the end of the previous line [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- 'while (true)\n'
- '{\n'
- ' int foo;\n'
- '}\n',
- 'This { should be at the end of the previous line [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- 'switch (type)\n'
- '{\n'
- 'case foo: return;\n'
- '}\n',
- 'This { should be at the end of the previous line [whitespace/braces] [4]')
- self.assert_multi_line_lint(
- 'else if (type)\n'
- '{\n'
- 'case foo: return;\n'
- '}\n',
- 'This { should be at the end of the previous line [whitespace/braces] [4]')
-
- # 3. One-line control clauses should not use braces unless
- # comments are included or a single statement spans multiple
- # lines.
- self.assert_multi_line_lint(
- 'if (true) {\n'
- ' int foo;\n'
- '}\n',
- 'One line control clauses should not use braces. [whitespace/braces] [4]')
-
- self.assert_multi_line_lint(
- 'for (; foo; bar) {\n'
- ' int foo;\n'
- '}\n',
- 'One line control clauses should not use braces. [whitespace/braces] [4]')
-
- self.assert_multi_line_lint(
- 'foreach (foo, foos) {\n'
- ' int bar;\n'
- '}\n',
- 'One line control clauses should not use braces. [whitespace/braces] [4]')
-
- self.assert_multi_line_lint(
- 'while (true) {\n'
- ' int foo;\n'
- '}\n',
- 'One line control clauses should not use braces. [whitespace/braces] [4]')
-
- self.assert_multi_line_lint(
- 'if (true)\n'
- ' int foo;\n'
- 'else {\n'
- ' int foo;\n'
- '}\n',
- 'One line control clauses should not use braces. [whitespace/braces] [4]')
-
- self.assert_multi_line_lint(
- 'if (true) {\n'
- ' int foo;\n'
- '} else\n'
- ' int foo;\n',
- 'One line control clauses should not use braces. [whitespace/braces] [4]')
-
- self.assert_multi_line_lint(
- 'if (true) {\n'
- ' // Some comment\n'
- ' int foo;\n'
- '}\n',
- '')
-
- self.assert_multi_line_lint(
- 'if (true) {\n'
- ' myFunction(reallyLongParam1, reallyLongParam2,\n'
- ' reallyLongParam3);\n'
- '}\n',
- '')
-
- # 4. Control clauses without a body should use empty braces.
- self.assert_multi_line_lint(
- 'for ( ; current; current = current->next) { }\n',
- '')
- self.assert_multi_line_lint(
- 'for ( ; current;\n'
- ' current = current->next) {}\n',
- '')
- self.assert_multi_line_lint(
- 'for ( ; current; current = current->next);\n',
- 'Semicolon defining empty statement for this loop. Use { } instead. [whitespace/semicolon] [5]')
- self.assert_multi_line_lint(
- 'while (true);\n',
- 'Semicolon defining empty statement for this loop. Use { } instead. [whitespace/semicolon] [5]')
- self.assert_multi_line_lint(
- '} while (true);\n',
- '')
-
- def test_null_false_zero(self):
- # 1. In C++, the null pointer value should be written as 0. In C,
- # it should be written as NULL. In Objective-C and Objective-C++,
- # follow the guideline for C or C++, respectively, but use nil to
- # represent a null Objective-C object.
- self.assert_lint(
- 'functionCall(NULL)',
- 'Use 0 instead of NULL.'
- ' [readability/null] [5]',
- 'foo.cpp')
- self.assert_lint(
- "// Don't use NULL in comments since it isn't in code.",
- 'Use 0 instead of NULL.'
- ' [readability/null] [4]',
- 'foo.cpp')
- self.assert_lint(
- '"A string with NULL" // and a comment with NULL is tricky to flag correctly in cpp_style.',
- 'Use 0 instead of NULL.'
- ' [readability/null] [4]',
- 'foo.cpp')
- self.assert_lint(
- '"A string containing NULL is ok"',
- '',
- 'foo.cpp')
- self.assert_lint(
- 'if (aboutNULL)',
- '',
- 'foo.cpp')
- self.assert_lint(
- 'myVariable = NULLify',
- '',
- 'foo.cpp')
- # Make sure that the NULL check does not apply to C and Objective-C files.
- self.assert_lint(
- 'functionCall(NULL)',
- '',
- 'foo.c')
- self.assert_lint(
- 'functionCall(NULL)',
- '',
- 'foo.m')
-
- # Make sure that the NULL check does not apply to g_object_{set,get} and
- # g_str{join,concat}
- self.assert_lint(
- 'g_object_get(foo, "prop", &bar, NULL);',
- '')
- self.assert_lint(
- 'g_object_set(foo, "prop", bar, NULL);',
- '')
- self.assert_lint(
- 'g_build_filename(foo, bar, NULL);',
- '')
- self.assert_lint(
- 'gst_bin_add_many(foo, bar, boo, NULL);',
- '')
- self.assert_lint(
- 'gst_bin_remove_many(foo, bar, boo, NULL);',
- '')
- self.assert_lint(
- 'gst_element_link_many(foo, bar, boo, NULL);',
- '')
- self.assert_lint(
- 'gst_element_unlink_many(foo, bar, boo, NULL);',
- '')
- self.assert_lint(
- 'gchar* result = g_strconcat("part1", "part2", "part3", NULL);',
- '')
- self.assert_lint(
- 'gchar* result = g_strconcat("part1", NULL);',
- '')
- self.assert_lint(
- 'gchar* result = g_strjoin(",", "part1", "part2", "part3", NULL);',
- '')
- self.assert_lint(
- 'gchar* result = g_strjoin(",", "part1", NULL);',
- '')
- self.assert_lint(
- 'gchar* result = gdk_pixbuf_save_to_callback(pixbuf, function, data, type, error, NULL);',
- '')
- self.assert_lint(
- 'gchar* result = gdk_pixbuf_save_to_buffer(pixbuf, function, data, type, error, NULL);',
- '')
- self.assert_lint(
- 'gchar* result = gdk_pixbuf_save_to_stream(pixbuf, function, data, type, error, NULL);',
- '')
-
- # 2. C++ and C bool values should be written as true and
- # false. Objective-C BOOL values should be written as YES and NO.
- # FIXME: Implement this.
-
- # 3. Tests for true/false, null/non-null, and zero/non-zero should
- # all be done without equality comparisons.
- self.assert_lint(
- 'if (count == 0)',
- 'Tests for true/false, null/non-null, and zero/non-zero should all be done without equality comparisons.'
- ' [readability/comparison_to_zero] [5]')
- self.assert_lint_one_of_many_errors_re(
- 'if (string != NULL)',
- r'Tests for true/false, null/non-null, and zero/non-zero should all be done without equality comparisons\.')
- self.assert_lint(
- 'if (condition == true)',
- 'Tests for true/false, null/non-null, and zero/non-zero should all be done without equality comparisons.'
- ' [readability/comparison_to_zero] [5]')
- self.assert_lint(
- 'if (myVariable != /* Why would anyone put a comment here? */ false)',
- 'Tests for true/false, null/non-null, and zero/non-zero should all be done without equality comparisons.'
- ' [readability/comparison_to_zero] [5]')
-
- self.assert_lint(
- 'if (0 /* This comment also looks odd to me. */ != aLongerVariableName)',
- 'Tests for true/false, null/non-null, and zero/non-zero should all be done without equality comparisons.'
- ' [readability/comparison_to_zero] [5]')
- self.assert_lint_one_of_many_errors_re(
- 'if (NULL == thisMayBeNull)',
- r'Tests for true/false, null/non-null, and zero/non-zero should all be done without equality comparisons\.')
- self.assert_lint(
- 'if (true != anotherCondition)',
- 'Tests for true/false, null/non-null, and zero/non-zero should all be done without equality comparisons.'
- ' [readability/comparison_to_zero] [5]')
- self.assert_lint(
- 'if (false == myBoolValue)',
- 'Tests for true/false, null/non-null, and zero/non-zero should all be done without equality comparisons.'
- ' [readability/comparison_to_zero] [5]')
-
- self.assert_lint(
- 'if (fontType == trueType)',
- '')
- self.assert_lint(
- 'if (othertrue == fontType)',
- '')
-
- def test_using_std(self):
- self.assert_lint(
- 'using std::min;',
- "Use 'using namespace std;' instead of 'using std::min;'."
- " [build/using_std] [4]",
- 'foo.cpp')
-
- def test_max_macro(self):
- self.assert_lint(
- 'int i = MAX(0, 1);',
- '',
- 'foo.c')
-
- self.assert_lint(
- 'int i = MAX(0, 1);',
- 'Use std::max() or std::max<type>() instead of the MAX() macro.'
- ' [runtime/max_min_macros] [4]',
- 'foo.cpp')
-
- self.assert_lint(
- 'inline int foo() { return MAX(0, 1); }',
- 'Use std::max() or std::max<type>() instead of the MAX() macro.'
- ' [runtime/max_min_macros] [4]',
- 'foo.h')
-
- def test_min_macro(self):
- self.assert_lint(
- 'int i = MIN(0, 1);',
- '',
- 'foo.c')
-
- self.assert_lint(
- 'int i = MIN(0, 1);',
- 'Use std::min() or std::min<type>() instead of the MIN() macro.'
- ' [runtime/max_min_macros] [4]',
- 'foo.cpp')
-
- self.assert_lint(
- 'inline int foo() { return MIN(0, 1); }',
- 'Use std::min() or std::min<type>() instead of the MIN() macro.'
- ' [runtime/max_min_macros] [4]',
- 'foo.h')
-
- def test_names(self):
- name_underscore_error_message = " is incorrectly named. Don't use underscores in your identifier names. [readability/naming] [4]"
- name_tooshort_error_message = " is incorrectly named. Don't use the single letter 'l' as an identifier name. [readability/naming] [4]"
-
- # Basic cases from WebKit style guide.
- self.assert_lint('struct Data;', '')
- self.assert_lint('size_t bufferSize;', '')
- self.assert_lint('class HTMLDocument;', '')
- self.assert_lint('String mimeType();', '')
- self.assert_lint('size_t buffer_size;',
- 'buffer_size' + name_underscore_error_message)
- self.assert_lint('short m_length;', '')
- self.assert_lint('short _length;',
- '_length' + name_underscore_error_message)
- self.assert_lint('short length_;',
- 'length_' + name_underscore_error_message)
- self.assert_lint('unsigned _length;',
- '_length' + name_underscore_error_message)
- self.assert_lint('unsigned int _length;',
- '_length' + name_underscore_error_message)
- self.assert_lint('unsigned long long _length;',
- '_length' + name_underscore_error_message)
-
- # Variable name 'l' is easy to confuse with '1'
- self.assert_lint('int l;', 'l' + name_tooshort_error_message)
- self.assert_lint('size_t l;', 'l' + name_tooshort_error_message)
- self.assert_lint('long long l;', 'l' + name_tooshort_error_message)
-
- # Pointers, references, functions, templates, and adjectives.
- self.assert_lint('char* under_score;',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('const int UNDER_SCORE;',
- 'UNDER_SCORE' + name_underscore_error_message)
- self.assert_lint('static inline const char const& const under_score;',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('WebCore::RenderObject* under_score;',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('int func_name();',
- 'func_name' + name_underscore_error_message)
- self.assert_lint('RefPtr<RenderObject*> under_score;',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('WTF::Vector<WTF::RefPtr<const RenderObject* const> > under_score;',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('int under_score[];',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('struct dirent* under_score;',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('long under_score;',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('long long under_score;',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('long double under_score;',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('long long int under_score;',
- 'under_score' + name_underscore_error_message)
-
- # Declarations in control statement.
- self.assert_lint('if (int under_score = 42) {',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('else if (int under_score = 42) {',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('for (int under_score = 42; cond; i++) {',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('while (foo & under_score = bar) {',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('for (foo * under_score = p; cond; i++) {',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('for (foo * under_score; cond; i++) {',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('while (foo & value_in_thirdparty_library) {', '')
- self.assert_lint('while (foo * value_in_thirdparty_library) {', '')
- self.assert_lint('if (mli && S_OK == mli->foo()) {', '')
-
- # More member variables and functions.
- self.assert_lint('int SomeClass::s_validName', '')
- self.assert_lint('int m_under_score;',
- 'm_under_score' + name_underscore_error_message)
- self.assert_lint('int SomeClass::s_under_score = 0;',
- 'SomeClass::s_under_score' + name_underscore_error_message)
- self.assert_lint('int SomeClass::under_score = 0;',
- 'SomeClass::under_score' + name_underscore_error_message)
-
- # Other statements.
- self.assert_lint('return INT_MAX;', '')
- self.assert_lint('return_t under_score;',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('goto under_score;',
- 'under_score' + name_underscore_error_message)
- self.assert_lint('delete static_cast<Foo*>(p);', '')
-
- # Multiple variables in one line.
- self.assert_lint('void myFunction(int variable1, int another_variable);',
- 'another_variable' + name_underscore_error_message)
- self.assert_lint('int variable1, another_variable;',
- 'another_variable' + name_underscore_error_message)
- self.assert_lint('int first_variable, secondVariable;',
- 'first_variable' + name_underscore_error_message)
- self.assert_lint('void my_function(int variable_1, int variable_2);',
- ['my_function' + name_underscore_error_message,
- 'variable_1' + name_underscore_error_message,
- 'variable_2' + name_underscore_error_message])
- self.assert_lint('for (int variable_1, variable_2;;) {',
- ['variable_1' + name_underscore_error_message,
- 'variable_2' + name_underscore_error_message])
-
- # There is an exception for op code functions but only in the JavaScriptCore directory.
- self.assert_lint('void this_op_code(int var1, int var2)', '', 'JavaScriptCore/foo.cpp')
- self.assert_lint('void op_code(int var1, int var2)', '', 'JavaScriptCore/foo.cpp')
- self.assert_lint('void this_op_code(int var1, int var2)', 'this_op_code' + name_underscore_error_message)
-
- # GObject requires certain magical names in class declarations.
- self.assert_lint('void webkit_dom_object_init();', '')
- self.assert_lint('void webkit_dom_object_class_init();', '')
-
- # There is an exception for some unit tests that begin with "tst_".
- self.assert_lint('void tst_QWebFrame::arrayObjectEnumerable(int var1, int var2)', '')
-
- # The Qt API uses names that begin with "qt_".
- self.assert_lint('void QTFrame::qt_drt_is_awesome(int var1, int var2)', '')
- self.assert_lint('void qt_drt_is_awesome(int var1, int var2);', '')
-
- # Cairo forward-declarations should not be a failure.
- self.assert_lint('typedef struct _cairo cairo_t;', '')
- self.assert_lint('typedef struct _cairo_surface cairo_surface_t;', '')
- self.assert_lint('typedef struct _cairo_scaled_font cairo_scaled_font_t;', '')
-
- # NPAPI functions that start with NPN_, NPP_ or NP_ are allowed.
- self.assert_lint('void NPN_Status(NPP, const char*)', '')
- self.assert_lint('NPError NPP_SetWindow(NPP instance, NPWindow *window)', '')
- self.assert_lint('NPObject* NP_Allocate(NPP, NPClass*)', '')
-
- # const_iterator is allowed as well.
- self.assert_lint('typedef VectorType::const_iterator const_iterator;', '')
-
- # vm_throw is allowed as well.
- self.assert_lint('int vm_throw;', '')
-
- # Bitfields.
- self.assert_lint('unsigned _fillRule : 1;',
- '_fillRule' + name_underscore_error_message)
-
- # new operators in initialization.
- self.assert_lint('OwnPtr<uint32_t> variable(new uint32_t);', '')
- self.assert_lint('OwnPtr<uint32_t> variable(new (expr) uint32_t);', '')
- self.assert_lint('OwnPtr<uint32_t> under_score(new uint32_t);',
- 'under_score' + name_underscore_error_message)
-
-
- def test_comments(self):
- # A comment at the beginning of a line is ok.
- self.assert_lint('// comment', '')
- self.assert_lint(' // comment', '')
-
- self.assert_lint('} // namespace WebCore',
- 'One space before end of line comments'
- ' [whitespace/comments] [5]')
-
- def test_other(self):
- # FIXME: Implement this.
- pass
-
-
-class CppCheckerTest(unittest.TestCase):
-
- """Tests CppChecker class."""
-
- def mock_handle_style_error(self):
- pass
-
- def _checker(self):
- return CppChecker("foo", "h", self.mock_handle_style_error, 3)
-
- def test_init(self):
- """Test __init__ constructor."""
- checker = self._checker()
- self.assertEquals(checker.file_extension, "h")
- self.assertEquals(checker.file_path, "foo")
- self.assertEquals(checker.handle_style_error, self.mock_handle_style_error)
- self.assertEquals(checker.min_confidence, 3)
-
- def test_eq(self):
- """Test __eq__ equality function."""
- checker1 = self._checker()
- checker2 = self._checker()
-
- # == calls __eq__.
- self.assertTrue(checker1 == checker2)
-
- def mock_handle_style_error2(self):
- pass
-
- # Verify that a difference in any argument cause equality to fail.
- checker = CppChecker("foo", "h", self.mock_handle_style_error, 3)
- self.assertFalse(checker == CppChecker("bar", "h", self.mock_handle_style_error, 3))
- self.assertFalse(checker == CppChecker("foo", "c", self.mock_handle_style_error, 3))
- self.assertFalse(checker == CppChecker("foo", "h", mock_handle_style_error2, 3))
- self.assertFalse(checker == CppChecker("foo", "h", self.mock_handle_style_error, 4))
-
- def test_ne(self):
- """Test __ne__ inequality function."""
- checker1 = self._checker()
- checker2 = self._checker()
-
- # != calls __ne__.
- # By default, __ne__ always returns true on different objects.
- # Thus, just check the distinguishing case to verify that the
- # code defines __ne__.
- self.assertFalse(checker1 != checker2)
-
-
-def tearDown():
- """A global check to make sure all error-categories have been tested.
-
- The main tearDown() routine is the only code we can guarantee will be
- run after all other tests have been executed.
- """
- try:
- if _run_verifyallcategoriesseen:
- ErrorCollector(None).verify_all_categories_are_seen()
- except NameError:
- # If nobody set the global _run_verifyallcategoriesseen, then
- # we assume we shouldn't run the test
- pass
-
-if __name__ == '__main__':
- import sys
- # We don't want to run the verify_all_categories_are_seen() test unless
- # we're running the full test suite: if we only run one test,
- # obviously we're not going to see all the error categories. So we
- # only run verify_all_categories_are_seen() when no commandline flags
- # are passed in.
- global _run_verifyallcategoriesseen
- _run_verifyallcategoriesseen = (len(sys.argv) == 1)
-
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/python.py b/WebKitTools/Scripts/webkitpy/style/checkers/python.py
deleted file mode 100644
index 70d4450..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checkers/python.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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.
-
-"""Supports checking WebKit style in Python files."""
-
-from ...style_references import pep8
-
-
-class PythonChecker(object):
-
- """Processes text lines for checking style."""
-
- def __init__(self, file_path, handle_style_error):
- self._file_path = file_path
- self._handle_style_error = handle_style_error
-
- def check(self, lines):
- # Initialize pep8.options, which is necessary for
- # Checker.check_all() to execute.
- pep8.process_options(arglist=[self._file_path])
-
- checker = pep8.Checker(self._file_path)
-
- def _pep8_handle_error(line_number, offset, text, check):
- # FIXME: Incorporate the character offset into the error output.
- # This will require updating the error handler __call__
- # signature to include an optional "offset" parameter.
- pep8_code = text[:4]
- pep8_message = text[5:]
-
- category = "pep8/" + pep8_code
-
- self._handle_style_error(line_number, category, 5, pep8_message)
-
- checker.report_error = _pep8_handle_error
-
- errors = checker.check_all()
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/python_unittest.py b/WebKitTools/Scripts/webkitpy/style/checkers/python_unittest.py
deleted file mode 100644
index e003eb8..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checkers/python_unittest.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# 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 for python.py."""
-
-import os
-import unittest
-
-from python import PythonChecker
-
-
-class PythonCheckerTest(unittest.TestCase):
-
- """Tests the PythonChecker class."""
-
- def test_init(self):
- """Test __init__() method."""
- def _mock_handle_style_error(self):
- pass
-
- checker = PythonChecker("foo.txt", _mock_handle_style_error)
- self.assertEquals(checker._file_path, "foo.txt")
- self.assertEquals(checker._handle_style_error,
- _mock_handle_style_error)
-
- def test_check(self):
- """Test check() method."""
- errors = []
-
- def _mock_handle_style_error(line_number, category, confidence,
- message):
- error = (line_number, category, confidence, message)
- errors.append(error)
-
- current_dir = os.path.dirname(__file__)
- file_path = os.path.join(current_dir, "python_unittest_input.py")
-
- checker = PythonChecker(file_path, _mock_handle_style_error)
- checker.check(lines=[])
-
- self.assertEquals(len(errors), 1)
- self.assertEquals(errors[0],
- (2, "pep8/W291", 5, "trailing whitespace"))
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/python_unittest_input.py b/WebKitTools/Scripts/webkitpy/style/checkers/python_unittest_input.py
deleted file mode 100644
index 9f1d118..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checkers/python_unittest_input.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# This file is sample input for python_unittest.py and includes a single
-# error which is an extra space at the end of this line.
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/test_expectations.py b/WebKitTools/Scripts/webkitpy/style/checkers/test_expectations.py
deleted file mode 100644
index d2d67f3..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checkers/test_expectations.py
+++ /dev/null
@@ -1,123 +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.
-
-"""Checks WebKit style for test_expectations files."""
-
-import logging
-import os
-import re
-import sys
-
-from common import TabChecker
-from webkitpy.style_references import port
-from webkitpy.style_references import test_expectations
-
-_log = logging.getLogger("webkitpy.style.checkers.test_expectations")
-
-
-class ChromiumOptions(object):
- """A mock object for creating chromium port object.
-
- port.get() requires an options object which has 'chromium' attribute to create
- chromium port object for each platform. This class mocks such object.
- """
- def __init__(self):
- self.chromium = True
- self.use_drt = True
-
-
-class TestExpectationsChecker(object):
- """Processes test_expectations.txt lines for validating the syntax."""
-
- categories = set(['test/expectations'])
-
- def __init__(self, file_path, handle_style_error):
- self._file_path = file_path
- self._handle_style_error = handle_style_error
- self._tab_checker = TabChecker(file_path, handle_style_error)
- self._output_regex = re.compile('Line:(?P<line>\d+)\s*(?P<message>.+)')
- # Determining the port of this expectations.
- try:
- port_name = self._file_path.split(os.sep)[-2]
- if port_name == "chromium":
- options = ChromiumOptions()
- self._port_obj = port.get(port_name=None, options=options)
- else:
- self._port_obj = port.get(port_name=port_name)
- except:
- # Using 'test' port when we couldn't determine the port for this
- # expectations.
- _log.warn("Could not determine the port for %s. "
- "Using 'test' port, but platform-specific expectations "
- "will fail the check." % self._file_path)
- self._port_obj = port.get('test')
- self._port_to_check = self._port_obj.test_platform_name()
- # Suppress error messages of test_expectations module since they will be
- # reported later.
- log = logging.getLogger("webkitpy.layout_tests.layout_package."
- "test_expectations")
- log.setLevel(logging.CRITICAL)
-
- def _handle_error_message(self, lineno, message, confidence):
- pass
-
- def check_test_expectations(self, expectations_str, tests=None, overrides=None):
- errors = []
- expectations = None
- try:
- expectations = test_expectations.TestExpectationsFile(
- port=self._port_obj, expectations=expectations_str, full_test_list=tests,
- test_platform_name=self._port_to_check, is_debug_mode=False,
- is_lint_mode=True, suppress_errors=False, overrides=overrides)
- except SyntaxError, error:
- errors = str(error).splitlines()
-
- for error in errors:
- matched = self._output_regex.match(error)
- if matched:
- lineno, message = matched.group('line', 'message')
- self._handle_style_error(int(lineno), 'test/expectations', 5, message)
-
- if expectations:
- for error in expectations.get_non_fatal_errors():
- matched = self._output_regex.match(error)
- if matched:
- lineno, message = matched.group('line', 'message')
- self._handle_style_error(int(lineno), 'test/expectations', 2, message)
-
- def check_tabs(self, lines):
- self._tab_checker.check(lines)
-
- def check(self, lines):
- overrides = self._port_obj.test_expectations_overrides()
- expectations = '\n'.join(lines)
- self.check_test_expectations(expectations_str=expectations,
- tests=None,
- overrides=overrides)
- # Warn tabs in lines as well
- self.check_tabs(lines)
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/test_expectations_unittest.py b/WebKitTools/Scripts/webkitpy/style/checkers/test_expectations_unittest.py
deleted file mode 100644
index 31f0b40..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checkers/test_expectations_unittest.py
+++ /dev/null
@@ -1,168 +0,0 @@
-#!/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 test_expectations.py."""
-
-import os
-import sys
-import unittest
-
-# We need following workaround hack to run this unit tests in stand-alone.
-try:
- d = os.path.dirname(__file__)
-except NameError:
- d = os.path.dirname(sys.argv[0])
-sys.path.append(os.path.abspath(os.path.join(d, '../../../')))
-
-from test_expectations import TestExpectationsChecker
-from webkitpy.style_references import port
-from webkitpy.style_references import test_expectations as test_expectations_style
-
-
-class ErrorCollector(object):
- """An error handler class for unit tests."""
-
- def __init__(self):
- self._errors = []
-
- def __call__(self, lineno, category, confidence, message):
- self._errors.append('%s [%s] [%d]' % (message, category, confidence))
-
- def get_errors(self):
- return ''.join(self._errors)
-
- def reset_errors(self):
- self._errors = []
-
-
-class TestExpectationsTestCase(unittest.TestCase):
- """TestCase for test_expectations.py"""
-
- def setUp(self):
- self._error_collector = ErrorCollector()
- port_obj = port.get('test')
- self._test_file = os.path.join(port_obj.layout_tests_dir(), 'passes/text.html')
-
- def process_expectations(self, expectations, overrides=None):
- self._checker = TestExpectationsChecker()
-
- def assert_lines_lint(self, lines, expected):
- self._error_collector.reset_errors()
- checker = TestExpectationsChecker('test/test_expectations.txt',
- self._error_collector)
- checker.check_test_expectations(expectations_str='\n'.join(lines),
- tests=[self._test_file],
- overrides=None)
- checker.check_tabs(lines)
- self.assertEqual(expected, self._error_collector.get_errors())
-
- def test_valid_expectations(self):
- self.assert_lines_lint(
- ["passes/text.html = PASS"],
- "")
- self.assert_lines_lint(
- ["passes/text.html = FAIL PASS"],
- "")
- self.assert_lines_lint(
- ["passes/text.html = CRASH TIMEOUT FAIL PASS"],
- "")
- self.assert_lines_lint(
- ["BUG1234 MAC : passes/text.html = PASS FAIL"],
- "")
- self.assert_lines_lint(
- ["SKIP BUG1234 : passes/text.html = TIMEOUT PASS"],
- "")
- self.assert_lines_lint(
- ["BUG1234 DEBUG : passes/text.html = TIMEOUT PASS"],
- "")
- self.assert_lines_lint(
- ["BUG1234 DEBUG SKIP : passes/text.html = TIMEOUT PASS"],
- "")
- self.assert_lines_lint(
- ["BUG1234 MAC DEBUG SKIP : passes/text.html = TIMEOUT PASS"],
- "")
- self.assert_lines_lint(
- ["BUG1234 DEBUG MAC : passes/text.html = TIMEOUT PASS"],
- "")
- self.assert_lines_lint(
- ["SLOW BUG1234 : passes/text.html = PASS"],
- "")
- self.assert_lines_lint(
- ["WONTFIX SKIP : passes/text.html = TIMEOUT"],
- "")
-
- def test_valid_modifiers(self):
- self.assert_lines_lint(
- ["INVALID-MODIFIER : passes/text.html = PASS"],
- "Invalid modifier for test: invalid-modifier "
- "passes/text.html [test/expectations] [5]")
- self.assert_lines_lint(
- ["SKIP : passes/text.html = PASS"],
- "Test lacks BUG modifier. "
- "passes/text.html [test/expectations] [2]")
-
- def test_expectation_errors(self):
- self.assert_lines_lint(
- ["missing expectations"],
- "Missing expectations. ['missing expectations'] [test/expectations] [5]")
- self.assert_lines_lint(
- ["SLOW : passes/text.html = TIMEOUT"],
- "A test can not be both slow and timeout. "
- "If it times out indefinitely, then it should be just timeout. "
- "passes/text.html [test/expectations] [5]")
- self.assert_lines_lint(
- ["does/not/exist.html = FAIL"],
- "Path does not exist. does/not/exist.html [test/expectations] [2]")
-
- def test_parse_expectations(self):
- self.assert_lines_lint(
- ["passes/text.html = PASS"],
- "")
- self.assert_lines_lint(
- ["passes/text.html = UNSUPPORTED"],
- "Unsupported expectation: unsupported "
- "passes/text.html [test/expectations] [5]")
- self.assert_lines_lint(
- ["passes/text.html = PASS UNSUPPORTED"],
- "Unsupported expectation: unsupported "
- "passes/text.html [test/expectations] [5]")
-
- def test_already_seen_test(self):
- self.assert_lines_lint(
- ["passes/text.html = PASS",
- "passes/text.html = TIMEOUT"],
- "Duplicate expectation. %s [test/expectations] [5]" % self._test_file)
-
- def test_tab(self):
- self.assert_lines_lint(
- ["\tpasses/text.html = PASS"],
- "Line contains tab character. [whitespace/tab] [5]")
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/text.py b/WebKitTools/Scripts/webkitpy/style/checkers/text.py
deleted file mode 100644
index 1147658..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checkers/text.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (C) 2009 Google Inc. All rights reserved.
-# 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:
-#
-# * 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.
-
-"""Checks WebKit style for text files."""
-
-from common import TabChecker
-
-class TextChecker(object):
-
- """Processes text lines for checking style."""
-
- def __init__(self, file_path, handle_style_error):
- self.file_path = file_path
- self.handle_style_error = handle_style_error
- self._tab_checker = TabChecker(file_path, handle_style_error)
-
- def check(self, lines):
- self._tab_checker.check(lines)
-
-
-# FIXME: Remove this function (requires refactoring unit tests).
-def process_file_data(filename, lines, error):
- checker = TextChecker(filename, error)
- checker.check(lines)
-
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/text_unittest.py b/WebKitTools/Scripts/webkitpy/style/checkers/text_unittest.py
deleted file mode 100644
index ced49a9..0000000
--- a/WebKitTools/Scripts/webkitpy/style/checkers/text_unittest.py
+++ /dev/null
@@ -1,94 +0,0 @@
-#!/usr/bin/python
-# 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
-# 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 test for text_style.py."""
-
-import unittest
-
-import text as text_style
-from text import TextChecker
-
-class TextStyleTestCase(unittest.TestCase):
- """TestCase for text_style.py"""
-
- def assertNoError(self, lines):
- """Asserts that the specified lines has no errors."""
- self.had_error = False
-
- def error_for_test(line_number, category, confidence, message):
- """Records if an error occurs."""
- self.had_error = True
-
- text_style.process_file_data('', lines, error_for_test)
- self.assert_(not self.had_error, '%s should not have any errors.' % lines)
-
- def assertError(self, lines, expected_line_number):
- """Asserts that the specified lines has an error."""
- self.had_error = False
-
- def error_for_test(line_number, category, confidence, message):
- """Checks if the expected error occurs."""
- self.assertEquals(expected_line_number, line_number)
- self.assertEquals('whitespace/tab', category)
- self.had_error = True
-
- text_style.process_file_data('', lines, error_for_test)
- self.assert_(self.had_error, '%s should have an error [whitespace/tab].' % lines)
-
-
- def test_no_error(self):
- """Tests for no error cases."""
- self.assertNoError([''])
- self.assertNoError(['abc def', 'ggg'])
-
-
- def test_error(self):
- """Tests for error cases."""
- self.assertError(['2009-12-16\tKent Tamura\t<tkent@chromium.org>'], 1)
- self.assertError(['2009-12-16 Kent Tamura <tkent@chromium.org>',
- '',
- '\tReviewed by NOBODY.'], 3)
-
-
-class TextCheckerTest(unittest.TestCase):
-
- """Tests TextChecker class."""
-
- def mock_handle_style_error(self):
- pass
-
- def test_init(self):
- """Test __init__ constructor."""
- checker = TextChecker("foo.txt", self.mock_handle_style_error)
- self.assertEquals(checker.file_path, "foo.txt")
- self.assertEquals(checker.handle_style_error, self.mock_handle_style_error)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/style/error_handlers.py b/WebKitTools/Scripts/webkitpy/style/error_handlers.py
deleted file mode 100644
index 0bede24..0000000
--- a/WebKitTools/Scripts/webkitpy/style/error_handlers.py
+++ /dev/null
@@ -1,159 +0,0 @@
-# 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.
-
-"""Defines style error handler classes.
-
-A style error handler is a function to call when a style error is
-found. Style error handlers can also have state. A class that represents
-a style error handler should implement the following methods.
-
-Methods:
-
- __call__(self, line_number, category, confidence, message):
-
- Handle the occurrence of a style error.
-
- Check whether the error is reportable. If so, increment the total
- error count and report the details. Note that error reporting can
- be suppressed after reaching a certain number of reports.
-
- Args:
- line_number: The integer line number of the line containing the error.
- category: The name of the category of the error, for example
- "whitespace/newline".
- confidence: An integer between 1 and 5 inclusive that represents the
- application's level of confidence in the error. The value
- 5 means that we are certain of the problem, and the
- value 1 means that it could be a legitimate construct.
- message: The error message to report.
-
-"""
-
-
-import sys
-
-
-class DefaultStyleErrorHandler(object):
-
- """The default style error handler."""
-
- def __init__(self, file_path, configuration, increment_error_count,
- line_numbers=None):
- """Create a default style error handler.
-
- Args:
- file_path: The path to the file containing the error. This
- is used for reporting to the user.
- configuration: A StyleProcessorConfiguration instance.
- increment_error_count: A function that takes no arguments and
- increments the total count of reportable
- errors.
- line_numbers: An array of line numbers of the lines for which
- style errors should be reported, or None if errors
- for all lines should be reported. When it is not
- None, this array normally contains the line numbers
- corresponding to the modified lines of a patch.
-
- """
- if line_numbers is not None:
- line_numbers = set(line_numbers)
-
- self._file_path = file_path
- self._configuration = configuration
- self._increment_error_count = increment_error_count
- self._line_numbers = line_numbers
-
- # A string to integer dictionary cache of the number of reportable
- # errors per category passed to this instance.
- self._category_totals = {}
-
- # Useful for unit testing.
- def __eq__(self, other):
- """Return whether this instance is equal to another."""
- if self._configuration != other._configuration:
- return False
- if self._file_path != other._file_path:
- return False
- if self._increment_error_count != other._increment_error_count:
- return False
- if self._line_numbers != other._line_numbers:
- return False
-
- return True
-
- # Useful for unit testing.
- def __ne__(self, other):
- # Python does not automatically deduce __ne__ from __eq__.
- return not self.__eq__(other)
-
- def _add_reportable_error(self, category):
- """Increment the error count and return the new category total."""
- self._increment_error_count() # Increment the total.
-
- # Increment the category total.
- if not category in self._category_totals:
- self._category_totals[category] = 1
- else:
- self._category_totals[category] += 1
-
- return self._category_totals[category]
-
- def _max_reports(self, category):
- """Return the maximum number of errors to report."""
- if not category in self._configuration.max_reports_per_category:
- return None
- return self._configuration.max_reports_per_category[category]
-
- def __call__(self, line_number, category, confidence, message):
- """Handle the occurrence of a style error.
-
- See the docstring of this module for more information.
-
- """
- if (self._line_numbers is not None and
- line_number not in self._line_numbers):
- # Then the error occurred in a line that was not modified, so
- # the error is not reportable.
- return
-
- if not self._configuration.is_reportable(category=category,
- confidence_in_error=confidence,
- file_path=self._file_path):
- return
-
- category_total = self._add_reportable_error(category)
-
- max_reports = self._max_reports(category)
-
- if (max_reports is not None) and (category_total > max_reports):
- # Then suppress displaying the error.
- return
-
- self._configuration.write_style_error(category=category,
- confidence_in_error=confidence,
- file_path=self._file_path,
- line_number=line_number,
- message=message)
-
- if category_total == max_reports:
- self._configuration.stderr_write("Suppressing further [%s] reports "
- "for this file.\n" % category)
diff --git a/WebKitTools/Scripts/webkitpy/style/error_handlers_unittest.py b/WebKitTools/Scripts/webkitpy/style/error_handlers_unittest.py
deleted file mode 100644
index 23619cc..0000000
--- a/WebKitTools/Scripts/webkitpy/style/error_handlers_unittest.py
+++ /dev/null
@@ -1,187 +0,0 @@
-# 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 for error_handlers.py."""
-
-
-import unittest
-
-from checker import StyleProcessorConfiguration
-from error_handlers import DefaultStyleErrorHandler
-from filter import FilterConfiguration
-
-
-class DefaultStyleErrorHandlerTest(unittest.TestCase):
-
- """Tests the DefaultStyleErrorHandler class."""
-
- def setUp(self):
- self._error_messages = []
- self._error_count = 0
-
- _category = "whitespace/tab"
- """The category name for the tests in this class."""
-
- _file_path = "foo.h"
- """The file path for the tests in this class."""
-
- def _mock_increment_error_count(self):
- self._error_count += 1
-
- def _mock_stderr_write(self, message):
- self._error_messages.append(message)
-
- def _style_checker_configuration(self):
- """Return a StyleProcessorConfiguration instance for testing."""
- base_rules = ["-whitespace", "+whitespace/tab"]
- filter_configuration = FilterConfiguration(base_rules=base_rules)
-
- return StyleProcessorConfiguration(
- filter_configuration=filter_configuration,
- max_reports_per_category={"whitespace/tab": 2},
- min_confidence=3,
- output_format="vs7",
- stderr_write=self._mock_stderr_write)
-
- def _error_handler(self, configuration, line_numbers=None):
- return DefaultStyleErrorHandler(configuration=configuration,
- file_path=self._file_path,
- increment_error_count=self._mock_increment_error_count,
- line_numbers=line_numbers)
-
- def _check_initialized(self):
- """Check that count and error messages are initialized."""
- self.assertEquals(0, self._error_count)
- self.assertEquals(0, len(self._error_messages))
-
- def _call_error_handler(self, handle_error, confidence, line_number=100):
- """Call the given error handler with a test error."""
- handle_error(line_number=line_number,
- category=self._category,
- confidence=confidence,
- message="message")
-
- def test_eq__true_return_value(self):
- """Test the __eq__() method for the return value of True."""
- handler1 = self._error_handler(configuration=None)
- handler2 = self._error_handler(configuration=None)
-
- self.assertTrue(handler1.__eq__(handler2))
-
- def test_eq__false_return_value(self):
- """Test the __eq__() method for the return value of False."""
- def make_handler(configuration=self._style_checker_configuration(),
- file_path='foo.txt', increment_error_count=lambda: True,
- line_numbers=[100]):
- return DefaultStyleErrorHandler(configuration=configuration,
- file_path=file_path,
- increment_error_count=increment_error_count,
- line_numbers=line_numbers)
-
- handler = make_handler()
-
- # Establish a baseline for our comparisons below.
- self.assertTrue(handler.__eq__(make_handler()))
-
- # Verify that a difference in any argument causes equality to fail.
- self.assertFalse(handler.__eq__(make_handler(configuration=None)))
- self.assertFalse(handler.__eq__(make_handler(file_path='bar.txt')))
- self.assertFalse(handler.__eq__(make_handler(increment_error_count=None)))
- self.assertFalse(handler.__eq__(make_handler(line_numbers=[50])))
-
- def test_ne(self):
- """Test the __ne__() method."""
- # By default, __ne__ always returns true on different objects.
- # Thus, check just the distinguishing case to verify that the
- # code defines __ne__.
- handler1 = self._error_handler(configuration=None)
- handler2 = self._error_handler(configuration=None)
-
- self.assertFalse(handler1.__ne__(handler2))
-
- def test_non_reportable_error(self):
- """Test __call__() with a non-reportable error."""
- self._check_initialized()
- configuration = self._style_checker_configuration()
-
- confidence = 1
- # Confirm the error is not reportable.
- self.assertFalse(configuration.is_reportable(self._category,
- confidence,
- self._file_path))
- error_handler = self._error_handler(configuration)
- self._call_error_handler(error_handler, confidence)
-
- self.assertEquals(0, self._error_count)
- self.assertEquals([], self._error_messages)
-
- # Also serves as a reportable error test.
- def test_max_reports_per_category(self):
- """Test error report suppression in __call__() method."""
- self._check_initialized()
- configuration = self._style_checker_configuration()
- error_handler = self._error_handler(configuration)
-
- confidence = 5
-
- # First call: usual reporting.
- self._call_error_handler(error_handler, confidence)
- self.assertEquals(1, self._error_count)
- self.assertEquals(1, len(self._error_messages))
- self.assertEquals(self._error_messages,
- ["foo.h(100): message [whitespace/tab] [5]\n"])
-
- # Second call: suppression message reported.
- self._call_error_handler(error_handler, confidence)
- # The "Suppressing further..." message counts as an additional
- # message (but not as an addition to the error count).
- self.assertEquals(2, self._error_count)
- self.assertEquals(3, len(self._error_messages))
- self.assertEquals(self._error_messages[-2],
- "foo.h(100): message [whitespace/tab] [5]\n")
- self.assertEquals(self._error_messages[-1],
- "Suppressing further [whitespace/tab] reports "
- "for this file.\n")
-
- # Third call: no report.
- self._call_error_handler(error_handler, confidence)
- self.assertEquals(3, self._error_count)
- self.assertEquals(3, len(self._error_messages))
-
- def test_line_numbers(self):
- """Test the line_numbers parameter."""
- self._check_initialized()
- configuration = self._style_checker_configuration()
- error_handler = self._error_handler(configuration,
- line_numbers=[50])
- confidence = 5
-
- # Error on non-modified line: no error.
- self._call_error_handler(error_handler, confidence, line_number=60)
- self.assertEquals(0, self._error_count)
- self.assertEquals([], self._error_messages)
-
- # Error on modified line: error.
- self._call_error_handler(error_handler, confidence, line_number=50)
- self.assertEquals(1, self._error_count)
- self.assertEquals(self._error_messages,
- ["foo.h(50): message [whitespace/tab] [5]\n"])
diff --git a/WebKitTools/Scripts/webkitpy/style/filereader.py b/WebKitTools/Scripts/webkitpy/style/filereader.py
deleted file mode 100644
index 1a24cb5..0000000
--- a/WebKitTools/Scripts/webkitpy/style/filereader.py
+++ /dev/null
@@ -1,162 +0,0 @@
-# Copyright (C) 2009 Google Inc. All rights reserved.
-# Copyright (C) 2010 Chris Jerdonek (chris.jerdonek@gmail.com)
-# Copyright (C) 2010 ProFUSION embedded systems
-#
-# 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.
-
-"""Supports reading and processing text files."""
-
-import codecs
-import logging
-import os
-import sys
-
-
-_log = logging.getLogger(__name__)
-
-
-class TextFileReader(object):
-
- """Supports reading and processing text files.
-
- Attributes:
- file_count: The total number of files passed to this instance
- for processing, including non-text files and files
- that should be skipped.
- delete_only_file_count: The total number of files that are not
- processed this instance actually because
- the files don't have any modified lines
- but should be treated as processed.
-
- """
-
- def __init__(self, processor):
- """Create an instance.
-
- Arguments:
- processor: A ProcessorBase instance.
-
- """
- self._processor = processor
- self.file_count = 0
- self.delete_only_file_count = 0
-
- def _read_lines(self, file_path):
- """Read the file at a path, and return its lines.
-
- Raises:
- IOError: If the file does not exist or cannot be read.
-
- """
- # Support the UNIX convention of using "-" for stdin.
- if file_path == '-':
- file = codecs.StreamReaderWriter(sys.stdin,
- codecs.getreader('utf8'),
- codecs.getwriter('utf8'),
- 'replace')
- else:
- # We do not open the file with universal newline support
- # (codecs does not support it anyway), so the resulting
- # lines contain trailing "\r" characters if we are reading
- # a file with CRLF endings.
- file = codecs.open(file_path, 'r', 'utf8', 'replace')
-
- try:
- contents = file.read()
- finally:
- file.close()
-
- lines = contents.split('\n')
- return lines
-
- def process_file(self, file_path, **kwargs):
- """Process the given file by calling the processor's process() method.
-
- Args:
- file_path: The path of the file to process.
- **kwargs: Any additional keyword parameters that should be passed
- to the processor's process() method. The process()
- method should support these keyword arguments.
-
- Raises:
- SystemExit: If no file at file_path exists.
-
- """
- self.file_count += 1
-
- if not os.path.exists(file_path) and file_path != "-":
- _log.error("File does not exist: '%s'" % file_path)
- sys.exit(1)
-
- if not self._processor.should_process(file_path):
- _log.debug("Skipping file: '%s'" % file_path)
- return
- _log.debug("Processing file: '%s'" % file_path)
-
- try:
- lines = self._read_lines(file_path)
- except IOError, err:
- message = ("Could not read file. Skipping: '%s'\n %s"
- % (file_path, err))
- _log.warn(message)
- return
-
- self._processor.process(lines, file_path, **kwargs)
-
- def _process_directory(self, directory):
- """Process all files in the given directory, recursively.
-
- Args:
- directory: A directory path.
-
- """
- for dir_path, dir_names, file_names in os.walk(directory):
- for file_name in file_names:
- file_path = os.path.join(dir_path, file_name)
- self.process_file(file_path)
-
- def process_paths(self, paths):
- """Process the given file and directory paths.
-
- Args:
- paths: A list of file and directory paths.
-
- """
- for path in paths:
- if os.path.isdir(path):
- self._process_directory(directory=path)
- else:
- self.process_file(path)
-
- def count_delete_only_file(self):
- """Count up files that contains only deleted lines.
-
- Files which has no modified or newly-added lines don't need
- to check style, but should be treated as checked. For that
- purpose, we just count up the number of such files.
- """
- self.delete_only_file_count += 1
diff --git a/WebKitTools/Scripts/webkitpy/style/filereader_unittest.py b/WebKitTools/Scripts/webkitpy/style/filereader_unittest.py
deleted file mode 100644
index 6328337..0000000
--- a/WebKitTools/Scripts/webkitpy/style/filereader_unittest.py
+++ /dev/null
@@ -1,166 +0,0 @@
-# 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.
-
-"""Contains unit tests for filereader.py."""
-
-from __future__ import with_statement
-
-import codecs
-import os
-import shutil
-import tempfile
-import unittest
-
-from webkitpy.common.system.logtesting import LoggingTestCase
-from webkitpy.style.checker import ProcessorBase
-from webkitpy.style.filereader import TextFileReader
-
-
-class TextFileReaderTest(LoggingTestCase):
-
- class MockProcessor(ProcessorBase):
-
- """A processor for test purposes.
-
- This processor simply records the parameters passed to its process()
- method for later checking by the unittest test methods.
-
- """
-
- def __init__(self):
- self.processed = []
- """The parameters passed for all calls to the process() method."""
-
- def should_process(self, file_path):
- return not file_path.endswith('should_not_process.txt')
-
- def process(self, lines, file_path, test_kwarg=None):
- self.processed.append((lines, file_path, test_kwarg))
-
- def setUp(self):
- LoggingTestCase.setUp(self)
- processor = TextFileReaderTest.MockProcessor()
-
- temp_dir = tempfile.mkdtemp()
-
- self._file_reader = TextFileReader(processor)
- self._processor = processor
- self._temp_dir = temp_dir
-
- def tearDown(self):
- LoggingTestCase.tearDown(self)
- shutil.rmtree(self._temp_dir)
-
- def _create_file(self, rel_path, text, encoding="utf-8"):
- """Create a file with given text and return the path to the file."""
- # FIXME: There are better/more secure APIs for creatin tmp file paths.
- file_path = os.path.join(self._temp_dir, rel_path)
- with codecs.open(file_path, "w", encoding) as file:
- file.write(text)
- return file_path
-
- def _passed_to_processor(self):
- """Return the parameters passed to MockProcessor.process()."""
- return self._processor.processed
-
- def _assert_file_reader(self, passed_to_processor, file_count):
- """Assert the state of the file reader."""
- self.assertEquals(passed_to_processor, self._passed_to_processor())
- self.assertEquals(file_count, self._file_reader.file_count)
-
- def test_process_file__does_not_exist(self):
- try:
- self._file_reader.process_file('does_not_exist.txt')
- except SystemExit, err:
- self.assertEquals(str(err), '1')
- else:
- self.fail('No Exception raised.')
- self._assert_file_reader([], 1)
- self.assertLog(["ERROR: File does not exist: 'does_not_exist.txt'\n"])
-
- def test_process_file__is_dir(self):
- temp_dir = os.path.join(self._temp_dir, 'test_dir')
- os.mkdir(temp_dir)
-
- self._file_reader.process_file(temp_dir)
-
- # Because the log message below contains exception text, it is
- # possible that the text varies across platforms. For this reason,
- # we check only the portion of the log message that we control,
- # namely the text at the beginning.
- log_messages = self.logMessages()
- # We remove the message we are looking at to prevent the tearDown()
- # from raising an exception when it asserts that no log messages
- # remain.
- message = log_messages.pop()
-
- self.assertTrue(message.startswith('WARNING: Could not read file. '
- "Skipping: '%s'\n " % temp_dir))
-
- self._assert_file_reader([], 1)
-
- def test_process_file__should_not_process(self):
- file_path = self._create_file('should_not_process.txt', 'contents')
-
- self._file_reader.process_file(file_path)
- self._assert_file_reader([], 1)
-
- def test_process_file__multiple_lines(self):
- file_path = self._create_file('foo.txt', 'line one\r\nline two\n')
-
- self._file_reader.process_file(file_path)
- processed = [(['line one\r', 'line two', ''], file_path, None)]
- self._assert_file_reader(processed, 1)
-
- def test_process_file__file_stdin(self):
- file_path = self._create_file('-', 'file contents')
-
- self._file_reader.process_file(file_path=file_path, test_kwarg='foo')
- processed = [(['file contents'], file_path, 'foo')]
- self._assert_file_reader(processed, 1)
-
- def test_process_file__with_kwarg(self):
- file_path = self._create_file('foo.txt', 'file contents')
-
- self._file_reader.process_file(file_path=file_path, test_kwarg='foo')
- processed = [(['file contents'], file_path, 'foo')]
- self._assert_file_reader(processed, 1)
-
- def test_process_paths(self):
- # We test a list of paths that contains both a file and a directory.
- dir = os.path.join(self._temp_dir, 'foo_dir')
- os.mkdir(dir)
-
- file_path1 = self._create_file('file1.txt', 'foo')
-
- rel_path = os.path.join('foo_dir', 'file2.txt')
- file_path2 = self._create_file(rel_path, 'bar')
-
- self._file_reader.process_paths([dir, file_path1])
- processed = [(['bar'], file_path2, None),
- (['foo'], file_path1, None)]
- self._assert_file_reader(processed, 2)
-
- def test_count_delete_only_file(self):
- self._file_reader.count_delete_only_file()
- delete_only_file_count = self._file_reader.delete_only_file_count
- self.assertEquals(delete_only_file_count, 1)
diff --git a/WebKitTools/Scripts/webkitpy/style/filter.py b/WebKitTools/Scripts/webkitpy/style/filter.py
deleted file mode 100644
index 608a9e6..0000000
--- a/WebKitTools/Scripts/webkitpy/style/filter.py
+++ /dev/null
@@ -1,278 +0,0 @@
-# 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:
-# 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.
-
-"""Contains filter-related code."""
-
-
-def validate_filter_rules(filter_rules, all_categories):
- """Validate the given filter rules, and raise a ValueError if not valid.
-
- Args:
- filter_rules: A list of boolean filter rules, for example--
- ["-whitespace", "+whitespace/braces"]
- all_categories: A list of all available category names, for example--
- ["whitespace/tabs", "whitespace/braces"]
-
- Raises:
- ValueError: An error occurs if a filter rule does not begin
- with "+" or "-" or if a filter rule does not match
- the beginning of some category name in the list
- of all available categories.
-
- """
- for rule in filter_rules:
- if not (rule.startswith('+') or rule.startswith('-')):
- raise ValueError('Invalid filter rule "%s": every rule '
- "must start with + or -." % rule)
-
- for category in all_categories:
- if category.startswith(rule[1:]):
- break
- else:
- raise ValueError('Suspected incorrect filter rule "%s": '
- "the rule does not match the beginning "
- "of any category name." % rule)
-
-
-class _CategoryFilter(object):
-
- """Filters whether to check style categories."""
-
- def __init__(self, filter_rules=None):
- """Create a category filter.
-
- Args:
- filter_rules: A list of strings that are filter rules, which
- are strings beginning with the plus or minus
- symbol (+/-). The list should include any
- default filter rules at the beginning.
- Defaults to the empty list.
-
- Raises:
- ValueError: Invalid filter rule if a rule does not start with
- plus ("+") or minus ("-").
-
- """
- if filter_rules is None:
- filter_rules = []
-
- self._filter_rules = filter_rules
- self._should_check_category = {} # Cached dictionary of category to True/False
-
- def __str__(self):
- return ",".join(self._filter_rules)
-
- # Useful for unit testing.
- def __eq__(self, other):
- """Return whether this CategoryFilter instance is equal to another."""
- return self._filter_rules == other._filter_rules
-
- # Useful for unit testing.
- def __ne__(self, other):
- # Python does not automatically deduce from __eq__().
- return not (self == other)
-
- def should_check(self, category):
- """Return whether the category should be checked.
-
- The rules for determining whether a category should be checked
- are as follows. By default all categories should be checked.
- Then apply the filter rules in order from first to last, with
- later flags taking precedence.
-
- A filter rule applies to a category if the string after the
- leading plus/minus (+/-) matches the beginning of the category
- name. A plus (+) means the category should be checked, while a
- minus (-) means the category should not be checked.
-
- """
- if category in self._should_check_category:
- return self._should_check_category[category]
-
- should_check = True # All categories checked by default.
- for rule in self._filter_rules:
- if not category.startswith(rule[1:]):
- continue
- should_check = rule.startswith('+')
- self._should_check_category[category] = should_check # Update cache.
- return should_check
-
-
-class FilterConfiguration(object):
-
- """Supports filtering with path-specific and user-specified rules."""
-
- def __init__(self, base_rules=None, path_specific=None, user_rules=None):
- """Create a FilterConfiguration instance.
-
- Args:
- base_rules: The starting list of filter rules to use for
- processing. The default is the empty list, which
- by itself would mean that all categories should be
- checked.
-
- path_specific: A list of (sub_paths, path_rules) pairs
- that stores the path-specific filter rules for
- appending to the base rules.
- The "sub_paths" value is a list of path
- substrings. If a file path contains one of the
- substrings, then the corresponding path rules
- are appended. The first substring match takes
- precedence, i.e. only the first match triggers
- an append.
- The "path_rules" value is a list of filter
- rules that can be appended to the base rules.
-
- user_rules: A list of filter rules that is always appended
- to the base rules and any path rules. In other
- words, the user rules take precedence over the
- everything. In practice, the user rules are
- provided by the user from the command line.
-
- """
- if base_rules is None:
- base_rules = []
- if path_specific is None:
- path_specific = []
- if user_rules is None:
- user_rules = []
-
- self._base_rules = base_rules
- self._path_specific = path_specific
- self._path_specific_lower = None
- """The backing store for self._get_path_specific_lower()."""
-
- self._user_rules = user_rules
-
- self._path_rules_to_filter = {}
- """Cached dictionary of path rules to CategoryFilter instance."""
-
- # The same CategoryFilter instance can be shared across
- # multiple keys in this dictionary. This allows us to take
- # greater advantage of the caching done by
- # CategoryFilter.should_check().
- self._path_to_filter = {}
- """Cached dictionary of file path to CategoryFilter instance."""
-
- # Useful for unit testing.
- def __eq__(self, other):
- """Return whether this FilterConfiguration is equal to another."""
- if self._base_rules != other._base_rules:
- return False
- if self._path_specific != other._path_specific:
- return False
- if self._user_rules != other._user_rules:
- return False
-
- return True
-
- # Useful for unit testing.
- def __ne__(self, other):
- # Python does not automatically deduce this from __eq__().
- return not self.__eq__(other)
-
- # We use the prefix "_get" since the name "_path_specific_lower"
- # is already taken up by the data attribute backing store.
- def _get_path_specific_lower(self):
- """Return a copy of self._path_specific with the paths lower-cased."""
- if self._path_specific_lower is None:
- self._path_specific_lower = []
- for (sub_paths, path_rules) in self._path_specific:
- sub_paths = map(str.lower, sub_paths)
- self._path_specific_lower.append((sub_paths, path_rules))
- return self._path_specific_lower
-
- def _path_rules_from_path(self, path):
- """Determine the path-specific rules to use, and return as a tuple.
-
- This method returns a tuple rather than a list so the return
- value can be passed to _filter_from_path_rules() without change.
-
- """
- path = path.lower()
- for (sub_paths, path_rules) in self._get_path_specific_lower():
- for sub_path in sub_paths:
- if path.find(sub_path) > -1:
- return tuple(path_rules)
- return () # Default to the empty tuple.
-
- def _filter_from_path_rules(self, path_rules):
- """Return the CategoryFilter associated to the given path rules.
-
- Args:
- path_rules: A tuple of path rules. We require a tuple rather
- than a list so the value can be used as a dictionary
- key in self._path_rules_to_filter.
-
- """
- # We reuse the same CategoryFilter where possible to take
- # advantage of the caching they do.
- if path_rules not in self._path_rules_to_filter:
- rules = list(self._base_rules) # Make a copy
- rules.extend(path_rules)
- rules.extend(self._user_rules)
- self._path_rules_to_filter[path_rules] = _CategoryFilter(rules)
-
- return self._path_rules_to_filter[path_rules]
-
- def _filter_from_path(self, path):
- """Return the CategoryFilter associated to a path."""
- if path not in self._path_to_filter:
- path_rules = self._path_rules_from_path(path)
- filter = self._filter_from_path_rules(path_rules)
- self._path_to_filter[path] = filter
-
- return self._path_to_filter[path]
-
- def should_check(self, category, path):
- """Return whether the given category should be checked.
-
- This method determines whether a category should be checked
- by checking the category name against the filter rules for
- the given path.
-
- For a given path, the filter rules are the combination of
- the base rules, the path-specific rules, and the user-provided
- rules -- in that order. As we will describe below, later rules
- in the list take precedence. The path-specific rules are the
- rules corresponding to the first element of the "path_specific"
- parameter that contains a string case-insensitively matching
- some substring of the path. If there is no such element,
- there are no path-specific rules for that path.
-
- Given a list of filter rules, the logic for determining whether
- a category should be checked is as follows. By default all
- categories should be checked. Then apply the filter rules in
- order from first to last, with later flags taking precedence.
-
- A filter rule applies to a category if the string after the
- leading plus/minus (+/-) matches the beginning of the category
- name. A plus (+) means the category should be checked, while a
- minus (-) means the category should not be checked.
-
- Args:
- category: The category name.
- path: The path of the file being checked.
-
- """
- return self._filter_from_path(path).should_check(category)
-
diff --git a/WebKitTools/Scripts/webkitpy/style/filter_unittest.py b/WebKitTools/Scripts/webkitpy/style/filter_unittest.py
deleted file mode 100644
index 7b8a5402..0000000
--- a/WebKitTools/Scripts/webkitpy/style/filter_unittest.py
+++ /dev/null
@@ -1,256 +0,0 @@
-# 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:
-# 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 for filter.py."""
-
-import unittest
-
-from filter import _CategoryFilter as CategoryFilter
-from filter import validate_filter_rules
-from filter import FilterConfiguration
-
-# On Testing __eq__() and __ne__():
-#
-# In the tests below, we deliberately do not use assertEquals() or
-# assertNotEquals() to test __eq__() or __ne__(). We do this to be
-# very explicit about what we are testing, especially in the case
-# of assertNotEquals().
-#
-# Part of the reason is that it is not immediately clear what
-# expression the unittest module uses to assert "not equals" -- the
-# negation of __eq__() or __ne__(), which are not necessarily
-# equivalent expresions in Python. For example, from Python's "Data
-# Model" documentation--
-#
-# "There are no implied relationships among the comparison
-# operators. The truth of x==y does not imply that x!=y is
-# false. Accordingly, when defining __eq__(), one should
-# also define __ne__() so that the operators will behave as
-# expected."
-#
-# (from http://docs.python.org/reference/datamodel.html#object.__ne__ )
-
-class ValidateFilterRulesTest(unittest.TestCase):
-
- """Tests validate_filter_rules() function."""
-
- def test_validate_filter_rules(self):
- all_categories = ["tabs", "whitespace", "build/include"]
-
- bad_rules = [
- "tabs",
- "*tabs",
- " tabs",
- " +tabs",
- "+whitespace/newline",
- "+xxx",
- ]
-
- good_rules = [
- "+tabs",
- "-tabs",
- "+build"
- ]
-
- for rule in bad_rules:
- self.assertRaises(ValueError, validate_filter_rules,
- [rule], all_categories)
-
- for rule in good_rules:
- # This works: no error.
- validate_filter_rules([rule], all_categories)
-
-
-class CategoryFilterTest(unittest.TestCase):
-
- """Tests CategoryFilter class."""
-
- def test_init(self):
- """Test __init__ method."""
- # Test that the attributes are getting set correctly.
- filter = CategoryFilter(["+"])
- self.assertEquals(["+"], filter._filter_rules)
-
- def test_init_default_arguments(self):
- """Test __init__ method default arguments."""
- filter = CategoryFilter()
- self.assertEquals([], filter._filter_rules)
-
- def test_str(self):
- """Test __str__ "to string" operator."""
- filter = CategoryFilter(["+a", "-b"])
- self.assertEquals(str(filter), "+a,-b")
-
- def test_eq(self):
- """Test __eq__ equality function."""
- filter1 = CategoryFilter(["+a", "+b"])
- filter2 = CategoryFilter(["+a", "+b"])
- filter3 = CategoryFilter(["+b", "+a"])
-
- # See the notes at the top of this module about testing
- # __eq__() and __ne__().
- self.assertTrue(filter1.__eq__(filter2))
- self.assertFalse(filter1.__eq__(filter3))
-
- def test_ne(self):
- """Test __ne__ inequality function."""
- # By default, __ne__ always returns true on different objects.
- # Thus, just check the distinguishing case to verify that the
- # code defines __ne__.
- #
- # Also, see the notes at the top of this module about testing
- # __eq__() and __ne__().
- self.assertFalse(CategoryFilter().__ne__(CategoryFilter()))
-
- def test_should_check(self):
- """Test should_check() method."""
- filter = CategoryFilter()
- self.assertTrue(filter.should_check("everything"))
- # Check a second time to exercise cache.
- self.assertTrue(filter.should_check("everything"))
-
- filter = CategoryFilter(["-"])
- self.assertFalse(filter.should_check("anything"))
- # Check a second time to exercise cache.
- self.assertFalse(filter.should_check("anything"))
-
- filter = CategoryFilter(["-", "+ab"])
- self.assertTrue(filter.should_check("abc"))
- self.assertFalse(filter.should_check("a"))
-
- filter = CategoryFilter(["+", "-ab"])
- self.assertFalse(filter.should_check("abc"))
- self.assertTrue(filter.should_check("a"))
-
-
-class FilterConfigurationTest(unittest.TestCase):
-
- """Tests FilterConfiguration class."""
-
- def _config(self, base_rules, path_specific, user_rules):
- """Return a FilterConfiguration instance."""
- return FilterConfiguration(base_rules=base_rules,
- path_specific=path_specific,
- user_rules=user_rules)
-
- def test_init(self):
- """Test __init__ method."""
- # Test that the attributes are getting set correctly.
- # We use parameter values that are different from the defaults.
- base_rules = ["-"]
- path_specific = [(["path"], ["+a"])]
- user_rules = ["+"]
-
- config = self._config(base_rules, path_specific, user_rules)
-
- self.assertEquals(base_rules, config._base_rules)
- self.assertEquals(path_specific, config._path_specific)
- self.assertEquals(user_rules, config._user_rules)
-
- def test_default_arguments(self):
- # Test that the attributes are getting set correctly to the defaults.
- config = FilterConfiguration()
-
- self.assertEquals([], config._base_rules)
- self.assertEquals([], config._path_specific)
- self.assertEquals([], config._user_rules)
-
- def test_eq(self):
- """Test __eq__ method."""
- # See the notes at the top of this module about testing
- # __eq__() and __ne__().
- self.assertTrue(FilterConfiguration().__eq__(FilterConfiguration()))
-
- # Verify that a difference in any argument causes equality to fail.
- config = FilterConfiguration()
-
- # These parameter values are different from the defaults.
- base_rules = ["-"]
- path_specific = [(["path"], ["+a"])]
- user_rules = ["+"]
-
- self.assertFalse(config.__eq__(FilterConfiguration(
- base_rules=base_rules)))
- self.assertFalse(config.__eq__(FilterConfiguration(
- path_specific=path_specific)))
- self.assertFalse(config.__eq__(FilterConfiguration(
- user_rules=user_rules)))
-
- def test_ne(self):
- """Test __ne__ method."""
- # By default, __ne__ always returns true on different objects.
- # Thus, just check the distinguishing case to verify that the
- # code defines __ne__.
- #
- # Also, see the notes at the top of this module about testing
- # __eq__() and __ne__().
- self.assertFalse(FilterConfiguration().__ne__(FilterConfiguration()))
-
- def test_base_rules(self):
- """Test effect of base_rules on should_check()."""
- base_rules = ["-b"]
- path_specific = []
- user_rules = []
-
- config = self._config(base_rules, path_specific, user_rules)
-
- self.assertTrue(config.should_check("a", "path"))
- self.assertFalse(config.should_check("b", "path"))
-
- def test_path_specific(self):
- """Test effect of path_rules_specifier on should_check()."""
- base_rules = ["-"]
- path_specific = [(["path1"], ["+b"]),
- (["path2"], ["+c"])]
- user_rules = []
-
- config = self._config(base_rules, path_specific, user_rules)
-
- self.assertFalse(config.should_check("c", "path1"))
- self.assertTrue(config.should_check("c", "path2"))
- # Test that first match takes precedence.
- self.assertFalse(config.should_check("c", "path2/path1"))
-
- def test_path_with_different_case(self):
- """Test a path that differs only in case."""
- base_rules = ["-"]
- path_specific = [(["Foo/"], ["+whitespace"])]
- user_rules = []
-
- config = self._config(base_rules, path_specific, user_rules)
-
- self.assertFalse(config.should_check("whitespace", "Fooo/bar.txt"))
- self.assertTrue(config.should_check("whitespace", "Foo/bar.txt"))
- # Test different case.
- self.assertTrue(config.should_check("whitespace", "FOO/bar.txt"))
-
- def test_user_rules(self):
- """Test effect of user_rules on should_check()."""
- base_rules = ["-"]
- path_specific = []
- user_rules = ["+b"]
-
- config = self._config(base_rules, path_specific, user_rules)
-
- self.assertFalse(config.should_check("a", "path"))
- self.assertTrue(config.should_check("b", "path"))
-
diff --git a/WebKitTools/Scripts/webkitpy/style/main.py b/WebKitTools/Scripts/webkitpy/style/main.py
deleted file mode 100644
index c933c6d..0000000
--- a/WebKitTools/Scripts/webkitpy/style/main.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# 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.
-
-import logging
-import os
-import sys
-
-from webkitpy.common.system.ospath import relpath as _relpath
-
-
-_log = logging.getLogger(__name__)
-
-
-def change_directory(checkout_root, paths, mock_os=None):
- """Change the working directory to the WebKit checkout root, if possible.
-
- If every path in the paths parameter is below the checkout root (or if
- the paths parameter is empty or None), this method changes the current
- working directory to the checkout root and converts the paths parameter
- as described below.
- This allows the paths being checked to be displayed relative to the
- checkout root, and for path-specific style checks to work as expected.
- Path-specific checks include whether files should be skipped, whether
- custom style rules should apply to certain files, etc.
- If the checkout root is None or the empty string, this method returns
- the paths parameter unchanged.
-
- Returns:
- paths: A copy of the paths parameter -- possibly converted, as follows.
- If this method changed the current working directory to the
- checkout root, then the list is the paths parameter converted to
- normalized paths relative to the checkout root. Otherwise, the
- paths are not converted.
-
- Args:
- paths: A list of paths to the files that should be checked for style.
- This argument can be None or the empty list if a git commit
- or all changes under the checkout root should be checked.
- checkout_root: The path to the root of the WebKit checkout, or None or
- the empty string if no checkout could be detected.
- mock_os: A replacement module for unit testing. Defaults to os.
-
- """
- os_module = os if mock_os is None else mock_os
-
- if paths is not None:
- paths = list(paths)
-
- if not checkout_root:
- if not paths:
- raise Exception("The paths parameter must be non-empty if "
- "there is no checkout root.")
-
- # FIXME: Consider trying to detect the checkout root for each file
- # being checked rather than only trying to detect the checkout
- # root for the current working directory. This would allow
- # files to be checked correctly even if the script is being
- # run from outside any WebKit checkout.
- #
- # Moreover, try to find the "source root" for each file
- # using path-based heuristics rather than using only the
- # presence of a WebKit checkout. For example, we could
- # examine parent directories until a directory is found
- # containing JavaScriptCore, WebCore, WebKit, WebKitSite,
- # and WebKitTools.
- # Then log an INFO message saying that a source root not
- # in a WebKit checkout was found. This will allow us to check
- # the style of non-scm copies of the source tree (e.g.
- # nightlies).
- _log.warn("WebKit checkout root not found:\n"
- " Path-dependent style checks may not work correctly.\n"
- " See the help documentation for more info.")
-
- return paths
-
- if paths:
- # Then try converting all of the paths to paths relative to
- # the checkout root.
- rel_paths = []
- for path in paths:
- rel_path = _relpath(path, checkout_root)
- if rel_path is None:
- # Then the path is not below the checkout root. Since all
- # paths should be interpreted relative to the same root,
- # do not interpret any of the paths as relative to the
- # checkout root. Interpret all of them relative to the
- # current working directory, and do not change the current
- # working directory.
- _log.warn(
-"""Path-dependent style checks may not work correctly:
-
- One of the given paths is outside the WebKit checkout of the current
- working directory:
-
- Path: %s
- Checkout root: %s
-
- Pass only files below the checkout root to ensure correct results.
- See the help documentation for more info.
-"""
- % (path, checkout_root))
-
- return paths
- rel_paths.append(rel_path)
- # If we got here, the conversion was successful.
- paths = rel_paths
-
- _log.debug("Changing to checkout root: " + checkout_root)
- os_module.chdir(checkout_root)
-
- return paths
diff --git a/WebKitTools/Scripts/webkitpy/style/main_unittest.py b/WebKitTools/Scripts/webkitpy/style/main_unittest.py
deleted file mode 100644
index fe448f5..0000000
--- a/WebKitTools/Scripts/webkitpy/style/main_unittest.py
+++ /dev/null
@@ -1,124 +0,0 @@
-# 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 for main.py."""
-
-import os
-import unittest
-
-from main import change_directory
-from webkitpy.style_references import LogTesting
-
-
-class ChangeDirectoryTest(unittest.TestCase):
-
- """Tests change_directory()."""
-
- _original_directory = "/original"
- _checkout_root = "/WebKit"
-
- class _MockOs(object):
-
- """A mock os module for unit testing."""
-
- def __init__(self, test_case):
- self._test_case = test_case
- self._current_directory = \
- ChangeDirectoryTest._original_directory
-
- def chdir(self, current_directory):
- self._current_directory = current_directory
-
- def assertCurrentDirectory(self, expected_directory):
- self._test_case.assertEquals(expected_directory,
- self._current_directory)
-
- def setUp(self):
- self._log = LogTesting.setUp(self)
- self._mock_os = self._MockOs(self)
-
- def tearDown(self):
- self._log.tearDown()
-
- # This method is a convenient wrapper for change_working_directory() that
- # passes the mock_os for this unit testing class.
- def _change_directory(self, paths, checkout_root):
- return change_directory(paths=paths,
- checkout_root=checkout_root,
- mock_os=self._mock_os)
-
- def _assert_result(self, actual_return_value, expected_return_value,
- expected_log_messages, expected_current_directory):
- self.assertEquals(actual_return_value, expected_return_value)
- self._log.assertMessages(expected_log_messages)
- self._mock_os.assertCurrentDirectory(expected_current_directory)
-
- def test_checkout_root_none_paths_none(self):
- self.assertRaises(Exception, self._change_directory,
- checkout_root=None, paths=None)
- self._log.assertMessages([])
- self._mock_os.assertCurrentDirectory(self._original_directory)
-
- def test_checkout_root_none(self):
- paths = self._change_directory(checkout_root=None,
- paths=["path1"])
- log_messages = [
-"""WARNING: WebKit checkout root not found:
- Path-dependent style checks may not work correctly.
- See the help documentation for more info.
-"""]
- self._assert_result(paths, ["path1"], log_messages,
- self._original_directory)
-
- def test_paths_none(self):
- paths = self._change_directory(checkout_root=self._checkout_root,
- paths=None)
- self._assert_result(paths, None, [], self._checkout_root)
-
- def test_paths_convertible(self):
- paths=["/WebKit/foo1.txt",
- "/WebKit/foo2.txt"]
- paths = self._change_directory(checkout_root=self._checkout_root,
- paths=paths)
- self._assert_result(paths, ["foo1.txt", "foo2.txt"], [],
- self._checkout_root)
-
- def test_with_scm_paths_unconvertible(self):
- paths=["/WebKit/foo1.txt",
- "/outside/foo2.txt"]
- paths = self._change_directory(checkout_root=self._checkout_root,
- paths=paths)
- log_messages = [
-"""WARNING: Path-dependent style checks may not work correctly:
-
- One of the given paths is outside the WebKit checkout of the current
- working directory:
-
- Path: /outside/foo2.txt
- Checkout root: /WebKit
-
- Pass only files below the checkout root to ensure correct results.
- See the help documentation for more info.
-
-"""]
- self._assert_result(paths, paths, log_messages,
- self._original_directory)
diff --git a/WebKitTools/Scripts/webkitpy/style/optparser.py b/WebKitTools/Scripts/webkitpy/style/optparser.py
deleted file mode 100644
index f4e9923..0000000
--- a/WebKitTools/Scripts/webkitpy/style/optparser.py
+++ /dev/null
@@ -1,457 +0,0 @@
-# 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.
-
-"""Supports the parsing of command-line options for check-webkit-style."""
-
-import logging
-from optparse import OptionParser
-import os.path
-import sys
-
-from filter import validate_filter_rules
-# This module should not import anything from checker.py.
-
-_log = logging.getLogger(__name__)
-
-_USAGE = """usage: %prog [--help] [options] [path1] [path2] ...
-
-Overview:
- Check coding style according to WebKit style guidelines:
-
- http://webkit.org/coding/coding-style.html
-
- Path arguments can be files and directories. If neither a git commit nor
- paths are passed, then all changes in your source control working directory
- are checked.
-
-Style errors:
- This script assigns to every style error a confidence score from 1-5 and
- a category name. A confidence score of 5 means the error is certainly
- a problem, and 1 means it could be fine.
-
- Category names appear in error messages in brackets, for example
- [whitespace/indent]. See the options section below for an option that
- displays all available categories and which are reported by default.
-
-Filters:
- Use filters to configure what errors to report. Filters are specified using
- a comma-separated list of boolean filter rules. The script reports errors
- in a category if the category passes the filter, as described below.
-
- All categories start out passing. Boolean filter rules are then evaluated
- from left to right, with later rules taking precedence. For example, the
- rule "+foo" passes any category that starts with "foo", and "-foo" fails
- any such category. The filter input "-whitespace,+whitespace/braces" fails
- the category "whitespace/tab" and passes "whitespace/braces".
-
- Examples: --filter=-whitespace,+whitespace/braces
- --filter=-whitespace,-runtime/printf,+runtime/printf_format
- --filter=-,+build/include_what_you_use
-
-Paths:
- Certain style-checking behavior depends on the paths relative to
- the WebKit source root of the files being checked. For example,
- certain types of errors may be handled differently for files in
- WebKit/gtk/webkit/ (e.g. by suppressing "readability/naming" errors
- for files in this directory).
-
- Consequently, if the path relative to the source root cannot be
- determined for a file being checked, then style checking may not
- work correctly for that file. This can occur, for example, if no
- WebKit checkout can be found, or if the source root can be detected,
- but one of the files being checked lies outside the source tree.
-
- If a WebKit checkout can be detected and all files being checked
- are in the source tree, then all paths will automatically be
- converted to paths relative to the source root prior to checking.
- This is also useful for display purposes.
-
- Currently, this command can detect the source root only if the
- command is run from within a WebKit checkout (i.e. if the current
- working directory is below the root of a checkout). In particular,
- it is not recommended to run this script from a directory outside
- a checkout.
-
- Running this script from a top-level WebKit source directory and
- checking only files in the source tree will ensure that all style
- checking behaves correctly -- whether or not a checkout can be
- detected. This is because all file paths will already be relative
- to the source root and so will not need to be converted."""
-
-_EPILOG = ("This script can miss errors and does not substitute for "
- "code review.")
-
-
-# This class should not have knowledge of the flag key names.
-class DefaultCommandOptionValues(object):
-
- """Stores the default check-webkit-style command-line options.
-
- Attributes:
- output_format: A string that is the default output format.
- min_confidence: An integer that is the default minimum confidence level.
-
- """
-
- def __init__(self, min_confidence, output_format):
- self.min_confidence = min_confidence
- self.output_format = output_format
-
-
-# This class should not have knowledge of the flag key names.
-class CommandOptionValues(object):
-
- """Stores the option values passed by the user via the command line.
-
- Attributes:
- is_verbose: A boolean value of whether verbose logging is enabled.
-
- filter_rules: The list of filter rules provided by the user.
- These rules are appended to the base rules and
- path-specific rules and so take precedence over
- the base filter rules, etc.
-
- git_commit: A string representing the git commit to check.
- The default is None.
-
- min_confidence: An integer between 1 and 5 inclusive that is the
- minimum confidence level of style errors to report.
- The default is 1, which reports all errors.
-
- output_format: A string that is the output format. The supported
- output formats are "emacs" which emacs can parse
- and "vs7" which Microsoft Visual Studio 7 can parse.
-
- """
- def __init__(self,
- filter_rules=None,
- git_commit=None,
- diff_files=None,
- is_verbose=False,
- min_confidence=1,
- output_format="emacs"):
- if filter_rules is None:
- filter_rules = []
-
- if (min_confidence < 1) or (min_confidence > 5):
- raise ValueError('Invalid "min_confidence" parameter: value '
- "must be an integer between 1 and 5 inclusive. "
- 'Value given: "%s".' % min_confidence)
-
- if output_format not in ("emacs", "vs7"):
- raise ValueError('Invalid "output_format" parameter: '
- 'value must be "emacs" or "vs7". '
- 'Value given: "%s".' % output_format)
-
- self.filter_rules = filter_rules
- self.git_commit = git_commit
- self.diff_files = diff_files
- self.is_verbose = is_verbose
- self.min_confidence = min_confidence
- self.output_format = output_format
-
- # Useful for unit testing.
- def __eq__(self, other):
- """Return whether this instance is equal to another."""
- if self.filter_rules != other.filter_rules:
- return False
- if self.git_commit != other.git_commit:
- return False
- if self.diff_files != other.diff_files:
- return False
- if self.is_verbose != other.is_verbose:
- return False
- if self.min_confidence != other.min_confidence:
- return False
- if self.output_format != other.output_format:
- return False
-
- return True
-
- # Useful for unit testing.
- def __ne__(self, other):
- # Python does not automatically deduce this from __eq__().
- return not self.__eq__(other)
-
-
-class ArgumentPrinter(object):
-
- """Supports the printing of check-webkit-style command arguments."""
-
- def _flag_pair_to_string(self, flag_key, flag_value):
- return '--%(key)s=%(val)s' % {'key': flag_key, 'val': flag_value }
-
- def to_flag_string(self, options):
- """Return a flag string of the given CommandOptionValues instance.
-
- This method orders the flag values alphabetically by the flag key.
-
- Args:
- options: A CommandOptionValues instance.
-
- """
- flags = {}
- flags['min-confidence'] = options.min_confidence
- flags['output'] = options.output_format
- # Only include the filter flag if user-provided rules are present.
- filter_rules = options.filter_rules
- if filter_rules:
- flags['filter'] = ",".join(filter_rules)
- if options.git_commit:
- flags['git-commit'] = options.git_commit
- if options.diff_files:
- flags['diff_files'] = options.diff_files
-
- flag_string = ''
- # Alphabetizing lets us unit test this method.
- for key in sorted(flags.keys()):
- flag_string += self._flag_pair_to_string(key, flags[key]) + ' '
-
- return flag_string.strip()
-
-
-class ArgumentParser(object):
-
- # FIXME: Move the documentation of the attributes to the __init__
- # docstring after making the attributes internal.
- """Supports the parsing of check-webkit-style command arguments.
-
- Attributes:
- create_usage: A function that accepts a DefaultCommandOptionValues
- instance and returns a string of usage instructions.
- Defaults to the function that generates the usage
- string for check-webkit-style.
- default_options: A DefaultCommandOptionValues instance that provides
- the default values for options not explicitly
- provided by the user.
- stderr_write: A function that takes a string as a parameter and
- serves as stderr.write. Defaults to sys.stderr.write.
- This parameter should be specified only for unit tests.
-
- """
-
- def __init__(self,
- all_categories,
- default_options,
- base_filter_rules=None,
- mock_stderr=None,
- usage=None):
- """Create an ArgumentParser instance.
-
- Args:
- all_categories: The set of all available style categories.
- default_options: See the corresponding attribute in the class
- docstring.
- Keyword Args:
- base_filter_rules: The list of filter rules at the beginning of
- the list of rules used to check style. This
- list has the least precedence when checking
- style and precedes any user-provided rules.
- The class uses this parameter only for display
- purposes to the user. Defaults to the empty list.
- create_usage: See the documentation of the corresponding
- attribute in the class docstring.
- stderr_write: See the documentation of the corresponding
- attribute in the class docstring.
-
- """
- if base_filter_rules is None:
- base_filter_rules = []
- stderr = sys.stderr if mock_stderr is None else mock_stderr
- if usage is None:
- usage = _USAGE
-
- self._all_categories = all_categories
- self._base_filter_rules = base_filter_rules
-
- # FIXME: Rename these to reflect that they are internal.
- self.default_options = default_options
- self.stderr_write = stderr.write
-
- self._parser = self._create_option_parser(stderr=stderr,
- usage=usage,
- default_min_confidence=self.default_options.min_confidence,
- default_output_format=self.default_options.output_format)
-
- def _create_option_parser(self, stderr, usage,
- default_min_confidence, default_output_format):
- # Since the epilog string is short, it is not necessary to replace
- # the epilog string with a mock epilog string when testing.
- # For this reason, we use _EPILOG directly rather than passing it
- # as an argument like we do for the usage string.
- parser = OptionParser(usage=usage, epilog=_EPILOG)
-
- filter_help = ('set a filter to control what categories of style '
- 'errors to report. Specify a filter using a comma-'
- 'delimited list of boolean filter rules, for example '
- '"--filter -whitespace,+whitespace/braces". To display '
- 'all categories and which are enabled by default, pass '
- """no value (e.g. '-f ""' or '--filter=').""")
- parser.add_option("-f", "--filter-rules", metavar="RULES",
- dest="filter_value", help=filter_help)
-
- git_commit_help = ("check all changes in the given commit. "
- "Use 'commit_id..' to check all changes after commmit_id")
- parser.add_option("-g", "--git-diff", "--git-commit",
- metavar="COMMIT", dest="git_commit", help=git_commit_help,)
-
- diff_files_help = "diff the files passed on the command line rather than checking the style of every line"
- parser.add_option("--diff-files", action="store_true", dest="diff_files", default=False, help=diff_files_help)
-
- min_confidence_help = ("set the minimum confidence of style errors "
- "to report. Can be an integer 1-5, with 1 "
- "displaying all errors. Defaults to %default.")
- parser.add_option("-m", "--min-confidence", metavar="INT",
- type="int", dest="min_confidence",
- default=default_min_confidence,
- help=min_confidence_help)
-
- output_format_help = ('set the output format, which can be "emacs" '
- 'or "vs7" (for Visual Studio). '
- 'Defaults to "%default".')
- parser.add_option("-o", "--output-format", metavar="FORMAT",
- choices=["emacs", "vs7"],
- dest="output_format", default=default_output_format,
- help=output_format_help)
-
- verbose_help = "enable verbose logging."
- parser.add_option("-v", "--verbose", dest="is_verbose", default=False,
- action="store_true", help=verbose_help)
-
- # Override OptionParser's error() method so that option help will
- # also display when an error occurs. Normally, just the usage
- # string displays and not option help.
- parser.error = self._parse_error
-
- # Override OptionParser's print_help() method so that help output
- # does not render to the screen while running unit tests.
- print_help = parser.print_help
- parser.print_help = lambda: print_help(file=stderr)
-
- return parser
-
- def _parse_error(self, error_message):
- """Print the help string and an error message, and exit."""
- # The method format_help() includes both the usage string and
- # the flag options.
- help = self._parser.format_help()
- # Separate help from the error message with a single blank line.
- self.stderr_write(help + "\n")
- if error_message:
- _log.error(error_message)
-
- # Since we are using this method to replace/override the Python
- # module optparse's OptionParser.error() method, we match its
- # behavior and exit with status code 2.
- #
- # As additional background, Python documentation says--
- #
- # "Unix programs generally use 2 for command line syntax errors
- # and 1 for all other kind of errors."
- #
- # (from http://docs.python.org/library/sys.html#sys.exit )
- sys.exit(2)
-
- def _exit_with_categories(self):
- """Exit and print the style categories and default filter rules."""
- self.stderr_write('\nAll categories:\n')
- for category in sorted(self._all_categories):
- self.stderr_write(' ' + category + '\n')
-
- self.stderr_write('\nDefault filter rules**:\n')
- for filter_rule in sorted(self._base_filter_rules):
- self.stderr_write(' ' + filter_rule + '\n')
- self.stderr_write('\n**The command always evaluates the above rules, '
- 'and before any --filter flag.\n\n')
-
- sys.exit(0)
-
- def _parse_filter_flag(self, flag_value):
- """Parse the --filter flag, and return a list of filter rules.
-
- Args:
- flag_value: A string of comma-separated filter rules, for
- example "-whitespace,+whitespace/indent".
-
- """
- filters = []
- for uncleaned_filter in flag_value.split(','):
- filter = uncleaned_filter.strip()
- if not filter:
- continue
- filters.append(filter)
- return filters
-
- def parse(self, args):
- """Parse the command line arguments to check-webkit-style.
-
- Args:
- args: A list of command-line arguments as returned by sys.argv[1:].
-
- Returns:
- A tuple of (paths, options)
-
- paths: The list of paths to check.
- options: A CommandOptionValues instance.
-
- """
- (options, paths) = self._parser.parse_args(args=args)
-
- filter_value = options.filter_value
- git_commit = options.git_commit
- diff_files = options.diff_files
- is_verbose = options.is_verbose
- min_confidence = options.min_confidence
- output_format = options.output_format
-
- if filter_value is not None and not filter_value:
- # Then the user explicitly passed no filter, for
- # example "-f ''" or "--filter=".
- self._exit_with_categories()
-
- # Validate user-provided values.
-
- min_confidence = int(min_confidence)
- if (min_confidence < 1) or (min_confidence > 5):
- self._parse_error('option --min-confidence: invalid integer: '
- '%s: value must be between 1 and 5'
- % min_confidence)
-
- if filter_value:
- filter_rules = self._parse_filter_flag(filter_value)
- else:
- filter_rules = []
-
- try:
- validate_filter_rules(filter_rules, self._all_categories)
- except ValueError, err:
- self._parse_error(err)
-
- options = CommandOptionValues(filter_rules=filter_rules,
- git_commit=git_commit,
- diff_files=diff_files,
- is_verbose=is_verbose,
- min_confidence=min_confidence,
- output_format=output_format)
-
- return (paths, options)
-
diff --git a/WebKitTools/Scripts/webkitpy/style/optparser_unittest.py b/WebKitTools/Scripts/webkitpy/style/optparser_unittest.py
deleted file mode 100644
index a6b64da..0000000
--- a/WebKitTools/Scripts/webkitpy/style/optparser_unittest.py
+++ /dev/null
@@ -1,258 +0,0 @@
-# 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 for parser.py."""
-
-import unittest
-
-from webkitpy.common.system.logtesting import LoggingTestCase
-from webkitpy.style.optparser import ArgumentParser
-from webkitpy.style.optparser import ArgumentPrinter
-from webkitpy.style.optparser import CommandOptionValues as ProcessorOptions
-from webkitpy.style.optparser import DefaultCommandOptionValues
-
-
-class ArgumentPrinterTest(unittest.TestCase):
-
- """Tests the ArgumentPrinter class."""
-
- _printer = ArgumentPrinter()
-
- def _create_options(self,
- output_format='emacs',
- min_confidence=3,
- filter_rules=[],
- git_commit=None):
- return ProcessorOptions(filter_rules=filter_rules,
- git_commit=git_commit,
- min_confidence=min_confidence,
- output_format=output_format)
-
- def test_to_flag_string(self):
- options = self._create_options('vs7', 5, ['+foo', '-bar'], 'git')
- self.assertEquals('--filter=+foo,-bar --git-commit=git '
- '--min-confidence=5 --output=vs7',
- self._printer.to_flag_string(options))
-
- # This is to check that --filter and --git-commit do not
- # show up when not user-specified.
- options = self._create_options()
- self.assertEquals('--min-confidence=3 --output=emacs',
- self._printer.to_flag_string(options))
-
-
-class ArgumentParserTest(LoggingTestCase):
-
- """Test the ArgumentParser class."""
-
- class _MockStdErr(object):
-
- def write(self, message):
- # We do not want the usage string or style categories
- # to print during unit tests, so print nothing.
- return
-
- def _parse(self, args):
- """Call a test parser.parse()."""
- parser = self._create_parser()
- return parser.parse(args)
-
- def _create_defaults(self):
- """Return a DefaultCommandOptionValues instance for testing."""
- base_filter_rules = ["-", "+whitespace"]
- return DefaultCommandOptionValues(min_confidence=3,
- output_format="vs7")
-
- def _create_parser(self):
- """Return an ArgumentParser instance for testing."""
- default_options = self._create_defaults()
-
- all_categories = ["build" ,"whitespace"]
-
- mock_stderr = self._MockStdErr()
-
- return ArgumentParser(all_categories=all_categories,
- base_filter_rules=[],
- default_options=default_options,
- mock_stderr=mock_stderr,
- usage="test usage")
-
- def test_parse_documentation(self):
- parse = self._parse
-
- # FIXME: Test both the printing of the usage string and the
- # filter categories help.
-
- # Request the usage string.
- self.assertRaises(SystemExit, parse, ['--help'])
- # Request default filter rules and available style categories.
- self.assertRaises(SystemExit, parse, ['--filter='])
-
- def test_parse_bad_values(self):
- parse = self._parse
-
- # Pass an unsupported argument.
- self.assertRaises(SystemExit, parse, ['--bad'])
- self.assertLog(['ERROR: no such option: --bad\n'])
-
- self.assertRaises(SystemExit, parse, ['--min-confidence=bad'])
- self.assertLog(['ERROR: option --min-confidence: '
- "invalid integer value: 'bad'\n"])
- self.assertRaises(SystemExit, parse, ['--min-confidence=0'])
- self.assertLog(['ERROR: option --min-confidence: invalid integer: 0: '
- 'value must be between 1 and 5\n'])
- self.assertRaises(SystemExit, parse, ['--min-confidence=6'])
- self.assertLog(['ERROR: option --min-confidence: invalid integer: 6: '
- 'value must be between 1 and 5\n'])
- parse(['--min-confidence=1']) # works
- parse(['--min-confidence=5']) # works
-
- self.assertRaises(SystemExit, parse, ['--output=bad'])
- self.assertLog(['ERROR: option --output-format: invalid choice: '
- "'bad' (choose from 'emacs', 'vs7')\n"])
- parse(['--output=vs7']) # works
-
- # Pass a filter rule not beginning with + or -.
- self.assertRaises(SystemExit, parse, ['--filter=build'])
- self.assertLog(['ERROR: Invalid filter rule "build": '
- 'every rule must start with + or -.\n'])
- parse(['--filter=+build']) # works
-
- def test_parse_default_arguments(self):
- parse = self._parse
-
- (files, options) = parse([])
-
- self.assertEquals(files, [])
-
- self.assertEquals(options.filter_rules, [])
- self.assertEquals(options.git_commit, None)
- self.assertEquals(options.diff_files, False)
- self.assertEquals(options.is_verbose, False)
- self.assertEquals(options.min_confidence, 3)
- self.assertEquals(options.output_format, 'vs7')
-
- def test_parse_explicit_arguments(self):
- parse = self._parse
-
- # Pass non-default explicit values.
- (files, options) = parse(['--min-confidence=4'])
- self.assertEquals(options.min_confidence, 4)
- (files, options) = parse(['--output=emacs'])
- self.assertEquals(options.output_format, 'emacs')
- (files, options) = parse(['-g', 'commit'])
- self.assertEquals(options.git_commit, 'commit')
- (files, options) = parse(['--git-commit=commit'])
- self.assertEquals(options.git_commit, 'commit')
- (files, options) = parse(['--git-diff=commit'])
- self.assertEquals(options.git_commit, 'commit')
- (files, options) = parse(['--verbose'])
- self.assertEquals(options.is_verbose, True)
- (files, options) = parse(['--diff-files', 'file.txt'])
- self.assertEquals(options.diff_files, True)
-
- # Pass user_rules.
- (files, options) = parse(['--filter=+build,-whitespace'])
- self.assertEquals(options.filter_rules,
- ["+build", "-whitespace"])
-
- # Pass spurious white space in user rules.
- (files, options) = parse(['--filter=+build, -whitespace'])
- self.assertEquals(options.filter_rules,
- ["+build", "-whitespace"])
-
- def test_parse_files(self):
- parse = self._parse
-
- (files, options) = parse(['foo.cpp'])
- self.assertEquals(files, ['foo.cpp'])
-
- # Pass multiple files.
- (files, options) = parse(['--output=emacs', 'foo.cpp', 'bar.cpp'])
- self.assertEquals(files, ['foo.cpp', 'bar.cpp'])
-
-
-class CommandOptionValuesTest(unittest.TestCase):
-
- """Tests CommandOptionValues class."""
-
- def test_init(self):
- """Test __init__ constructor."""
- # Check default parameters.
- options = ProcessorOptions()
- self.assertEquals(options.filter_rules, [])
- self.assertEquals(options.git_commit, None)
- self.assertEquals(options.is_verbose, False)
- self.assertEquals(options.min_confidence, 1)
- self.assertEquals(options.output_format, "emacs")
-
- # Check argument validation.
- self.assertRaises(ValueError, ProcessorOptions, output_format="bad")
- ProcessorOptions(output_format="emacs") # No ValueError: works
- ProcessorOptions(output_format="vs7") # works
- self.assertRaises(ValueError, ProcessorOptions, min_confidence=0)
- self.assertRaises(ValueError, ProcessorOptions, min_confidence=6)
- ProcessorOptions(min_confidence=1) # works
- ProcessorOptions(min_confidence=5) # works
-
- # Check attributes.
- options = ProcessorOptions(filter_rules=["+"],
- git_commit="commit",
- is_verbose=True,
- min_confidence=3,
- output_format="vs7")
- self.assertEquals(options.filter_rules, ["+"])
- self.assertEquals(options.git_commit, "commit")
- self.assertEquals(options.is_verbose, True)
- self.assertEquals(options.min_confidence, 3)
- self.assertEquals(options.output_format, "vs7")
-
- def test_eq(self):
- """Test __eq__ equality function."""
- self.assertTrue(ProcessorOptions().__eq__(ProcessorOptions()))
-
- # Also verify that a difference in any argument causes equality to fail.
-
- # Explicitly create a ProcessorOptions instance with all default
- # values. We do this to be sure we are assuming the right default
- # values in our self.assertFalse() calls below.
- options = ProcessorOptions(filter_rules=[],
- git_commit=None,
- is_verbose=False,
- min_confidence=1,
- output_format="emacs")
- # Verify that we created options correctly.
- self.assertTrue(options.__eq__(ProcessorOptions()))
-
- self.assertFalse(options.__eq__(ProcessorOptions(filter_rules=["+"])))
- self.assertFalse(options.__eq__(ProcessorOptions(git_commit="commit")))
- self.assertFalse(options.__eq__(ProcessorOptions(is_verbose=True)))
- self.assertFalse(options.__eq__(ProcessorOptions(min_confidence=2)))
- self.assertFalse(options.__eq__(ProcessorOptions(output_format="vs7")))
-
- def test_ne(self):
- """Test __ne__ inequality function."""
- # By default, __ne__ always returns true on different objects.
- # Thus, just check the distinguishing case to verify that the
- # code defines __ne__.
- self.assertFalse(ProcessorOptions().__ne__(ProcessorOptions()))
-
diff --git a/WebKitTools/Scripts/webkitpy/style/patchreader.py b/WebKitTools/Scripts/webkitpy/style/patchreader.py
deleted file mode 100644
index f44839d..0000000
--- a/WebKitTools/Scripts/webkitpy/style/patchreader.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright (C) 2010 Google Inc. All rights reserved.
-# Copyright (C) 2010 Chris Jerdonek (chris.jerdonek@gmail.com)
-# Copyright (C) 2010 ProFUSION embedded systems
-#
-# 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 logging
-
-from webkitpy.common.checkout.diff_parser import DiffParser
-
-
-_log = logging.getLogger("webkitpy.style.patchreader")
-
-
-class PatchReader(object):
- """Supports checking style in patches."""
-
- def __init__(self, text_file_reader):
- """Create a PatchReader instance.
-
- Args:
- text_file_reader: A TextFileReader instance.
-
- """
- self._text_file_reader = text_file_reader
-
- def check(self, patch_string):
- """Check style in the given patch."""
- patch_files = DiffParser(patch_string.splitlines()).files
-
- for path, diff_file in patch_files.iteritems():
- line_numbers = diff_file.added_or_modified_line_numbers()
- _log.debug('Found %s new or modified lines in: %s' % (len(line_numbers), path))
-
- if not line_numbers:
- # Don't check files which contain only deleted lines
- # as they can never add style errors. However, mark them as
- # processed so that we count up number of such files.
- self._text_file_reader.count_delete_only_file()
- continue
-
- self._text_file_reader.process_file(file_path=path, line_numbers=line_numbers)
diff --git a/WebKitTools/Scripts/webkitpy/style/patchreader_unittest.py b/WebKitTools/Scripts/webkitpy/style/patchreader_unittest.py
deleted file mode 100644
index b121082..0000000
--- a/WebKitTools/Scripts/webkitpy/style/patchreader_unittest.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright (C) 2010 Google Inc. All rights reserved.
-# Copyright (C) 2009 Torch Mobile Inc.
-# Copyright (C) 2009 Apple Inc. 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 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 unittest
-
-from webkitpy.style.patchreader import PatchReader
-
-
-class PatchReaderTest(unittest.TestCase):
-
- """Test the PatchReader class."""
-
- class MockTextFileReader(object):
-
- def __init__(self):
- self.passed_to_process_file = []
- """A list of (file_path, line_numbers) pairs."""
- self.delete_only_file_count = 0
- """A number of times count_delete_only_file() called"""
-
- def process_file(self, file_path, line_numbers):
- self.passed_to_process_file.append((file_path, line_numbers))
-
- def count_delete_only_file(self):
- self.delete_only_file_count += 1
-
- def setUp(self):
- file_reader = self.MockTextFileReader()
- self._file_reader = file_reader
- self._patch_checker = PatchReader(file_reader)
-
- def _call_check_patch(self, patch_string):
- self._patch_checker.check(patch_string)
-
- def _assert_checked(self, passed_to_process_file, delete_only_file_count):
- self.assertEquals(self._file_reader.passed_to_process_file,
- passed_to_process_file)
- self.assertEquals(self._file_reader.delete_only_file_count,
- delete_only_file_count)
-
- def test_check_patch(self):
- # The modified line_numbers array for this patch is: [2].
- self._call_check_patch("""diff --git a/__init__.py b/__init__.py
-index ef65bee..e3db70e 100644
---- a/__init__.py
-+++ b/__init__.py
-@@ -1,1 +1,2 @@
- # Required for Python to search this directory for module files
-+# New line
-""")
- self._assert_checked([("__init__.py", [2])], 0)
-
- def test_check_patch_with_deletion(self):
- self._call_check_patch("""Index: __init__.py
-===================================================================
---- __init__.py (revision 3593)
-+++ __init__.py (working copy)
-@@ -1 +0,0 @@
--foobar
-""")
- # _mock_check_file should not be called for the deletion patch.
- self._assert_checked([], 1)
diff --git a/WebKitTools/Scripts/webkitpy/style_references.py b/WebKitTools/Scripts/webkitpy/style_references.py
deleted file mode 100644
index a21e931..0000000
--- a/WebKitTools/Scripts/webkitpy/style_references.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# 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.
-
-"""References to non-style modules used by the style package."""
-
-# This module is a simple facade to the functionality used by the
-# style package that comes from WebKit modules outside the style
-# package.
-#
-# With this module, the only intra-package references (i.e.
-# references to webkitpy modules outside the style folder) that
-# the style package needs to make are relative references to
-# this module. For example--
-#
-# > from .. style_references import parse_patch
-#
-# Similarly, people maintaining non-style code are not beholden
-# to the contents of the style package when refactoring or
-# otherwise changing non-style code. They only have to be aware
-# of this module.
-
-import os
-
-from webkitpy.common.checkout.diff_parser import DiffParser
-from webkitpy.common.system.logtesting import LogTesting
-from webkitpy.common.system.logtesting import TestLogStream
-from webkitpy.common.system.logutils import configure_logging
-from webkitpy.common.checkout.scm import detect_scm_system
-from webkitpy.layout_tests import port
-from webkitpy.layout_tests.layout_package import test_expectations
-from webkitpy.thirdparty.autoinstalled import pep8
-
-
-def detect_checkout():
- """Return a WebKitCheckout instance, or None if it cannot be found."""
- cwd = os.path.abspath(os.curdir)
- scm = detect_scm_system(cwd)
-
- return None if scm is None else WebKitCheckout(scm)
-
-
-class WebKitCheckout(object):
-
- """Simple facade to the SCM class for use by style package."""
-
- def __init__(self, scm):
- self._scm = scm
-
- def root_path(self):
- """Return the checkout root as an absolute path."""
- return self._scm.checkout_root
-
- def create_patch(self, git_commit, changed_files=None):
- # FIXME: SCM.create_patch should understand how to handle None.
- return self._scm.create_patch(git_commit, changed_files=changed_files or [])
diff --git a/WebKitTools/Scripts/webkitpy/test/__init__.py b/WebKitTools/Scripts/webkitpy/test/__init__.py
deleted file mode 100644
index ef65bee..0000000
--- a/WebKitTools/Scripts/webkitpy/test/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/test/cat.py b/WebKitTools/Scripts/webkitpy/test/cat.py
deleted file mode 100644
index ae1e143..0000000
--- a/WebKitTools/Scripts/webkitpy/test/cat.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright (C) 2010 Apple 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.
-#
-# 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.
-
-import os.path
-import sys
-
-# Add WebKitTools/Scripts to the path to ensure we can find webkitpy.
-sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
-
-from webkitpy.common.system import fileutils
-
-
-def command_arguments(*args):
- return ['python', __file__] + list(args)
-
-
-def main():
- fileutils.make_stdout_binary()
- sys.stdout.write(sys.stdin.read())
- return 0
-
-if __name__ == '__main__':
- sys.exit(main())
diff --git a/WebKitTools/Scripts/webkitpy/test/cat_unittest.py b/WebKitTools/Scripts/webkitpy/test/cat_unittest.py
deleted file mode 100644
index 4ed1f67..0000000
--- a/WebKitTools/Scripts/webkitpy/test/cat_unittest.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright (C) 2010 Apple 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.
-#
-# 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.
-
-import StringIO
-import os.path
-import sys
-import unittest
-
-from webkitpy.common.system import executive, outputcapture
-from webkitpy.test import cat
-
-
-class CatTest(outputcapture.OutputCaptureTestCaseBase):
- def assert_cat(self, input):
- saved_stdin = sys.stdin
- sys.stdin = StringIO.StringIO(input)
- cat.main()
- self.assertStdout(input)
- sys.stdin = saved_stdin
-
- def test_basic(self):
- self.assert_cat('foo bar baz\n')
-
- def test_no_newline(self):
- self.assert_cat('foo bar baz')
-
- def test_unicode(self):
- self.assert_cat(u'WebKit \u2661 Tor Arne Vestb\u00F8!')
-
- def test_as_command(self):
- input = 'foo bar baz\n'
- output = executive.Executive().run_command(cat.command_arguments(), input=input)
- self.assertEqual(input, output)
diff --git a/WebKitTools/Scripts/webkitpy/test/echo.py b/WebKitTools/Scripts/webkitpy/test/echo.py
deleted file mode 100644
index f7468f7..0000000
--- a/WebKitTools/Scripts/webkitpy/test/echo.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright (C) 2010 Apple 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.
-#
-# 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.
-
-import os.path
-import sys
-
-# Add WebKitTools/Scripts to the path to ensure we can find webkitpy.
-sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
-
-from webkitpy.common.system import fileutils
-
-
-def command_arguments(*args):
- return ['python', __file__] + list(args)
-
-
-def main(args=None):
- if args is None:
- args = sys.argv[1:]
-
- fileutils.make_stdout_binary()
-
- print_newline = True
- if len(args) and args[0] == '-n':
- print_newline = False
- del args[0]
- sys.stdout.write(' '.join(args))
- if print_newline:
- sys.stdout.write('\n')
- return 0
-
-if __name__ == '__main__':
- sys.exit(main())
diff --git a/WebKitTools/Scripts/webkitpy/test/echo_unittest.py b/WebKitTools/Scripts/webkitpy/test/echo_unittest.py
deleted file mode 100644
index bc13b5e..0000000
--- a/WebKitTools/Scripts/webkitpy/test/echo_unittest.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright (C) 2010 Apple 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.
-#
-# 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.
-
-import os.path
-import sys
-import unittest
-
-from webkitpy.common.system import executive, outputcapture
-from webkitpy.test import echo
-
-
-class EchoTest(outputcapture.OutputCaptureTestCaseBase):
- def test_basic(self):
- echo.main(['foo', 'bar', 'baz'])
- self.assertStdout('foo bar baz\n')
-
- def test_no_newline(self):
- echo.main(['-n', 'foo', 'bar', 'baz'])
- self.assertStdout('foo bar baz')
-
- def test_unicode(self):
- echo.main([u'WebKit \u2661', 'Tor Arne', u'Vestb\u00F8!'])
- self.assertStdout(u'WebKit \u2661 Tor Arne Vestb\u00F8!\n')
-
- def test_argument_order(self):
- echo.main(['foo', '-n', 'bar'])
- self.assertStdout('foo -n bar\n')
-
- def test_empty_arguments(self):
- old_argv = sys.argv
- sys.argv = ['echo.py', 'foo', 'bar', 'baz']
- echo.main([])
- self.assertStdout('\n')
- sys.argv = old_argv
-
- def test_no_arguments(self):
- old_argv = sys.argv
- sys.argv = ['echo.py', 'foo', 'bar', 'baz']
- echo.main()
- self.assertStdout('foo bar baz\n')
- sys.argv = old_argv
-
- def test_as_command(self):
- output = executive.Executive().run_command(echo.command_arguments('foo', 'bar', 'baz'))
- self.assertEqual(output, 'foo bar baz\n')
diff --git a/WebKitTools/Scripts/webkitpy/test/main.py b/WebKitTools/Scripts/webkitpy/test/main.py
deleted file mode 100644
index 1038d82..0000000
--- a/WebKitTools/Scripts/webkitpy/test/main.py
+++ /dev/null
@@ -1,140 +0,0 @@
-# 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.
-
-"""Contains the entry method for test-webkitpy."""
-
-import logging
-import os
-import sys
-import unittest
-
-import webkitpy
-
-
-_log = logging.getLogger(__name__)
-
-
-class Tester(object):
-
- """Discovers and runs webkitpy unit tests."""
-
- def _find_unittest_files(self, webkitpy_dir):
- """Return a list of paths to all unit-test files."""
- unittest_paths = [] # Return value.
-
- for dir_path, dir_names, file_names in os.walk(webkitpy_dir):
- for file_name in file_names:
- if not file_name.endswith("_unittest.py"):
- continue
- unittest_path = os.path.join(dir_path, file_name)
- unittest_paths.append(unittest_path)
-
- return unittest_paths
-
- def _modules_from_paths(self, package_root, paths):
- """Return a list of fully-qualified module names given paths."""
- package_path = os.path.abspath(package_root)
- root_package_name = os.path.split(package_path)[1] # Equals "webkitpy".
-
- prefix_length = len(package_path)
-
- modules = []
- for path in paths:
- path = os.path.abspath(path)
- # This gives us, for example: /common/config/ports_unittest.py
- rel_path = path[prefix_length:]
- # This gives us, for example: /common/config/ports_unittest
- rel_path = os.path.splitext(rel_path)[0]
-
- parts = []
- while True:
- (rel_path, tail) = os.path.split(rel_path)
- if not tail:
- break
- parts.insert(0, tail)
- # We now have, for example: common.config.ports_unittest
- # FIXME: This is all a hack around the fact that we always prefix webkitpy includes with "webkitpy."
- parts.insert(0, root_package_name) # Put "webkitpy" at the beginning.
- module = ".".join(parts)
- modules.append(module)
-
- return modules
-
- def run_tests(self, sys_argv, external_package_paths=None):
- """Run the unit tests in all *_unittest.py modules in webkitpy.
-
- This method excludes "webkitpy.common.checkout.scm_unittest" unless
- the --all option is the second element of sys_argv.
-
- Args:
- sys_argv: A reference to sys.argv.
-
- """
- if external_package_paths is None:
- external_package_paths = []
- else:
- # FIXME: We should consider moving webkitpy off of using "webkitpy." to prefix
- # all includes. If we did that, then this would use path instead of dirname(path).
- # QueueStatusServer.__init__ has a sys.path import hack due to this code.
- sys.path.extend(set(os.path.dirname(path) for path in external_package_paths))
-
- if len(sys_argv) > 1 and not sys_argv[-1].startswith("-"):
- # Then explicit modules or test names were provided, which
- # the unittest module is equipped to handle.
- unittest.main(argv=sys_argv, module=None)
- # No need to return since unitttest.main() exits.
-
- # Otherwise, auto-detect all unit tests.
-
- # FIXME: This should be combined with the external_package_paths code above.
- webkitpy_dir = os.path.dirname(webkitpy.__file__)
-
- modules = []
- for path in [webkitpy_dir] + external_package_paths:
- modules.extend(self._modules_from_paths(path, self._find_unittest_files(path)))
- modules.sort()
-
- # This is a sanity check to ensure that the unit-test discovery
- # methods are working.
- if len(modules) < 1:
- raise Exception("No unit-test modules found.")
-
- for module in modules:
- _log.debug("Found: %s" % module)
-
- # FIXME: This is a hack, but I'm tired of commenting out the test.
- # See https://bugs.webkit.org/show_bug.cgi?id=31818
- if len(sys_argv) > 1 and sys.argv[1] == "--all":
- sys.argv.remove("--all")
- else:
- excluded_module = "webkitpy.common.checkout.scm_unittest"
- _log.info("Excluding: %s (use --all to include)" % excluded_module)
- modules.remove(excluded_module)
-
- sys_argv.extend(modules)
-
- # We pass None for the module because we do not want the unittest
- # module to resolve module names relative to a given module.
- # (This would require importing all of the unittest modules from
- # this module.) See the loadTestsFromName() method of the
- # unittest.TestLoader class for more details on this parameter.
- unittest.main(argv=sys_argv, module=None)
diff --git a/WebKitTools/Scripts/webkitpy/test/skip.py b/WebKitTools/Scripts/webkitpy/test/skip.py
deleted file mode 100644
index 8587d56..0000000
--- a/WebKitTools/Scripts/webkitpy/test/skip.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright (C) 2010 Apple 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.
-#
-# 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.
-
-import logging
-
-_log = logging.getLogger(__name__)
-
-
-def skip_if(klass, condition, message=None, logger=None):
- """Makes all test_* methods in a given class no-ops if the given condition
- is False. Backported from Python 3.1+'s unittest.skipIf decorator."""
- if not logger:
- logger = _log
- if not condition:
- return klass
- for name in dir(klass):
- attr = getattr(klass, name)
- if not callable(attr):
- continue
- if not name.startswith('test_'):
- continue
- setattr(klass, name, _skipped_method(attr, message, logger))
- klass._printed_skipped_message = False
- return klass
-
-
-def _skipped_method(method, message, logger):
- def _skip(*args):
- if method.im_class._printed_skipped_message:
- return
- method.im_class._printed_skipped_message = True
- logger.info('Skipping %s.%s: %s' % (method.__module__, method.im_class.__name__, message))
- return _skip
diff --git a/WebKitTools/Scripts/webkitpy/test/skip_unittest.py b/WebKitTools/Scripts/webkitpy/test/skip_unittest.py
deleted file mode 100644
index f61a1bb..0000000
--- a/WebKitTools/Scripts/webkitpy/test/skip_unittest.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# Copyright (C) 2010 Apple 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.
-#
-# 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.
-
-import StringIO
-import logging
-import unittest
-
-from webkitpy.test.skip import skip_if
-
-
-class SkipTest(unittest.TestCase):
- def setUp(self):
- self.logger = logging.getLogger(__name__)
-
- self.old_level = self.logger.level
- self.logger.setLevel(logging.INFO)
-
- self.old_propagate = self.logger.propagate
- self.logger.propagate = False
-
- self.log_stream = StringIO.StringIO()
- self.handler = logging.StreamHandler(self.log_stream)
- self.logger.addHandler(self.handler)
-
- self.foo_was_called = False
-
- def tearDown(self):
- self.logger.removeHandler(self.handler)
- self.propagate = self.old_propagate
- self.logger.setLevel(self.old_level)
-
- def create_fixture_class(self):
- class TestSkipFixture(object):
- def __init__(self, callback):
- self.callback = callback
-
- def test_foo(self):
- self.callback()
-
- return TestSkipFixture
-
- def foo_callback(self):
- self.foo_was_called = True
-
- def test_skip_if_false(self):
- klass = skip_if(self.create_fixture_class(), False, 'Should not see this message.', logger=self.logger)
- klass(self.foo_callback).test_foo()
- self.assertEqual(self.log_stream.getvalue(), '')
- self.assertTrue(self.foo_was_called)
-
- def test_skip_if_true(self):
- klass = skip_if(self.create_fixture_class(), True, 'Should see this message.', logger=self.logger)
- klass(self.foo_callback).test_foo()
- self.assertEqual(self.log_stream.getvalue(), 'Skipping webkitpy.test.skip_unittest.TestSkipFixture: Should see this message.\n')
- self.assertFalse(self.foo_was_called)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/BeautifulSoup.py b/WebKitTools/Scripts/webkitpy/thirdparty/BeautifulSoup.py
deleted file mode 100644
index 34204e7..0000000
--- a/WebKitTools/Scripts/webkitpy/thirdparty/BeautifulSoup.py
+++ /dev/null
@@ -1,2000 +0,0 @@
-"""Beautiful Soup
-Elixir and Tonic
-"The Screen-Scraper's Friend"
-http://www.crummy.com/software/BeautifulSoup/
-
-Beautiful Soup parses a (possibly invalid) XML or HTML document into a
-tree representation. It provides methods and Pythonic idioms that make
-it easy to navigate, search, and modify the tree.
-
-A well-formed XML/HTML document yields a well-formed data
-structure. An ill-formed XML/HTML document yields a correspondingly
-ill-formed data structure. If your document is only locally
-well-formed, you can use this library to find and process the
-well-formed part of it.
-
-Beautiful Soup works with Python 2.2 and up. It has no external
-dependencies, but you'll have more success at converting data to UTF-8
-if you also install these three packages:
-
-* chardet, for auto-detecting character encodings
- http://chardet.feedparser.org/
-* cjkcodecs and iconv_codec, which add more encodings to the ones supported
- by stock Python.
- http://cjkpython.i18n.org/
-
-Beautiful Soup defines classes for two main parsing strategies:
-
- * BeautifulStoneSoup, for parsing XML, SGML, or your domain-specific
- language that kind of looks like XML.
-
- * BeautifulSoup, for parsing run-of-the-mill HTML code, be it valid
- or invalid. This class has web browser-like heuristics for
- obtaining a sensible parse tree in the face of common HTML errors.
-
-Beautiful Soup also defines a class (UnicodeDammit) for autodetecting
-the encoding of an HTML or XML document, and converting it to
-Unicode. Much of this code is taken from Mark Pilgrim's Universal Feed Parser.
-
-For more than you ever wanted to know about Beautiful Soup, see the
-documentation:
-http://www.crummy.com/software/BeautifulSoup/documentation.html
-
-Here, have some legalese:
-
-Copyright (c) 2004-2009, Leonard Richardson
-
-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 the the Beautiful Soup Consortium and All
- Night Kosher Bakery 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, DAMMIT.
-
-"""
-from __future__ import generators
-
-__author__ = "Leonard Richardson (leonardr@segfault.org)"
-__version__ = "3.1.0.1"
-__copyright__ = "Copyright (c) 2004-2009 Leonard Richardson"
-__license__ = "New-style BSD"
-
-import codecs
-import markupbase
-import types
-import re
-from HTMLParser import HTMLParser, HTMLParseError
-try:
- from htmlentitydefs import name2codepoint
-except ImportError:
- name2codepoint = {}
-try:
- set
-except NameError:
- from sets import Set as set
-
-#These hacks make Beautiful Soup able to parse XML with namespaces
-markupbase._declname_match = re.compile(r'[a-zA-Z][-_.:a-zA-Z0-9]*\s*').match
-
-DEFAULT_OUTPUT_ENCODING = "utf-8"
-
-# First, the classes that represent markup elements.
-
-def sob(unicode, encoding):
- """Returns either the given Unicode string or its encoding."""
- if encoding is None:
- return unicode
- else:
- return unicode.encode(encoding)
-
-class PageElement:
- """Contains the navigational information for some part of the page
- (either a tag or a piece of text)"""
-
- def setup(self, parent=None, previous=None):
- """Sets up the initial relations between this element and
- other elements."""
- self.parent = parent
- self.previous = previous
- self.next = None
- self.previousSibling = None
- self.nextSibling = None
- if self.parent and self.parent.contents:
- self.previousSibling = self.parent.contents[-1]
- self.previousSibling.nextSibling = self
-
- def replaceWith(self, replaceWith):
- oldParent = self.parent
- myIndex = self.parent.contents.index(self)
- if hasattr(replaceWith, 'parent') and replaceWith.parent == self.parent:
- # We're replacing this element with one of its siblings.
- index = self.parent.contents.index(replaceWith)
- if index and index < myIndex:
- # Furthermore, it comes before this element. That
- # means that when we extract it, the index of this
- # element will change.
- myIndex = myIndex - 1
- self.extract()
- oldParent.insert(myIndex, replaceWith)
-
- def extract(self):
- """Destructively rips this element out of the tree."""
- if self.parent:
- try:
- self.parent.contents.remove(self)
- except ValueError:
- pass
-
- #Find the two elements that would be next to each other if
- #this element (and any children) hadn't been parsed. Connect
- #the two.
- lastChild = self._lastRecursiveChild()
- nextElement = lastChild.next
-
- if self.previous:
- self.previous.next = nextElement
- if nextElement:
- nextElement.previous = self.previous
- self.previous = None
- lastChild.next = None
-
- self.parent = None
- if self.previousSibling:
- self.previousSibling.nextSibling = self.nextSibling
- if self.nextSibling:
- self.nextSibling.previousSibling = self.previousSibling
- self.previousSibling = self.nextSibling = None
- return self
-
- def _lastRecursiveChild(self):
- "Finds the last element beneath this object to be parsed."
- lastChild = self
- while hasattr(lastChild, 'contents') and lastChild.contents:
- lastChild = lastChild.contents[-1]
- return lastChild
-
- def insert(self, position, newChild):
- if (isinstance(newChild, basestring)
- or isinstance(newChild, unicode)) \
- and not isinstance(newChild, NavigableString):
- newChild = NavigableString(newChild)
-
- position = min(position, len(self.contents))
- if hasattr(newChild, 'parent') and newChild.parent != None:
- # We're 'inserting' an element that's already one
- # of this object's children.
- if newChild.parent == self:
- index = self.find(newChild)
- if index and index < position:
- # Furthermore we're moving it further down the
- # list of this object's children. That means that
- # when we extract this element, our target index
- # will jump down one.
- position = position - 1
- newChild.extract()
-
- newChild.parent = self
- previousChild = None
- if position == 0:
- newChild.previousSibling = None
- newChild.previous = self
- else:
- previousChild = self.contents[position-1]
- newChild.previousSibling = previousChild
- newChild.previousSibling.nextSibling = newChild
- newChild.previous = previousChild._lastRecursiveChild()
- if newChild.previous:
- newChild.previous.next = newChild
-
- newChildsLastElement = newChild._lastRecursiveChild()
-
- if position >= len(self.contents):
- newChild.nextSibling = None
-
- parent = self
- parentsNextSibling = None
- while not parentsNextSibling:
- parentsNextSibling = parent.nextSibling
- parent = parent.parent
- if not parent: # This is the last element in the document.
- break
- if parentsNextSibling:
- newChildsLastElement.next = parentsNextSibling
- else:
- newChildsLastElement.next = None
- else:
- nextChild = self.contents[position]
- newChild.nextSibling = nextChild
- if newChild.nextSibling:
- newChild.nextSibling.previousSibling = newChild
- newChildsLastElement.next = nextChild
-
- if newChildsLastElement.next:
- newChildsLastElement.next.previous = newChildsLastElement
- self.contents.insert(position, newChild)
-
- def append(self, tag):
- """Appends the given tag to the contents of this tag."""
- self.insert(len(self.contents), tag)
-
- def findNext(self, name=None, attrs={}, text=None, **kwargs):
- """Returns the first item that matches the given criteria and
- appears after this Tag in the document."""
- return self._findOne(self.findAllNext, name, attrs, text, **kwargs)
-
- def findAllNext(self, name=None, attrs={}, text=None, limit=None,
- **kwargs):
- """Returns all items that match the given criteria and appear
- after this Tag in the document."""
- return self._findAll(name, attrs, text, limit, self.nextGenerator,
- **kwargs)
-
- def findNextSibling(self, name=None, attrs={}, text=None, **kwargs):
- """Returns the closest sibling to this Tag that matches the
- given criteria and appears after this Tag in the document."""
- return self._findOne(self.findNextSiblings, name, attrs, text,
- **kwargs)
-
- def findNextSiblings(self, name=None, attrs={}, text=None, limit=None,
- **kwargs):
- """Returns the siblings of this Tag that match the given
- criteria and appear after this Tag in the document."""
- return self._findAll(name, attrs, text, limit,
- self.nextSiblingGenerator, **kwargs)
- fetchNextSiblings = findNextSiblings # Compatibility with pre-3.x
-
- def findPrevious(self, name=None, attrs={}, text=None, **kwargs):
- """Returns the first item that matches the given criteria and
- appears before this Tag in the document."""
- return self._findOne(self.findAllPrevious, name, attrs, text, **kwargs)
-
- def findAllPrevious(self, name=None, attrs={}, text=None, limit=None,
- **kwargs):
- """Returns all items that match the given criteria and appear
- before this Tag in the document."""
- return self._findAll(name, attrs, text, limit, self.previousGenerator,
- **kwargs)
- fetchPrevious = findAllPrevious # Compatibility with pre-3.x
-
- def findPreviousSibling(self, name=None, attrs={}, text=None, **kwargs):
- """Returns the closest sibling to this Tag that matches the
- given criteria and appears before this Tag in the document."""
- return self._findOne(self.findPreviousSiblings, name, attrs, text,
- **kwargs)
-
- def findPreviousSiblings(self, name=None, attrs={}, text=None,
- limit=None, **kwargs):
- """Returns the siblings of this Tag that match the given
- criteria and appear before this Tag in the document."""
- return self._findAll(name, attrs, text, limit,
- self.previousSiblingGenerator, **kwargs)
- fetchPreviousSiblings = findPreviousSiblings # Compatibility with pre-3.x
-
- def findParent(self, name=None, attrs={}, **kwargs):
- """Returns the closest parent of this Tag that matches the given
- criteria."""
- # NOTE: We can't use _findOne because findParents takes a different
- # set of arguments.
- r = None
- l = self.findParents(name, attrs, 1)
- if l:
- r = l[0]
- return r
-
- def findParents(self, name=None, attrs={}, limit=None, **kwargs):
- """Returns the parents of this Tag that match the given
- criteria."""
-
- return self._findAll(name, attrs, None, limit, self.parentGenerator,
- **kwargs)
- fetchParents = findParents # Compatibility with pre-3.x
-
- #These methods do the real heavy lifting.
-
- def _findOne(self, method, name, attrs, text, **kwargs):
- r = None
- l = method(name, attrs, text, 1, **kwargs)
- if l:
- r = l[0]
- return r
-
- def _findAll(self, name, attrs, text, limit, generator, **kwargs):
- "Iterates over a generator looking for things that match."
-
- if isinstance(name, SoupStrainer):
- strainer = name
- else:
- # Build a SoupStrainer
- strainer = SoupStrainer(name, attrs, text, **kwargs)
- results = ResultSet(strainer)
- g = generator()
- while True:
- try:
- i = g.next()
- except StopIteration:
- break
- if i:
- found = strainer.search(i)
- if found:
- results.append(found)
- if limit and len(results) >= limit:
- break
- return results
-
- #These Generators can be used to navigate starting from both
- #NavigableStrings and Tags.
- def nextGenerator(self):
- i = self
- while i:
- i = i.next
- yield i
-
- def nextSiblingGenerator(self):
- i = self
- while i:
- i = i.nextSibling
- yield i
-
- def previousGenerator(self):
- i = self
- while i:
- i = i.previous
- yield i
-
- def previousSiblingGenerator(self):
- i = self
- while i:
- i = i.previousSibling
- yield i
-
- def parentGenerator(self):
- i = self
- while i:
- i = i.parent
- yield i
-
- # Utility methods
- def substituteEncoding(self, str, encoding=None):
- encoding = encoding or "utf-8"
- return str.replace("%SOUP-ENCODING%", encoding)
-
- def toEncoding(self, s, encoding=None):
- """Encodes an object to a string in some encoding, or to Unicode.
- ."""
- if isinstance(s, unicode):
- if encoding:
- s = s.encode(encoding)
- elif isinstance(s, str):
- if encoding:
- s = s.encode(encoding)
- else:
- s = unicode(s)
- else:
- if encoding:
- s = self.toEncoding(str(s), encoding)
- else:
- s = unicode(s)
- return s
-
-class NavigableString(unicode, PageElement):
-
- def __new__(cls, value):
- """Create a new NavigableString.
-
- When unpickling a NavigableString, this method is called with
- the string in DEFAULT_OUTPUT_ENCODING. That encoding needs to be
- passed in to the superclass's __new__ or the superclass won't know
- how to handle non-ASCII characters.
- """
- if isinstance(value, unicode):
- return unicode.__new__(cls, value)
- return unicode.__new__(cls, value, DEFAULT_OUTPUT_ENCODING)
-
- def __getnewargs__(self):
- return (unicode(self),)
-
- def __getattr__(self, attr):
- """text.string gives you text. This is for backwards
- compatibility for Navigable*String, but for CData* it lets you
- get the string without the CData wrapper."""
- if attr == 'string':
- return self
- else:
- raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr)
-
- def encode(self, encoding=DEFAULT_OUTPUT_ENCODING):
- return self.decode().encode(encoding)
-
- def decodeGivenEventualEncoding(self, eventualEncoding):
- return self
-
-class CData(NavigableString):
-
- def decodeGivenEventualEncoding(self, eventualEncoding):
- return u'<![CDATA[' + self + u']]>'
-
-class ProcessingInstruction(NavigableString):
-
- def decodeGivenEventualEncoding(self, eventualEncoding):
- output = self
- if u'%SOUP-ENCODING%' in output:
- output = self.substituteEncoding(output, eventualEncoding)
- return u'<?' + output + u'?>'
-
-class Comment(NavigableString):
- def decodeGivenEventualEncoding(self, eventualEncoding):
- return u'<!--' + self + u'-->'
-
-class Declaration(NavigableString):
- def decodeGivenEventualEncoding(self, eventualEncoding):
- return u'<!' + self + u'>'
-
-class Tag(PageElement):
-
- """Represents a found HTML tag with its attributes and contents."""
-
- def _invert(h):
- "Cheap function to invert a hash."
- i = {}
- for k,v in h.items():
- i[v] = k
- return i
-
- XML_ENTITIES_TO_SPECIAL_CHARS = { "apos" : "'",
- "quot" : '"',
- "amp" : "&",
- "lt" : "<",
- "gt" : ">" }
-
- XML_SPECIAL_CHARS_TO_ENTITIES = _invert(XML_ENTITIES_TO_SPECIAL_CHARS)
-
- def _convertEntities(self, match):
- """Used in a call to re.sub to replace HTML, XML, and numeric
- entities with the appropriate Unicode characters. If HTML
- entities are being converted, any unrecognized entities are
- escaped."""
- x = match.group(1)
- if self.convertHTMLEntities and x in name2codepoint:
- return unichr(name2codepoint[x])
- elif x in self.XML_ENTITIES_TO_SPECIAL_CHARS:
- if self.convertXMLEntities:
- return self.XML_ENTITIES_TO_SPECIAL_CHARS[x]
- else:
- return u'&%s;' % x
- elif len(x) > 0 and x[0] == '#':
- # Handle numeric entities
- if len(x) > 1 and x[1] == 'x':
- return unichr(int(x[2:], 16))
- else:
- return unichr(int(x[1:]))
-
- elif self.escapeUnrecognizedEntities:
- return u'&amp;%s;' % x
- else:
- return u'&%s;' % x
-
- def __init__(self, parser, name, attrs=None, parent=None,
- previous=None):
- "Basic constructor."
-
- # We don't actually store the parser object: that lets extracted
- # chunks be garbage-collected
- self.parserClass = parser.__class__
- self.isSelfClosing = parser.isSelfClosingTag(name)
- self.name = name
- if attrs == None:
- attrs = []
- self.attrs = attrs
- self.contents = []
- self.setup(parent, previous)
- self.hidden = False
- self.containsSubstitutions = False
- self.convertHTMLEntities = parser.convertHTMLEntities
- self.convertXMLEntities = parser.convertXMLEntities
- self.escapeUnrecognizedEntities = parser.escapeUnrecognizedEntities
-
- def convert(kval):
- "Converts HTML, XML and numeric entities in the attribute value."
- k, val = kval
- if val is None:
- return kval
- return (k, re.sub("&(#\d+|#x[0-9a-fA-F]+|\w+);",
- self._convertEntities, val))
- self.attrs = map(convert, self.attrs)
-
- def get(self, key, default=None):
- """Returns the value of the 'key' attribute for the tag, or
- the value given for 'default' if it doesn't have that
- attribute."""
- return self._getAttrMap().get(key, default)
-
- def has_key(self, key):
- return self._getAttrMap().has_key(key)
-
- def __getitem__(self, key):
- """tag[key] returns the value of the 'key' attribute for the tag,
- and throws an exception if it's not there."""
- return self._getAttrMap()[key]
-
- def __iter__(self):
- "Iterating over a tag iterates over its contents."
- return iter(self.contents)
-
- def __len__(self):
- "The length of a tag is the length of its list of contents."
- return len(self.contents)
-
- def __contains__(self, x):
- return x in self.contents
-
- def __nonzero__(self):
- "A tag is non-None even if it has no contents."
- return True
-
- def __setitem__(self, key, value):
- """Setting tag[key] sets the value of the 'key' attribute for the
- tag."""
- self._getAttrMap()
- self.attrMap[key] = value
- found = False
- for i in range(0, len(self.attrs)):
- if self.attrs[i][0] == key:
- self.attrs[i] = (key, value)
- found = True
- if not found:
- self.attrs.append((key, value))
- self._getAttrMap()[key] = value
-
- def __delitem__(self, key):
- "Deleting tag[key] deletes all 'key' attributes for the tag."
- for item in self.attrs:
- if item[0] == key:
- self.attrs.remove(item)
- #We don't break because bad HTML can define the same
- #attribute multiple times.
- self._getAttrMap()
- if self.attrMap.has_key(key):
- del self.attrMap[key]
-
- def __call__(self, *args, **kwargs):
- """Calling a tag like a function is the same as calling its
- findAll() method. Eg. tag('a') returns a list of all the A tags
- found within this tag."""
- return apply(self.findAll, args, kwargs)
-
- def __getattr__(self, tag):
- #print "Getattr %s.%s" % (self.__class__, tag)
- if len(tag) > 3 and tag.rfind('Tag') == len(tag)-3:
- return self.find(tag[:-3])
- elif tag.find('__') != 0:
- return self.find(tag)
- raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__, tag)
-
- def __eq__(self, other):
- """Returns true iff this tag has the same name, the same attributes,
- and the same contents (recursively) as the given tag.
-
- NOTE: right now this will return false if two tags have the
- same attributes in a different order. Should this be fixed?"""
- if not hasattr(other, 'name') or not hasattr(other, 'attrs') or not hasattr(other, 'contents') or self.name != other.name or self.attrs != other.attrs or len(self) != len(other):
- return False
- for i in range(0, len(self.contents)):
- if self.contents[i] != other.contents[i]:
- return False
- return True
-
- def __ne__(self, other):
- """Returns true iff this tag is not identical to the other tag,
- as defined in __eq__."""
- return not self == other
-
- def __repr__(self, encoding=DEFAULT_OUTPUT_ENCODING):
- """Renders this tag as a string."""
- return self.decode(eventualEncoding=encoding)
-
- BARE_AMPERSAND_OR_BRACKET = re.compile("([<>]|"
- + "&(?!#\d+;|#x[0-9a-fA-F]+;|\w+;)"
- + ")")
-
- def _sub_entity(self, x):
- """Used with a regular expression to substitute the
- appropriate XML entity for an XML special character."""
- return "&" + self.XML_SPECIAL_CHARS_TO_ENTITIES[x.group(0)[0]] + ";"
-
- def __unicode__(self):
- return self.decode()
-
- def __str__(self):
- return self.encode()
-
- def encode(self, encoding=DEFAULT_OUTPUT_ENCODING,
- prettyPrint=False, indentLevel=0):
- return self.decode(prettyPrint, indentLevel, encoding).encode(encoding)
-
- def decode(self, prettyPrint=False, indentLevel=0,
- eventualEncoding=DEFAULT_OUTPUT_ENCODING):
- """Returns a string or Unicode representation of this tag and
- its contents. To get Unicode, pass None for encoding."""
-
- attrs = []
- if self.attrs:
- for key, val in self.attrs:
- fmt = '%s="%s"'
- if isString(val):
- if (self.containsSubstitutions
- and eventualEncoding is not None
- and '%SOUP-ENCODING%' in val):
- val = self.substituteEncoding(val, eventualEncoding)
-
- # The attribute value either:
- #
- # * Contains no embedded double quotes or single quotes.
- # No problem: we enclose it in double quotes.
- # * Contains embedded single quotes. No problem:
- # double quotes work here too.
- # * Contains embedded double quotes. No problem:
- # we enclose it in single quotes.
- # * Embeds both single _and_ double quotes. This
- # can't happen naturally, but it can happen if
- # you modify an attribute value after parsing
- # the document. Now we have a bit of a
- # problem. We solve it by enclosing the
- # attribute in single quotes, and escaping any
- # embedded single quotes to XML entities.
- if '"' in val:
- fmt = "%s='%s'"
- if "'" in val:
- # TODO: replace with apos when
- # appropriate.
- val = val.replace("'", "&squot;")
-
- # Now we're okay w/r/t quotes. But the attribute
- # value might also contain angle brackets, or
- # ampersands that aren't part of entities. We need
- # to escape those to XML entities too.
- val = self.BARE_AMPERSAND_OR_BRACKET.sub(self._sub_entity, val)
- if val is None:
- # Handle boolean attributes.
- decoded = key
- else:
- decoded = fmt % (key, val)
- attrs.append(decoded)
- close = ''
- closeTag = ''
- if self.isSelfClosing:
- close = ' /'
- else:
- closeTag = '</%s>' % self.name
-
- indentTag, indentContents = 0, 0
- if prettyPrint:
- indentTag = indentLevel
- space = (' ' * (indentTag-1))
- indentContents = indentTag + 1
- contents = self.decodeContents(prettyPrint, indentContents,
- eventualEncoding)
- if self.hidden:
- s = contents
- else:
- s = []
- attributeString = ''
- if attrs:
- attributeString = ' ' + ' '.join(attrs)
- if prettyPrint:
- s.append(space)
- s.append('<%s%s%s>' % (self.name, attributeString, close))
- if prettyPrint:
- s.append("\n")
- s.append(contents)
- if prettyPrint and contents and contents[-1] != "\n":
- s.append("\n")
- if prettyPrint and closeTag:
- s.append(space)
- s.append(closeTag)
- if prettyPrint and closeTag and self.nextSibling:
- s.append("\n")
- s = ''.join(s)
- return s
-
- def decompose(self):
- """Recursively destroys the contents of this tree."""
- contents = [i for i in self.contents]
- for i in contents:
- if isinstance(i, Tag):
- i.decompose()
- else:
- i.extract()
- self.extract()
-
- def prettify(self, encoding=DEFAULT_OUTPUT_ENCODING):
- return self.encode(encoding, True)
-
- def encodeContents(self, encoding=DEFAULT_OUTPUT_ENCODING,
- prettyPrint=False, indentLevel=0):
- return self.decodeContents(prettyPrint, indentLevel).encode(encoding)
-
- def decodeContents(self, prettyPrint=False, indentLevel=0,
- eventualEncoding=DEFAULT_OUTPUT_ENCODING):
- """Renders the contents of this tag as a string in the given
- encoding. If encoding is None, returns a Unicode string.."""
- s=[]
- for c in self:
- text = None
- if isinstance(c, NavigableString):
- text = c.decodeGivenEventualEncoding(eventualEncoding)
- elif isinstance(c, Tag):
- s.append(c.decode(prettyPrint, indentLevel, eventualEncoding))
- if text and prettyPrint:
- text = text.strip()
- if text:
- if prettyPrint:
- s.append(" " * (indentLevel-1))
- s.append(text)
- if prettyPrint:
- s.append("\n")
- return ''.join(s)
-
- #Soup methods
-
- def find(self, name=None, attrs={}, recursive=True, text=None,
- **kwargs):
- """Return only the first child of this Tag matching the given
- criteria."""
- r = None
- l = self.findAll(name, attrs, recursive, text, 1, **kwargs)
- if l:
- r = l[0]
- return r
- findChild = find
-
- def findAll(self, name=None, attrs={}, recursive=True, text=None,
- limit=None, **kwargs):
- """Extracts a list of Tag objects that match the given
- criteria. You can specify the name of the Tag and any
- attributes you want the Tag to have.
-
- The value of a key-value pair in the 'attrs' map can be a
- string, a list of strings, a regular expression object, or a
- callable that takes a string and returns whether or not the
- string matches for some custom definition of 'matches'. The
- same is true of the tag name."""
- generator = self.recursiveChildGenerator
- if not recursive:
- generator = self.childGenerator
- return self._findAll(name, attrs, text, limit, generator, **kwargs)
- findChildren = findAll
-
- # Pre-3.x compatibility methods. Will go away in 4.0.
- first = find
- fetch = findAll
-
- def fetchText(self, text=None, recursive=True, limit=None):
- return self.findAll(text=text, recursive=recursive, limit=limit)
-
- def firstText(self, text=None, recursive=True):
- return self.find(text=text, recursive=recursive)
-
- # 3.x compatibility methods. Will go away in 4.0.
- def renderContents(self, encoding=DEFAULT_OUTPUT_ENCODING,
- prettyPrint=False, indentLevel=0):
- if encoding is None:
- return self.decodeContents(prettyPrint, indentLevel, encoding)
- else:
- return self.encodeContents(encoding, prettyPrint, indentLevel)
-
-
- #Private methods
-
- def _getAttrMap(self):
- """Initializes a map representation of this tag's attributes,
- if not already initialized."""
- if not getattr(self, 'attrMap'):
- self.attrMap = {}
- for (key, value) in self.attrs:
- self.attrMap[key] = value
- return self.attrMap
-
- #Generator methods
- def recursiveChildGenerator(self):
- if not len(self.contents):
- raise StopIteration
- stopNode = self._lastRecursiveChild().next
- current = self.contents[0]
- while current is not stopNode:
- yield current
- current = current.next
-
- def childGenerator(self):
- if not len(self.contents):
- raise StopIteration
- current = self.contents[0]
- while current:
- yield current
- current = current.nextSibling
- raise StopIteration
-
-# Next, a couple classes to represent queries and their results.
-class SoupStrainer:
- """Encapsulates a number of ways of matching a markup element (tag or
- text)."""
-
- def __init__(self, name=None, attrs={}, text=None, **kwargs):
- self.name = name
- if isString(attrs):
- kwargs['class'] = attrs
- attrs = None
- if kwargs:
- if attrs:
- attrs = attrs.copy()
- attrs.update(kwargs)
- else:
- attrs = kwargs
- self.attrs = attrs
- self.text = text
-
- def __str__(self):
- if self.text:
- return self.text
- else:
- return "%s|%s" % (self.name, self.attrs)
-
- def searchTag(self, markupName=None, markupAttrs={}):
- found = None
- markup = None
- if isinstance(markupName, Tag):
- markup = markupName
- markupAttrs = markup
- callFunctionWithTagData = callable(self.name) \
- and not isinstance(markupName, Tag)
-
- if (not self.name) \
- or callFunctionWithTagData \
- or (markup and self._matches(markup, self.name)) \
- or (not markup and self._matches(markupName, self.name)):
- if callFunctionWithTagData:
- match = self.name(markupName, markupAttrs)
- else:
- match = True
- markupAttrMap = None
- for attr, matchAgainst in self.attrs.items():
- if not markupAttrMap:
- if hasattr(markupAttrs, 'get'):
- markupAttrMap = markupAttrs
- else:
- markupAttrMap = {}
- for k,v in markupAttrs:
- markupAttrMap[k] = v
- attrValue = markupAttrMap.get(attr)
- if not self._matches(attrValue, matchAgainst):
- match = False
- break
- if match:
- if markup:
- found = markup
- else:
- found = markupName
- return found
-
- def search(self, markup):
- #print 'looking for %s in %s' % (self, markup)
- found = None
- # If given a list of items, scan it for a text element that
- # matches.
- if isList(markup) and not isinstance(markup, Tag):
- for element in markup:
- if isinstance(element, NavigableString) \
- and self.search(element):
- found = element
- break
- # If it's a Tag, make sure its name or attributes match.
- # Don't bother with Tags if we're searching for text.
- elif isinstance(markup, Tag):
- if not self.text:
- found = self.searchTag(markup)
- # If it's text, make sure the text matches.
- elif isinstance(markup, NavigableString) or \
- isString(markup):
- if self._matches(markup, self.text):
- found = markup
- else:
- raise Exception, "I don't know how to match against a %s" \
- % markup.__class__
- return found
-
- def _matches(self, markup, matchAgainst):
- #print "Matching %s against %s" % (markup, matchAgainst)
- result = False
- if matchAgainst == True and type(matchAgainst) == types.BooleanType:
- result = markup != None
- elif callable(matchAgainst):
- result = matchAgainst(markup)
- else:
- #Custom match methods take the tag as an argument, but all
- #other ways of matching match the tag name as a string.
- if isinstance(markup, Tag):
- markup = markup.name
- if markup is not None and not isString(markup):
- markup = unicode(markup)
- #Now we know that chunk is either a string, or None.
- if hasattr(matchAgainst, 'match'):
- # It's a regexp object.
- result = markup and matchAgainst.search(markup)
- elif (isList(matchAgainst)
- and (markup is not None or not isString(matchAgainst))):
- result = markup in matchAgainst
- elif hasattr(matchAgainst, 'items'):
- result = markup.has_key(matchAgainst)
- elif matchAgainst and isString(markup):
- if isinstance(markup, unicode):
- matchAgainst = unicode(matchAgainst)
- else:
- matchAgainst = str(matchAgainst)
-
- if not result:
- result = matchAgainst == markup
- return result
-
-class ResultSet(list):
- """A ResultSet is just a list that keeps track of the SoupStrainer
- that created it."""
- def __init__(self, source):
- list.__init__([])
- self.source = source
-
-# Now, some helper functions.
-
-def isList(l):
- """Convenience method that works with all 2.x versions of Python
- to determine whether or not something is listlike."""
- return ((hasattr(l, '__iter__') and not isString(l))
- or (type(l) in (types.ListType, types.TupleType)))
-
-def isString(s):
- """Convenience method that works with all 2.x versions of Python
- to determine whether or not something is stringlike."""
- try:
- return isinstance(s, unicode) or isinstance(s, basestring)
- except NameError:
- return isinstance(s, str)
-
-def buildTagMap(default, *args):
- """Turns a list of maps, lists, or scalars into a single map.
- Used to build the SELF_CLOSING_TAGS, NESTABLE_TAGS, and
- NESTING_RESET_TAGS maps out of lists and partial maps."""
- built = {}
- for portion in args:
- if hasattr(portion, 'items'):
- #It's a map. Merge it.
- for k,v in portion.items():
- built[k] = v
- elif isList(portion) and not isString(portion):
- #It's a list. Map each item to the default.
- for k in portion:
- built[k] = default
- else:
- #It's a scalar. Map it to the default.
- built[portion] = default
- return built
-
-# Now, the parser classes.
-
-class HTMLParserBuilder(HTMLParser):
-
- def __init__(self, soup):
- HTMLParser.__init__(self)
- self.soup = soup
-
- # We inherit feed() and reset().
-
- def handle_starttag(self, name, attrs):
- if name == 'meta':
- self.soup.extractCharsetFromMeta(attrs)
- else:
- self.soup.unknown_starttag(name, attrs)
-
- def handle_endtag(self, name):
- self.soup.unknown_endtag(name)
-
- def handle_data(self, content):
- self.soup.handle_data(content)
-
- def _toStringSubclass(self, text, subclass):
- """Adds a certain piece of text to the tree as a NavigableString
- subclass."""
- self.soup.endData()
- self.handle_data(text)
- self.soup.endData(subclass)
-
- def handle_pi(self, text):
- """Handle a processing instruction as a ProcessingInstruction
- object, possibly one with a %SOUP-ENCODING% slot into which an
- encoding will be plugged later."""
- if text[:3] == "xml":
- text = u"xml version='1.0' encoding='%SOUP-ENCODING%'"
- self._toStringSubclass(text, ProcessingInstruction)
-
- def handle_comment(self, text):
- "Handle comments as Comment objects."
- self._toStringSubclass(text, Comment)
-
- def handle_charref(self, ref):
- "Handle character references as data."
- if self.soup.convertEntities:
- data = unichr(int(ref))
- else:
- data = '&#%s;' % ref
- self.handle_data(data)
-
- def handle_entityref(self, ref):
- """Handle entity references as data, possibly converting known
- HTML and/or XML entity references to the corresponding Unicode
- characters."""
- data = None
- if self.soup.convertHTMLEntities:
- try:
- data = unichr(name2codepoint[ref])
- except KeyError:
- pass
-
- if not data and self.soup.convertXMLEntities:
- data = self.soup.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref)
-
- if not data and self.soup.convertHTMLEntities and \
- not self.soup.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref):
- # TODO: We've got a problem here. We're told this is
- # an entity reference, but it's not an XML entity
- # reference or an HTML entity reference. Nonetheless,
- # the logical thing to do is to pass it through as an
- # unrecognized entity reference.
- #
- # Except: when the input is "&carol;" this function
- # will be called with input "carol". When the input is
- # "AT&T", this function will be called with input
- # "T". We have no way of knowing whether a semicolon
- # was present originally, so we don't know whether
- # this is an unknown entity or just a misplaced
- # ampersand.
- #
- # The more common case is a misplaced ampersand, so I
- # escape the ampersand and omit the trailing semicolon.
- data = "&amp;%s" % ref
- if not data:
- # This case is different from the one above, because we
- # haven't already gone through a supposedly comprehensive
- # mapping of entities to Unicode characters. We might not
- # have gone through any mapping at all. So the chances are
- # very high that this is a real entity, and not a
- # misplaced ampersand.
- data = "&%s;" % ref
- self.handle_data(data)
-
- def handle_decl(self, data):
- "Handle DOCTYPEs and the like as Declaration objects."
- self._toStringSubclass(data, Declaration)
-
- def parse_declaration(self, i):
- """Treat a bogus SGML declaration as raw data. Treat a CDATA
- declaration as a CData object."""
- j = None
- if self.rawdata[i:i+9] == '<![CDATA[':
- k = self.rawdata.find(']]>', i)
- if k == -1:
- k = len(self.rawdata)
- data = self.rawdata[i+9:k]
- j = k+3
- self._toStringSubclass(data, CData)
- else:
- try:
- j = HTMLParser.parse_declaration(self, i)
- except HTMLParseError:
- toHandle = self.rawdata[i:]
- self.handle_data(toHandle)
- j = i + len(toHandle)
- return j
-
-
-class BeautifulStoneSoup(Tag):
-
- """This class contains the basic parser and search code. It defines
- a parser that knows nothing about tag behavior except for the
- following:
-
- You can't close a tag without closing all the tags it encloses.
- That is, "<foo><bar></foo>" actually means
- "<foo><bar></bar></foo>".
-
- [Another possible explanation is "<foo><bar /></foo>", but since
- this class defines no SELF_CLOSING_TAGS, it will never use that
- explanation.]
-
- This class is useful for parsing XML or made-up markup languages,
- or when BeautifulSoup makes an assumption counter to what you were
- expecting."""
-
- SELF_CLOSING_TAGS = {}
- NESTABLE_TAGS = {}
- RESET_NESTING_TAGS = {}
- QUOTE_TAGS = {}
- PRESERVE_WHITESPACE_TAGS = []
-
- MARKUP_MASSAGE = [(re.compile('(<[^<>]*)/>'),
- lambda x: x.group(1) + ' />'),
- (re.compile('<!\s+([^<>]*)>'),
- lambda x: '<!' + x.group(1) + '>')
- ]
-
- ROOT_TAG_NAME = u'[document]'
-
- HTML_ENTITIES = "html"
- XML_ENTITIES = "xml"
- XHTML_ENTITIES = "xhtml"
- # TODO: This only exists for backwards-compatibility
- ALL_ENTITIES = XHTML_ENTITIES
-
- # Used when determining whether a text node is all whitespace and
- # can be replaced with a single space. A text node that contains
- # fancy Unicode spaces (usually non-breaking) should be left
- # alone.
- STRIP_ASCII_SPACES = { 9: None, 10: None, 12: None, 13: None, 32: None, }
-
- def __init__(self, markup="", parseOnlyThese=None, fromEncoding=None,
- markupMassage=True, smartQuotesTo=XML_ENTITIES,
- convertEntities=None, selfClosingTags=None, isHTML=False,
- builder=HTMLParserBuilder):
- """The Soup object is initialized as the 'root tag', and the
- provided markup (which can be a string or a file-like object)
- is fed into the underlying parser.
-
- HTMLParser will process most bad HTML, and the BeautifulSoup
- class has some tricks for dealing with some HTML that kills
- HTMLParser, but Beautiful Soup can nonetheless choke or lose data
- if your data uses self-closing tags or declarations
- incorrectly.
-
- By default, Beautiful Soup uses regexes to sanitize input,
- avoiding the vast majority of these problems. If the problems
- don't apply to you, pass in False for markupMassage, and
- you'll get better performance.
-
- The default parser massage techniques fix the two most common
- instances of invalid HTML that choke HTMLParser:
-
- <br/> (No space between name of closing tag and tag close)
- <! --Comment--> (Extraneous whitespace in declaration)
-
- You can pass in a custom list of (RE object, replace method)
- tuples to get Beautiful Soup to scrub your input the way you
- want."""
-
- self.parseOnlyThese = parseOnlyThese
- self.fromEncoding = fromEncoding
- self.smartQuotesTo = smartQuotesTo
- self.convertEntities = convertEntities
- # Set the rules for how we'll deal with the entities we
- # encounter
- if self.convertEntities:
- # It doesn't make sense to convert encoded characters to
- # entities even while you're converting entities to Unicode.
- # Just convert it all to Unicode.
- self.smartQuotesTo = None
- if convertEntities == self.HTML_ENTITIES:
- self.convertXMLEntities = False
- self.convertHTMLEntities = True
- self.escapeUnrecognizedEntities = True
- elif convertEntities == self.XHTML_ENTITIES:
- self.convertXMLEntities = True
- self.convertHTMLEntities = True
- self.escapeUnrecognizedEntities = False
- elif convertEntities == self.XML_ENTITIES:
- self.convertXMLEntities = True
- self.convertHTMLEntities = False
- self.escapeUnrecognizedEntities = False
- else:
- self.convertXMLEntities = False
- self.convertHTMLEntities = False
- self.escapeUnrecognizedEntities = False
-
- self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags)
- self.builder = builder(self)
- self.reset()
-
- if hasattr(markup, 'read'): # It's a file-type object.
- markup = markup.read()
- self.markup = markup
- self.markupMassage = markupMassage
- try:
- self._feed(isHTML=isHTML)
- except StopParsing:
- pass
- self.markup = None # The markup can now be GCed.
- self.builder = None # So can the builder.
-
- def _feed(self, inDocumentEncoding=None, isHTML=False):
- # Convert the document to Unicode.
- markup = self.markup
- if isinstance(markup, unicode):
- if not hasattr(self, 'originalEncoding'):
- self.originalEncoding = None
- else:
- dammit = UnicodeDammit\
- (markup, [self.fromEncoding, inDocumentEncoding],
- smartQuotesTo=self.smartQuotesTo, isHTML=isHTML)
- markup = dammit.unicode
- self.originalEncoding = dammit.originalEncoding
- self.declaredHTMLEncoding = dammit.declaredHTMLEncoding
- if markup:
- if self.markupMassage:
- if not isList(self.markupMassage):
- self.markupMassage = self.MARKUP_MASSAGE
- for fix, m in self.markupMassage:
- markup = fix.sub(m, markup)
- # TODO: We get rid of markupMassage so that the
- # soup object can be deepcopied later on. Some
- # Python installations can't copy regexes. If anyone
- # was relying on the existence of markupMassage, this
- # might cause problems.
- del(self.markupMassage)
- self.builder.reset()
-
- self.builder.feed(markup)
- # Close out any unfinished strings and close all the open tags.
- self.endData()
- while self.currentTag.name != self.ROOT_TAG_NAME:
- self.popTag()
-
- def isSelfClosingTag(self, name):
- """Returns true iff the given string is the name of a
- self-closing tag according to this parser."""
- return self.SELF_CLOSING_TAGS.has_key(name) \
- or self.instanceSelfClosingTags.has_key(name)
-
- def reset(self):
- Tag.__init__(self, self, self.ROOT_TAG_NAME)
- self.hidden = 1
- self.builder.reset()
- self.currentData = []
- self.currentTag = None
- self.tagStack = []
- self.quoteStack = []
- self.pushTag(self)
-
- def popTag(self):
- tag = self.tagStack.pop()
- # Tags with just one string-owning child get the child as a
- # 'string' property, so that soup.tag.string is shorthand for
- # soup.tag.contents[0]
- if len(self.currentTag.contents) == 1 and \
- isinstance(self.currentTag.contents[0], NavigableString):
- self.currentTag.string = self.currentTag.contents[0]
-
- #print "Pop", tag.name
- if self.tagStack:
- self.currentTag = self.tagStack[-1]
- return self.currentTag
-
- def pushTag(self, tag):
- #print "Push", tag.name
- if self.currentTag:
- self.currentTag.contents.append(tag)
- self.tagStack.append(tag)
- self.currentTag = self.tagStack[-1]
-
- def endData(self, containerClass=NavigableString):
- if self.currentData:
- currentData = u''.join(self.currentData)
- if (currentData.translate(self.STRIP_ASCII_SPACES) == '' and
- not set([tag.name for tag in self.tagStack]).intersection(
- self.PRESERVE_WHITESPACE_TAGS)):
- if '\n' in currentData:
- currentData = '\n'
- else:
- currentData = ' '
- self.currentData = []
- if self.parseOnlyThese and len(self.tagStack) <= 1 and \
- (not self.parseOnlyThese.text or \
- not self.parseOnlyThese.search(currentData)):
- return
- o = containerClass(currentData)
- o.setup(self.currentTag, self.previous)
- if self.previous:
- self.previous.next = o
- self.previous = o
- self.currentTag.contents.append(o)
-
-
- def _popToTag(self, name, inclusivePop=True):
- """Pops the tag stack up to and including the most recent
- instance of the given tag. If inclusivePop is false, pops the tag
- stack up to but *not* including the most recent instqance of
- the given tag."""
- #print "Popping to %s" % name
- if name == self.ROOT_TAG_NAME:
- return
-
- numPops = 0
- mostRecentTag = None
- for i in range(len(self.tagStack)-1, 0, -1):
- if name == self.tagStack[i].name:
- numPops = len(self.tagStack)-i
- break
- if not inclusivePop:
- numPops = numPops - 1
-
- for i in range(0, numPops):
- mostRecentTag = self.popTag()
- return mostRecentTag
-
- def _smartPop(self, name):
-
- """We need to pop up to the previous tag of this type, unless
- one of this tag's nesting reset triggers comes between this
- tag and the previous tag of this type, OR unless this tag is a
- generic nesting trigger and another generic nesting trigger
- comes between this tag and the previous tag of this type.
-
- Examples:
- <p>Foo<b>Bar *<p>* should pop to 'p', not 'b'.
- <p>Foo<table>Bar *<p>* should pop to 'table', not 'p'.
- <p>Foo<table><tr>Bar *<p>* should pop to 'tr', not 'p'.
-
- <li><ul><li> *<li>* should pop to 'ul', not the first 'li'.
- <tr><table><tr> *<tr>* should pop to 'table', not the first 'tr'
- <td><tr><td> *<td>* should pop to 'tr', not the first 'td'
- """
-
- nestingResetTriggers = self.NESTABLE_TAGS.get(name)
- isNestable = nestingResetTriggers != None
- isResetNesting = self.RESET_NESTING_TAGS.has_key(name)
- popTo = None
- inclusive = True
- for i in range(len(self.tagStack)-1, 0, -1):
- p = self.tagStack[i]
- if (not p or p.name == name) and not isNestable:
- #Non-nestable tags get popped to the top or to their
- #last occurance.
- popTo = name
- break
- if (nestingResetTriggers != None
- and p.name in nestingResetTriggers) \
- or (nestingResetTriggers == None and isResetNesting
- and self.RESET_NESTING_TAGS.has_key(p.name)):
-
- #If we encounter one of the nesting reset triggers
- #peculiar to this tag, or we encounter another tag
- #that causes nesting to reset, pop up to but not
- #including that tag.
- popTo = p.name
- inclusive = False
- break
- p = p.parent
- if popTo:
- self._popToTag(popTo, inclusive)
-
- def unknown_starttag(self, name, attrs, selfClosing=0):
- #print "Start tag %s: %s" % (name, attrs)
- if self.quoteStack:
- #This is not a real tag.
- #print "<%s> is not real!" % name
- attrs = ''.join(map(lambda(x, y): ' %s="%s"' % (x, y), attrs))
- self.handle_data('<%s%s>' % (name, attrs))
- return
- self.endData()
-
- if not self.isSelfClosingTag(name) and not selfClosing:
- self._smartPop(name)
-
- if self.parseOnlyThese and len(self.tagStack) <= 1 \
- and (self.parseOnlyThese.text or not self.parseOnlyThese.searchTag(name, attrs)):
- return
-
- tag = Tag(self, name, attrs, self.currentTag, self.previous)
- if self.previous:
- self.previous.next = tag
- self.previous = tag
- self.pushTag(tag)
- if selfClosing or self.isSelfClosingTag(name):
- self.popTag()
- if name in self.QUOTE_TAGS:
- #print "Beginning quote (%s)" % name
- self.quoteStack.append(name)
- self.literal = 1
- return tag
-
- def unknown_endtag(self, name):
- #print "End tag %s" % name
- if self.quoteStack and self.quoteStack[-1] != name:
- #This is not a real end tag.
- #print "</%s> is not real!" % name
- self.handle_data('</%s>' % name)
- return
- self.endData()
- self._popToTag(name)
- if self.quoteStack and self.quoteStack[-1] == name:
- self.quoteStack.pop()
- self.literal = (len(self.quoteStack) > 0)
-
- def handle_data(self, data):
- self.currentData.append(data)
-
- def extractCharsetFromMeta(self, attrs):
- self.unknown_starttag('meta', attrs)
-
-
-class BeautifulSoup(BeautifulStoneSoup):
-
- """This parser knows the following facts about HTML:
-
- * Some tags have no closing tag and should be interpreted as being
- closed as soon as they are encountered.
-
- * The text inside some tags (ie. 'script') may contain tags which
- are not really part of the document and which should be parsed
- as text, not tags. If you want to parse the text as tags, you can
- always fetch it and parse it explicitly.
-
- * Tag nesting rules:
-
- Most tags can't be nested at all. For instance, the occurance of
- a <p> tag should implicitly close the previous <p> tag.
-
- <p>Para1<p>Para2
- should be transformed into:
- <p>Para1</p><p>Para2
-
- Some tags can be nested arbitrarily. For instance, the occurance
- of a <blockquote> tag should _not_ implicitly close the previous
- <blockquote> tag.
-
- Alice said: <blockquote>Bob said: <blockquote>Blah
- should NOT be transformed into:
- Alice said: <blockquote>Bob said: </blockquote><blockquote>Blah
-
- Some tags can be nested, but the nesting is reset by the
- interposition of other tags. For instance, a <tr> tag should
- implicitly close the previous <tr> tag within the same <table>,
- but not close a <tr> tag in another table.
-
- <table><tr>Blah<tr>Blah
- should be transformed into:
- <table><tr>Blah</tr><tr>Blah
- but,
- <tr>Blah<table><tr>Blah
- should NOT be transformed into
- <tr>Blah<table></tr><tr>Blah
-
- Differing assumptions about tag nesting rules are a major source
- of problems with the BeautifulSoup class. If BeautifulSoup is not
- treating as nestable a tag your page author treats as nestable,
- try ICantBelieveItsBeautifulSoup, MinimalSoup, or
- BeautifulStoneSoup before writing your own subclass."""
-
- def __init__(self, *args, **kwargs):
- if not kwargs.has_key('smartQuotesTo'):
- kwargs['smartQuotesTo'] = self.HTML_ENTITIES
- kwargs['isHTML'] = True
- BeautifulStoneSoup.__init__(self, *args, **kwargs)
-
- SELF_CLOSING_TAGS = buildTagMap(None,
- ['br' , 'hr', 'input', 'img', 'meta',
- 'spacer', 'link', 'frame', 'base'])
-
- PRESERVE_WHITESPACE_TAGS = set(['pre', 'textarea'])
-
- QUOTE_TAGS = {'script' : None, 'textarea' : None}
-
- #According to the HTML standard, each of these inline tags can
- #contain another tag of the same type. Furthermore, it's common
- #to actually use these tags this way.
- NESTABLE_INLINE_TAGS = ['span', 'font', 'q', 'object', 'bdo', 'sub', 'sup',
- 'center']
-
- #According to the HTML standard, these block tags can contain
- #another tag of the same type. Furthermore, it's common
- #to actually use these tags this way.
- NESTABLE_BLOCK_TAGS = ['blockquote', 'div', 'fieldset', 'ins', 'del']
-
- #Lists can contain other lists, but there are restrictions.
- NESTABLE_LIST_TAGS = { 'ol' : [],
- 'ul' : [],
- 'li' : ['ul', 'ol'],
- 'dl' : [],
- 'dd' : ['dl'],
- 'dt' : ['dl'] }
-
- #Tables can contain other tables, but there are restrictions.
- NESTABLE_TABLE_TAGS = {'table' : [],
- 'tr' : ['table', 'tbody', 'tfoot', 'thead'],
- 'td' : ['tr'],
- 'th' : ['tr'],
- 'thead' : ['table'],
- 'tbody' : ['table'],
- 'tfoot' : ['table'],
- }
-
- NON_NESTABLE_BLOCK_TAGS = ['address', 'form', 'p', 'pre']
-
- #If one of these tags is encountered, all tags up to the next tag of
- #this type are popped.
- RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript',
- NON_NESTABLE_BLOCK_TAGS,
- NESTABLE_LIST_TAGS,
- NESTABLE_TABLE_TAGS)
-
- NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS,
- NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)
-
- # Used to detect the charset in a META tag; see start_meta
- CHARSET_RE = re.compile("((^|;)\s*charset=)([^;]*)", re.M)
-
- def extractCharsetFromMeta(self, attrs):
- """Beautiful Soup can detect a charset included in a META tag,
- try to convert the document to that charset, and re-parse the
- document from the beginning."""
- httpEquiv = None
- contentType = None
- contentTypeIndex = None
- tagNeedsEncodingSubstitution = False
-
- for i in range(0, len(attrs)):
- key, value = attrs[i]
- key = key.lower()
- if key == 'http-equiv':
- httpEquiv = value
- elif key == 'content':
- contentType = value
- contentTypeIndex = i
-
- if httpEquiv and contentType: # It's an interesting meta tag.
- match = self.CHARSET_RE.search(contentType)
- if match:
- if (self.declaredHTMLEncoding is not None or
- self.originalEncoding == self.fromEncoding):
- # An HTML encoding was sniffed while converting
- # the document to Unicode, or an HTML encoding was
- # sniffed during a previous pass through the
- # document, or an encoding was specified
- # explicitly and it worked. Rewrite the meta tag.
- def rewrite(match):
- return match.group(1) + "%SOUP-ENCODING%"
- newAttr = self.CHARSET_RE.sub(rewrite, contentType)
- attrs[contentTypeIndex] = (attrs[contentTypeIndex][0],
- newAttr)
- tagNeedsEncodingSubstitution = True
- else:
- # This is our first pass through the document.
- # Go through it again with the encoding information.
- newCharset = match.group(3)
- if newCharset and newCharset != self.originalEncoding:
- self.declaredHTMLEncoding = newCharset
- self._feed(self.declaredHTMLEncoding)
- raise StopParsing
- pass
- tag = self.unknown_starttag("meta", attrs)
- if tag and tagNeedsEncodingSubstitution:
- tag.containsSubstitutions = True
-
-
-class StopParsing(Exception):
- pass
-
-class ICantBelieveItsBeautifulSoup(BeautifulSoup):
-
- """The BeautifulSoup class is oriented towards skipping over
- common HTML errors like unclosed tags. However, sometimes it makes
- errors of its own. For instance, consider this fragment:
-
- <b>Foo<b>Bar</b></b>
-
- This is perfectly valid (if bizarre) HTML. However, the
- BeautifulSoup class will implicitly close the first b tag when it
- encounters the second 'b'. It will think the author wrote
- "<b>Foo<b>Bar", and didn't close the first 'b' tag, because
- there's no real-world reason to bold something that's already
- bold. When it encounters '</b></b>' it will close two more 'b'
- tags, for a grand total of three tags closed instead of two. This
- can throw off the rest of your document structure. The same is
- true of a number of other tags, listed below.
-
- It's much more common for someone to forget to close a 'b' tag
- than to actually use nested 'b' tags, and the BeautifulSoup class
- handles the common case. This class handles the not-co-common
- case: where you can't believe someone wrote what they did, but
- it's valid HTML and BeautifulSoup screwed up by assuming it
- wouldn't be."""
-
- I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = \
- ['em', 'big', 'i', 'small', 'tt', 'abbr', 'acronym', 'strong',
- 'cite', 'code', 'dfn', 'kbd', 'samp', 'strong', 'var', 'b',
- 'big']
-
- I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = ['noscript']
-
- NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS,
- I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS,
- I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS)
-
-class MinimalSoup(BeautifulSoup):
- """The MinimalSoup class is for parsing HTML that contains
- pathologically bad markup. It makes no assumptions about tag
- nesting, but it does know which tags are self-closing, that
- <script> tags contain Javascript and should not be parsed, that
- META tags may contain encoding information, and so on.
-
- This also makes it better for subclassing than BeautifulStoneSoup
- or BeautifulSoup."""
-
- RESET_NESTING_TAGS = buildTagMap('noscript')
- NESTABLE_TAGS = {}
-
-class BeautifulSOAP(BeautifulStoneSoup):
- """This class will push a tag with only a single string child into
- the tag's parent as an attribute. The attribute's name is the tag
- name, and the value is the string child. An example should give
- the flavor of the change:
-
- <foo><bar>baz</bar></foo>
- =>
- <foo bar="baz"><bar>baz</bar></foo>
-
- You can then access fooTag['bar'] instead of fooTag.barTag.string.
-
- This is, of course, useful for scraping structures that tend to
- use subelements instead of attributes, such as SOAP messages. Note
- that it modifies its input, so don't print the modified version
- out.
-
- I'm not sure how many people really want to use this class; let me
- know if you do. Mainly I like the name."""
-
- def popTag(self):
- if len(self.tagStack) > 1:
- tag = self.tagStack[-1]
- parent = self.tagStack[-2]
- parent._getAttrMap()
- if (isinstance(tag, Tag) and len(tag.contents) == 1 and
- isinstance(tag.contents[0], NavigableString) and
- not parent.attrMap.has_key(tag.name)):
- parent[tag.name] = tag.contents[0]
- BeautifulStoneSoup.popTag(self)
-
-#Enterprise class names! It has come to our attention that some people
-#think the names of the Beautiful Soup parser classes are too silly
-#and "unprofessional" for use in enterprise screen-scraping. We feel
-#your pain! For such-minded folk, the Beautiful Soup Consortium And
-#All-Night Kosher Bakery recommends renaming this file to
-#"RobustParser.py" (or, in cases of extreme enterprisiness,
-#"RobustParserBeanInterface.class") and using the following
-#enterprise-friendly class aliases:
-class RobustXMLParser(BeautifulStoneSoup):
- pass
-class RobustHTMLParser(BeautifulSoup):
- pass
-class RobustWackAssHTMLParser(ICantBelieveItsBeautifulSoup):
- pass
-class RobustInsanelyWackAssHTMLParser(MinimalSoup):
- pass
-class SimplifyingSOAPParser(BeautifulSOAP):
- pass
-
-######################################################
-#
-# Bonus library: Unicode, Dammit
-#
-# This class forces XML data into a standard format (usually to UTF-8
-# or Unicode). It is heavily based on code from Mark Pilgrim's
-# Universal Feed Parser. It does not rewrite the XML or HTML to
-# reflect a new encoding: that happens in BeautifulStoneSoup.handle_pi
-# (XML) and BeautifulSoup.start_meta (HTML).
-
-# Autodetects character encodings.
-# Download from http://chardet.feedparser.org/
-try:
- import chardet
-# import chardet.constants
-# chardet.constants._debug = 1
-except ImportError:
- chardet = None
-
-# cjkcodecs and iconv_codec make Python know about more character encodings.
-# Both are available from http://cjkpython.i18n.org/
-# They're built in if you use Python 2.4.
-try:
- import cjkcodecs.aliases
-except ImportError:
- pass
-try:
- import iconv_codec
-except ImportError:
- pass
-
-class UnicodeDammit:
- """A class for detecting the encoding of a *ML document and
- converting it to a Unicode string. If the source encoding is
- windows-1252, can replace MS smart quotes with their HTML or XML
- equivalents."""
-
- # This dictionary maps commonly seen values for "charset" in HTML
- # meta tags to the corresponding Python codec names. It only covers
- # values that aren't in Python's aliases and can't be determined
- # by the heuristics in find_codec.
- CHARSET_ALIASES = { "macintosh" : "mac-roman",
- "x-sjis" : "shift-jis" }
-
- def __init__(self, markup, overrideEncodings=[],
- smartQuotesTo='xml', isHTML=False):
- self.declaredHTMLEncoding = None
- self.markup, documentEncoding, sniffedEncoding = \
- self._detectEncoding(markup, isHTML)
- self.smartQuotesTo = smartQuotesTo
- self.triedEncodings = []
- if markup == '' or isinstance(markup, unicode):
- self.originalEncoding = None
- self.unicode = unicode(markup)
- return
-
- u = None
- for proposedEncoding in overrideEncodings:
- u = self._convertFrom(proposedEncoding)
- if u: break
- if not u:
- for proposedEncoding in (documentEncoding, sniffedEncoding):
- u = self._convertFrom(proposedEncoding)
- if u: break
-
- # If no luck and we have auto-detection library, try that:
- if not u and chardet and not isinstance(self.markup, unicode):
- u = self._convertFrom(chardet.detect(self.markup)['encoding'])
-
- # As a last resort, try utf-8 and windows-1252:
- if not u:
- for proposed_encoding in ("utf-8", "windows-1252"):
- u = self._convertFrom(proposed_encoding)
- if u: break
-
- self.unicode = u
- if not u: self.originalEncoding = None
-
- def _subMSChar(self, match):
- """Changes a MS smart quote character to an XML or HTML
- entity."""
- orig = match.group(1)
- sub = self.MS_CHARS.get(orig)
- if type(sub) == types.TupleType:
- if self.smartQuotesTo == 'xml':
- sub = '&#x'.encode() + sub[1].encode() + ';'.encode()
- else:
- sub = '&'.encode() + sub[0].encode() + ';'.encode()
- else:
- sub = sub.encode()
- return sub
-
- def _convertFrom(self, proposed):
- proposed = self.find_codec(proposed)
- if not proposed or proposed in self.triedEncodings:
- return None
- self.triedEncodings.append(proposed)
- markup = self.markup
-
- # Convert smart quotes to HTML if coming from an encoding
- # that might have them.
- if self.smartQuotesTo and proposed.lower() in("windows-1252",
- "iso-8859-1",
- "iso-8859-2"):
- smart_quotes_re = "([\x80-\x9f])"
- smart_quotes_compiled = re.compile(smart_quotes_re)
- markup = smart_quotes_compiled.sub(self._subMSChar, markup)
-
- try:
- # print "Trying to convert document to %s" % proposed
- u = self._toUnicode(markup, proposed)
- self.markup = u
- self.originalEncoding = proposed
- except Exception, e:
- # print "That didn't work!"
- # print e
- return None
- #print "Correct encoding: %s" % proposed
- return self.markup
-
- def _toUnicode(self, data, encoding):
- '''Given a string and its encoding, decodes the string into Unicode.
- %encoding is a string recognized by encodings.aliases'''
-
- # strip Byte Order Mark (if present)
- if (len(data) >= 4) and (data[:2] == '\xfe\xff') \
- and (data[2:4] != '\x00\x00'):
- encoding = 'utf-16be'
- data = data[2:]
- elif (len(data) >= 4) and (data[:2] == '\xff\xfe') \
- and (data[2:4] != '\x00\x00'):
- encoding = 'utf-16le'
- data = data[2:]
- elif data[:3] == '\xef\xbb\xbf':
- encoding = 'utf-8'
- data = data[3:]
- elif data[:4] == '\x00\x00\xfe\xff':
- encoding = 'utf-32be'
- data = data[4:]
- elif data[:4] == '\xff\xfe\x00\x00':
- encoding = 'utf-32le'
- data = data[4:]
- newdata = unicode(data, encoding)
- return newdata
-
- def _detectEncoding(self, xml_data, isHTML=False):
- """Given a document, tries to detect its XML encoding."""
- xml_encoding = sniffed_xml_encoding = None
- try:
- if xml_data[:4] == '\x4c\x6f\xa7\x94':
- # EBCDIC
- xml_data = self._ebcdic_to_ascii(xml_data)
- elif xml_data[:4] == '\x00\x3c\x00\x3f':
- # UTF-16BE
- sniffed_xml_encoding = 'utf-16be'
- xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
- elif (len(xml_data) >= 4) and (xml_data[:2] == '\xfe\xff') \
- and (xml_data[2:4] != '\x00\x00'):
- # UTF-16BE with BOM
- sniffed_xml_encoding = 'utf-16be'
- xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
- elif xml_data[:4] == '\x3c\x00\x3f\x00':
- # UTF-16LE
- sniffed_xml_encoding = 'utf-16le'
- xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
- elif (len(xml_data) >= 4) and (xml_data[:2] == '\xff\xfe') and \
- (xml_data[2:4] != '\x00\x00'):
- # UTF-16LE with BOM
- sniffed_xml_encoding = 'utf-16le'
- xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
- elif xml_data[:4] == '\x00\x00\x00\x3c':
- # UTF-32BE
- sniffed_xml_encoding = 'utf-32be'
- xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
- elif xml_data[:4] == '\x3c\x00\x00\x00':
- # UTF-32LE
- sniffed_xml_encoding = 'utf-32le'
- xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
- elif xml_data[:4] == '\x00\x00\xfe\xff':
- # UTF-32BE with BOM
- sniffed_xml_encoding = 'utf-32be'
- xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
- elif xml_data[:4] == '\xff\xfe\x00\x00':
- # UTF-32LE with BOM
- sniffed_xml_encoding = 'utf-32le'
- xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
- elif xml_data[:3] == '\xef\xbb\xbf':
- # UTF-8 with BOM
- sniffed_xml_encoding = 'utf-8'
- xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
- else:
- sniffed_xml_encoding = 'ascii'
- pass
- except:
- xml_encoding_match = None
- xml_encoding_re = '^<\?.*encoding=[\'"](.*?)[\'"].*\?>'.encode()
- xml_encoding_match = re.compile(xml_encoding_re).match(xml_data)
- if not xml_encoding_match and isHTML:
- meta_re = '<\s*meta[^>]+charset=([^>]*?)[;\'">]'.encode()
- regexp = re.compile(meta_re, re.I)
- xml_encoding_match = regexp.search(xml_data)
- if xml_encoding_match is not None:
- xml_encoding = xml_encoding_match.groups()[0].decode(
- 'ascii').lower()
- if isHTML:
- self.declaredHTMLEncoding = xml_encoding
- if sniffed_xml_encoding and \
- (xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode',
- 'iso-10646-ucs-4', 'ucs-4', 'csucs4',
- 'utf-16', 'utf-32', 'utf_16', 'utf_32',
- 'utf16', 'u16')):
- xml_encoding = sniffed_xml_encoding
- return xml_data, xml_encoding, sniffed_xml_encoding
-
-
- def find_codec(self, charset):
- return self._codec(self.CHARSET_ALIASES.get(charset, charset)) \
- or (charset and self._codec(charset.replace("-", ""))) \
- or (charset and self._codec(charset.replace("-", "_"))) \
- or charset
-
- def _codec(self, charset):
- if not charset: return charset
- codec = None
- try:
- codecs.lookup(charset)
- codec = charset
- except (LookupError, ValueError):
- pass
- return codec
-
- EBCDIC_TO_ASCII_MAP = None
- def _ebcdic_to_ascii(self, s):
- c = self.__class__
- if not c.EBCDIC_TO_ASCII_MAP:
- emap = (0,1,2,3,156,9,134,127,151,141,142,11,12,13,14,15,
- 16,17,18,19,157,133,8,135,24,25,146,143,28,29,30,31,
- 128,129,130,131,132,10,23,27,136,137,138,139,140,5,6,7,
- 144,145,22,147,148,149,150,4,152,153,154,155,20,21,158,26,
- 32,160,161,162,163,164,165,166,167,168,91,46,60,40,43,33,
- 38,169,170,171,172,173,174,175,176,177,93,36,42,41,59,94,
- 45,47,178,179,180,181,182,183,184,185,124,44,37,95,62,63,
- 186,187,188,189,190,191,192,193,194,96,58,35,64,39,61,34,
- 195,97,98,99,100,101,102,103,104,105,196,197,198,199,200,
- 201,202,106,107,108,109,110,111,112,113,114,203,204,205,
- 206,207,208,209,126,115,116,117,118,119,120,121,122,210,
- 211,212,213,214,215,216,217,218,219,220,221,222,223,224,
- 225,226,227,228,229,230,231,123,65,66,67,68,69,70,71,72,
- 73,232,233,234,235,236,237,125,74,75,76,77,78,79,80,81,
- 82,238,239,240,241,242,243,92,159,83,84,85,86,87,88,89,
- 90,244,245,246,247,248,249,48,49,50,51,52,53,54,55,56,57,
- 250,251,252,253,254,255)
- import string
- c.EBCDIC_TO_ASCII_MAP = string.maketrans( \
- ''.join(map(chr, range(256))), ''.join(map(chr, emap)))
- return s.translate(c.EBCDIC_TO_ASCII_MAP)
-
- MS_CHARS = { '\x80' : ('euro', '20AC'),
- '\x81' : ' ',
- '\x82' : ('sbquo', '201A'),
- '\x83' : ('fnof', '192'),
- '\x84' : ('bdquo', '201E'),
- '\x85' : ('hellip', '2026'),
- '\x86' : ('dagger', '2020'),
- '\x87' : ('Dagger', '2021'),
- '\x88' : ('circ', '2C6'),
- '\x89' : ('permil', '2030'),
- '\x8A' : ('Scaron', '160'),
- '\x8B' : ('lsaquo', '2039'),
- '\x8C' : ('OElig', '152'),
- '\x8D' : '?',
- '\x8E' : ('#x17D', '17D'),
- '\x8F' : '?',
- '\x90' : '?',
- '\x91' : ('lsquo', '2018'),
- '\x92' : ('rsquo', '2019'),
- '\x93' : ('ldquo', '201C'),
- '\x94' : ('rdquo', '201D'),
- '\x95' : ('bull', '2022'),
- '\x96' : ('ndash', '2013'),
- '\x97' : ('mdash', '2014'),
- '\x98' : ('tilde', '2DC'),
- '\x99' : ('trade', '2122'),
- '\x9a' : ('scaron', '161'),
- '\x9b' : ('rsaquo', '203A'),
- '\x9c' : ('oelig', '153'),
- '\x9d' : '?',
- '\x9e' : ('#x17E', '17E'),
- '\x9f' : ('Yuml', ''),}
-
-#######################################################################
-
-
-#By default, act as an HTML pretty-printer.
-if __name__ == '__main__':
- import sys
- soup = BeautifulSoup(sys.stdin)
- print soup.prettify()
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/__init__.py b/WebKitTools/Scripts/webkitpy/thirdparty/__init__.py
deleted file mode 100644
index c2249c2..0000000
--- a/WebKitTools/Scripts/webkitpy/thirdparty/__init__.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# 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.
-
-# This module is required for Python to treat this directory as a package.
-
-"""Autoinstalls third-party code required by WebKit."""
-
-from __future__ import with_statement
-
-import codecs
-import os
-
-from webkitpy.common.system.autoinstall import AutoInstaller
-
-# Putting the autoinstall code into webkitpy/thirdparty/__init__.py
-# ensures that no autoinstalling occurs until a caller imports from
-# webkitpy.thirdparty. This is useful if the caller wants to configure
-# logging prior to executing autoinstall code.
-
-# FIXME: Ideally, a package should be autoinstalled only if the caller
-# attempts to import from that individual package. This would
-# make autoinstalling lazier than it is currently. This can
-# perhaps be done using Python's import hooks as the original
-# autoinstall implementation did.
-
-# FIXME: If any of these servers is offline, webkit-patch breaks (and maybe
-# other scripts do, too). See <http://webkit.org/b/42080>.
-
-# We put auto-installed third-party modules in this directory--
-#
-# webkitpy/thirdparty/autoinstalled
-thirdparty_dir = os.path.dirname(__file__)
-autoinstalled_dir = os.path.join(thirdparty_dir, "autoinstalled")
-
-# We need to download ClientForm since the mechanize package that we download
-# below requires it. The mechanize package uses ClientForm, for example,
-# in _html.py. Since mechanize imports ClientForm in the following way,
-#
-# > import sgmllib, ClientForm
-#
-# the search path needs to include ClientForm. We put ClientForm in
-# its own directory so that we can include it in the search path without
-# including other modules as a side effect.
-clientform_dir = os.path.join(autoinstalled_dir, "clientform")
-installer = AutoInstaller(append_to_search_path=True,
- target_dir=clientform_dir)
-installer.install(url="http://pypi.python.org/packages/source/C/ClientForm/ClientForm-0.2.10.zip",
- url_subpath="ClientForm.py")
-
-# The remaining packages do not need to be in the search path, so we create
-# a new AutoInstaller instance that does not append to the search path.
-installer = AutoInstaller(target_dir=autoinstalled_dir)
-
-installer.install(url="http://pypi.python.org/packages/source/m/mechanize/mechanize-0.1.11.zip",
- url_subpath="mechanize")
-installer.install(url="http://pypi.python.org/packages/source/p/pep8/pep8-0.5.0.tar.gz#md5=512a818af9979290cd619cce8e9c2e2b",
- url_subpath="pep8-0.5.0/pep8.py")
-installer.install(url="http://www.adambarth.com/webkit/eliza",
- target_name="eliza.py")
-
-# Since irclib and ircbot are two top-level packages, we need to import
-# them separately. We group them into an irc package for better
-# organization purposes.
-irc_dir = os.path.join(autoinstalled_dir, "irc")
-installer = AutoInstaller(target_dir=irc_dir)
-installer.install(url="http://downloads.sourceforge.net/project/python-irclib/python-irclib/0.4.8/python-irclib-0.4.8.zip", url_subpath="irclib.py")
-installer.install(url="http://downloads.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.2.tar.gz",
- url_subpath="pywebsocket-0.5.2/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:
- file.write("This directory is auto-generated by WebKit and is "
- "safe to delete.\nIt contains needed third-party Python "
- "packages automatically downloaded from the web.")
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/mock.py b/WebKitTools/Scripts/webkitpy/thirdparty/mock.py
deleted file mode 100644
index 015c19e..0000000
--- a/WebKitTools/Scripts/webkitpy/thirdparty/mock.py
+++ /dev/null
@@ -1,309 +0,0 @@
-# mock.py
-# Test tools for mocking and patching.
-# Copyright (C) 2007-2009 Michael Foord
-# E-mail: fuzzyman AT voidspace DOT org DOT uk
-
-# mock 0.6.0
-# http://www.voidspace.org.uk/python/mock/
-
-# Released subject to the BSD License
-# Please see http://www.voidspace.org.uk/python/license.shtml
-
-# 2009-11-25: Licence downloaded from above URL.
-# BEGIN DOWNLOADED LICENSE
-#
-# Copyright (c) 2003-2009, Michael Foord
-# All rights reserved.
-# E-mail : fuzzyman AT voidspace DOT org DOT uk
-#
-# 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 Michael Foord nor the name of Voidspace
-# 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.
-#
-# END DOWNLOADED LICENSE
-
-# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
-# Comments, suggestions and bug reports welcome.
-
-
-__all__ = (
- 'Mock',
- 'patch',
- 'patch_object',
- 'sentinel',
- 'DEFAULT'
-)
-
-__version__ = '0.6.0'
-
-class SentinelObject(object):
- def __init__(self, name):
- self.name = name
-
- def __repr__(self):
- return '<SentinelObject "%s">' % self.name
-
-
-class Sentinel(object):
- def __init__(self):
- self._sentinels = {}
-
- def __getattr__(self, name):
- return self._sentinels.setdefault(name, SentinelObject(name))
-
-
-sentinel = Sentinel()
-
-DEFAULT = sentinel.DEFAULT
-
-class OldStyleClass:
- pass
-ClassType = type(OldStyleClass)
-
-def _is_magic(name):
- return '__%s__' % name[2:-2] == name
-
-def _copy(value):
- if type(value) in (dict, list, tuple, set):
- return type(value)(value)
- return value
-
-
-class Mock(object):
-
- def __init__(self, spec=None, side_effect=None, return_value=DEFAULT,
- name=None, parent=None, wraps=None):
- self._parent = parent
- self._name = name
- if spec is not None and not isinstance(spec, list):
- spec = [member for member in dir(spec) if not _is_magic(member)]
-
- self._methods = spec
- self._children = {}
- self._return_value = return_value
- self.side_effect = side_effect
- self._wraps = wraps
-
- self.reset_mock()
-
-
- def reset_mock(self):
- self.called = False
- self.call_args = None
- self.call_count = 0
- self.call_args_list = []
- self.method_calls = []
- for child in self._children.itervalues():
- child.reset_mock()
- if isinstance(self._return_value, Mock):
- self._return_value.reset_mock()
-
-
- def __get_return_value(self):
- if self._return_value is DEFAULT:
- self._return_value = Mock()
- return self._return_value
-
- def __set_return_value(self, value):
- self._return_value = value
-
- return_value = property(__get_return_value, __set_return_value)
-
-
- def __call__(self, *args, **kwargs):
- self.called = True
- self.call_count += 1
- self.call_args = (args, kwargs)
- self.call_args_list.append((args, kwargs))
-
- parent = self._parent
- name = self._name
- while parent is not None:
- parent.method_calls.append((name, args, kwargs))
- if parent._parent is None:
- break
- name = parent._name + '.' + name
- parent = parent._parent
-
- ret_val = DEFAULT
- if self.side_effect is not None:
- if (isinstance(self.side_effect, Exception) or
- isinstance(self.side_effect, (type, ClassType)) and
- issubclass(self.side_effect, Exception)):
- raise self.side_effect
-
- ret_val = self.side_effect(*args, **kwargs)
- if ret_val is DEFAULT:
- ret_val = self.return_value
-
- if self._wraps is not None and self._return_value is DEFAULT:
- return self._wraps(*args, **kwargs)
- if ret_val is DEFAULT:
- ret_val = self.return_value
- return ret_val
-
-
- def __getattr__(self, name):
- if self._methods is not None:
- if name not in self._methods:
- raise AttributeError("Mock object has no attribute '%s'" % name)
- elif _is_magic(name):
- raise AttributeError(name)
-
- if name not in self._children:
- wraps = None
- if self._wraps is not None:
- wraps = getattr(self._wraps, name)
- self._children[name] = Mock(parent=self, name=name, wraps=wraps)
-
- return self._children[name]
-
-
- def assert_called_with(self, *args, **kwargs):
- assert self.call_args == (args, kwargs), 'Expected: %s\nCalled with: %s' % ((args, kwargs), self.call_args)
-
-
-def _dot_lookup(thing, comp, import_path):
- try:
- return getattr(thing, comp)
- except AttributeError:
- __import__(import_path)
- return getattr(thing, comp)
-
-
-def _importer(target):
- components = target.split('.')
- import_path = components.pop(0)
- thing = __import__(import_path)
-
- for comp in components:
- import_path += ".%s" % comp
- thing = _dot_lookup(thing, comp, import_path)
- return thing
-
-
-class _patch(object):
- def __init__(self, target, attribute, new, spec, create):
- self.target = target
- self.attribute = attribute
- self.new = new
- self.spec = spec
- self.create = create
- self.has_local = False
-
-
- def __call__(self, func):
- if hasattr(func, 'patchings'):
- func.patchings.append(self)
- return func
-
- def patched(*args, **keywargs):
- # don't use a with here (backwards compatability with 2.5)
- extra_args = []
- for patching in patched.patchings:
- arg = patching.__enter__()
- if patching.new is DEFAULT:
- extra_args.append(arg)
- args += tuple(extra_args)
- try:
- return func(*args, **keywargs)
- finally:
- for patching in getattr(patched, 'patchings', []):
- patching.__exit__()
-
- patched.patchings = [self]
- patched.__name__ = func.__name__
- patched.compat_co_firstlineno = getattr(func, "compat_co_firstlineno",
- func.func_code.co_firstlineno)
- return patched
-
-
- def get_original(self):
- target = self.target
- name = self.attribute
- create = self.create
-
- original = DEFAULT
- if _has_local_attr(target, name):
- try:
- original = target.__dict__[name]
- except AttributeError:
- # for instances of classes with slots, they have no __dict__
- original = getattr(target, name)
- elif not create and not hasattr(target, name):
- raise AttributeError("%s does not have the attribute %r" % (target, name))
- return original
-
-
- def __enter__(self):
- new, spec, = self.new, self.spec
- original = self.get_original()
- if new is DEFAULT:
- # XXXX what if original is DEFAULT - shouldn't use it as a spec
- inherit = False
- if spec == True:
- # set spec to the object we are replacing
- spec = original
- if isinstance(spec, (type, ClassType)):
- inherit = True
- new = Mock(spec=spec)
- if inherit:
- new.return_value = Mock(spec=spec)
- self.temp_original = original
- setattr(self.target, self.attribute, new)
- return new
-
-
- def __exit__(self, *_):
- if self.temp_original is not DEFAULT:
- setattr(self.target, self.attribute, self.temp_original)
- else:
- delattr(self.target, self.attribute)
- del self.temp_original
-
-
-def patch_object(target, attribute, new=DEFAULT, spec=None, create=False):
- return _patch(target, attribute, new, spec, create)
-
-
-def patch(target, new=DEFAULT, spec=None, create=False):
- try:
- target, attribute = target.rsplit('.', 1)
- except (TypeError, ValueError):
- raise TypeError("Need a valid target to patch. You supplied: %r" % (target,))
- target = _importer(target)
- return _patch(target, attribute, new, spec, create)
-
-
-
-def _has_local_attr(obj, name):
- try:
- return name in vars(obj)
- except TypeError:
- # objects without a __dict__
- return hasattr(obj, name)
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/LICENSE.txt b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/LICENSE.txt
deleted file mode 100644
index ad95f29..0000000
--- a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/LICENSE.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) 2006 Bob Ippolito
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/README.txt b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/README.txt
deleted file mode 100644
index 7f726ce..0000000
--- a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/README.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-URL: http://undefined.org/python/#simplejson
-Version: 1.7.3
-License: MIT
-License File: LICENSE.txt
-
-Description:
-simplejson is a JSON encoder and decoder for Python.
-
-
-Local Modifications:
-Removed unit tests from current distribution.
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/__init__.py b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/__init__.py
deleted file mode 100644
index 38d6229..0000000
--- a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/__init__.py
+++ /dev/null
@@ -1,287 +0,0 @@
-r"""
-A simple, fast, extensible JSON encoder and decoder
-
-JSON (JavaScript Object Notation) <http://json.org> is a subset of
-JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data
-interchange format.
-
-simplejson exposes an API familiar to uses of the standard library
-marshal and pickle modules.
-
-Encoding basic Python object hierarchies::
-
- >>> import simplejson
- >>> simplejson.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
- '["foo", {"bar": ["baz", null, 1.0, 2]}]'
- >>> print simplejson.dumps("\"foo\bar")
- "\"foo\bar"
- >>> print simplejson.dumps(u'\u1234')
- "\u1234"
- >>> print simplejson.dumps('\\')
- "\\"
- >>> print simplejson.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)
- {"a": 0, "b": 0, "c": 0}
- >>> from StringIO import StringIO
- >>> io = StringIO()
- >>> simplejson.dump(['streaming API'], io)
- >>> io.getvalue()
- '["streaming API"]'
-
-Compact encoding::
-
- >>> import simplejson
- >>> simplejson.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
- '[1,2,3,{"4":5,"6":7}]'
-
-Pretty printing::
-
- >>> import simplejson
- >>> print simplejson.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
- {
- "4": 5,
- "6": 7
- }
-
-Decoding JSON::
-
- >>> import simplejson
- >>> simplejson.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')
- [u'foo', {u'bar': [u'baz', None, 1.0, 2]}]
- >>> simplejson.loads('"\\"foo\\bar"')
- u'"foo\x08ar'
- >>> from StringIO import StringIO
- >>> io = StringIO('["streaming API"]')
- >>> simplejson.load(io)
- [u'streaming API']
-
-Specializing JSON object decoding::
-
- >>> import simplejson
- >>> def as_complex(dct):
- ... if '__complex__' in dct:
- ... return complex(dct['real'], dct['imag'])
- ... return dct
- ...
- >>> simplejson.loads('{"__complex__": true, "real": 1, "imag": 2}',
- ... object_hook=as_complex)
- (1+2j)
-
-Extending JSONEncoder::
-
- >>> import simplejson
- >>> class ComplexEncoder(simplejson.JSONEncoder):
- ... def default(self, obj):
- ... if isinstance(obj, complex):
- ... return [obj.real, obj.imag]
- ... return simplejson.JSONEncoder.default(self, obj)
- ...
- >>> dumps(2 + 1j, cls=ComplexEncoder)
- '[2.0, 1.0]'
- >>> ComplexEncoder().encode(2 + 1j)
- '[2.0, 1.0]'
- >>> list(ComplexEncoder().iterencode(2 + 1j))
- ['[', '2.0', ', ', '1.0', ']']
-
-
-Note that the JSON produced by this module's default settings
-is a subset of YAML, so it may be used as a serializer for that as well.
-"""
-__version__ = '1.7.3'
-__all__ = [
- 'dump', 'dumps', 'load', 'loads',
- 'JSONDecoder', 'JSONEncoder',
-]
-
-from decoder import JSONDecoder
-from encoder import JSONEncoder
-
-_default_encoder = JSONEncoder(
- skipkeys=False,
- ensure_ascii=True,
- check_circular=True,
- allow_nan=True,
- indent=None,
- separators=None,
- encoding='utf-8'
-)
-
-def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
- allow_nan=True, cls=None, indent=None, separators=None,
- encoding='utf-8', **kw):
- """
- Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
- ``.write()``-supporting file-like object).
-
- If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types
- (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
- will be skipped instead of raising a ``TypeError``.
-
- If ``ensure_ascii`` is ``False``, then the some chunks written to ``fp``
- may be ``unicode`` instances, subject to normal Python ``str`` to
- ``unicode`` coercion rules. Unless ``fp.write()`` explicitly
- understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
- to cause an error.
-
- If ``check_circular`` is ``False``, then the circular reference check
- for container types will be skipped and a circular reference will
- result in an ``OverflowError`` (or worse).
-
- If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to
- serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
- in strict compliance of the JSON specification, instead of using the
- JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
-
- If ``indent`` is a non-negative integer, then JSON array elements and object
- members will be pretty-printed with that indent level. An indent level
- of 0 will only insert newlines. ``None`` is the most compact representation.
-
- If ``separators`` is an ``(item_separator, dict_separator)`` tuple
- then it will be used instead of the default ``(', ', ': ')`` separators.
- ``(',', ':')`` is the most compact JSON representation.
-
- ``encoding`` is the character encoding for str instances, default is UTF-8.
-
- To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
- ``.default()`` method to serialize additional types), specify it with
- the ``cls`` kwarg.
- """
- # cached encoder
- if (skipkeys is False and ensure_ascii is True and
- check_circular is True and allow_nan is True and
- cls is None and indent is None and separators is None and
- encoding == 'utf-8' and not kw):
- iterable = _default_encoder.iterencode(obj)
- else:
- if cls is None:
- cls = JSONEncoder
- iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
- check_circular=check_circular, allow_nan=allow_nan, indent=indent,
- separators=separators, encoding=encoding, **kw).iterencode(obj)
- # could accelerate with writelines in some versions of Python, at
- # a debuggability cost
- for chunk in iterable:
- fp.write(chunk)
-
-
-def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
- allow_nan=True, cls=None, indent=None, separators=None,
- encoding='utf-8', **kw):
- """
- Serialize ``obj`` to a JSON formatted ``str``.
-
- If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types
- (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
- will be skipped instead of raising a ``TypeError``.
-
- If ``ensure_ascii`` is ``False``, then the return value will be a
- ``unicode`` instance subject to normal Python ``str`` to ``unicode``
- coercion rules instead of being escaped to an ASCII ``str``.
-
- If ``check_circular`` is ``False``, then the circular reference check
- for container types will be skipped and a circular reference will
- result in an ``OverflowError`` (or worse).
-
- If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to
- serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
- strict compliance of the JSON specification, instead of using the
- JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
-
- If ``indent`` is a non-negative integer, then JSON array elements and
- object members will be pretty-printed with that indent level. An indent
- level of 0 will only insert newlines. ``None`` is the most compact
- representation.
-
- If ``separators`` is an ``(item_separator, dict_separator)`` tuple
- then it will be used instead of the default ``(', ', ': ')`` separators.
- ``(',', ':')`` is the most compact JSON representation.
-
- ``encoding`` is the character encoding for str instances, default is UTF-8.
-
- To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
- ``.default()`` method to serialize additional types), specify it with
- the ``cls`` kwarg.
- """
- # cached encoder
- if (skipkeys is False and ensure_ascii is True and
- check_circular is True and allow_nan is True and
- cls is None and indent is None and separators is None and
- encoding == 'utf-8' and not kw):
- return _default_encoder.encode(obj)
- if cls is None:
- cls = JSONEncoder
- return cls(
- skipkeys=skipkeys, ensure_ascii=ensure_ascii,
- check_circular=check_circular, allow_nan=allow_nan, indent=indent,
- separators=separators, encoding=encoding,
- **kw).encode(obj)
-
-_default_decoder = JSONDecoder(encoding=None, object_hook=None)
-
-def load(fp, encoding=None, cls=None, object_hook=None, **kw):
- """
- Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
- a JSON document) to a Python object.
-
- If the contents of ``fp`` is encoded with an ASCII based encoding other
- than utf-8 (e.g. latin-1), then an appropriate ``encoding`` name must
- be specified. Encodings that are not ASCII based (such as UCS-2) are
- not allowed, and should be wrapped with
- ``codecs.getreader(fp)(encoding)``, or simply decoded to a ``unicode``
- object and passed to ``loads()``
-
- ``object_hook`` is an optional function that will be called with the
- result of any object literal decode (a ``dict``). The return value of
- ``object_hook`` will be used instead of the ``dict``. This feature
- can be used to implement custom decoders (e.g. JSON-RPC class hinting).
-
- To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
- kwarg.
- """
- return loads(fp.read(),
- encoding=encoding, cls=cls, object_hook=object_hook, **kw)
-
-def loads(s, encoding=None, cls=None, object_hook=None, **kw):
- """
- Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
- document) to a Python object.
-
- If ``s`` is a ``str`` instance and is encoded with an ASCII based encoding
- other than utf-8 (e.g. latin-1) then an appropriate ``encoding`` name
- must be specified. Encodings that are not ASCII based (such as UCS-2)
- are not allowed and should be decoded to ``unicode`` first.
-
- ``object_hook`` is an optional function that will be called with the
- result of any object literal decode (a ``dict``). The return value of
- ``object_hook`` will be used instead of the ``dict``. This feature
- can be used to implement custom decoders (e.g. JSON-RPC class hinting).
-
- To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
- kwarg.
- """
- if cls is None and encoding is None and object_hook is None and not kw:
- return _default_decoder.decode(s)
- if cls is None:
- cls = JSONDecoder
- if object_hook is not None:
- kw['object_hook'] = object_hook
- return cls(encoding=encoding, **kw).decode(s)
-
-def read(s):
- """
- json-py API compatibility hook. Use loads(s) instead.
- """
- import warnings
- warnings.warn("simplejson.loads(s) should be used instead of read(s)",
- DeprecationWarning)
- return loads(s)
-
-def write(obj):
- """
- json-py API compatibility hook. Use dumps(s) instead.
- """
- import warnings
- warnings.warn("simplejson.dumps(s) should be used instead of write(s)",
- DeprecationWarning)
- return dumps(obj)
-
-
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/_speedups.c b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/_speedups.c
deleted file mode 100644
index 8f290bb..0000000
--- a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/_speedups.c
+++ /dev/null
@@ -1,215 +0,0 @@
-#include "Python.h"
-#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
-typedef int Py_ssize_t;
-#define PY_SSIZE_T_MAX INT_MAX
-#define PY_SSIZE_T_MIN INT_MIN
-#endif
-
-static Py_ssize_t
-ascii_escape_char(Py_UNICODE c, char *output, Py_ssize_t chars);
-static PyObject *
-ascii_escape_unicode(PyObject *pystr);
-static PyObject *
-ascii_escape_str(PyObject *pystr);
-static PyObject *
-py_encode_basestring_ascii(PyObject* self __attribute__((__unused__)), PyObject *pystr);
-void init_speedups(void);
-
-#define S_CHAR(c) (c >= ' ' && c <= '~' && c != '\\' && c != '/' && c != '"')
-
-#define MIN_EXPANSION 6
-#ifdef Py_UNICODE_WIDE
-#define MAX_EXPANSION (2 * MIN_EXPANSION)
-#else
-#define MAX_EXPANSION MIN_EXPANSION
-#endif
-
-static Py_ssize_t
-ascii_escape_char(Py_UNICODE c, char *output, Py_ssize_t chars) {
- Py_UNICODE x;
- output[chars++] = '\\';
- switch (c) {
- case '/': output[chars++] = (char)c; break;
- case '\\': output[chars++] = (char)c; break;
- case '"': output[chars++] = (char)c; break;
- case '\b': output[chars++] = 'b'; break;
- case '\f': output[chars++] = 'f'; break;
- case '\n': output[chars++] = 'n'; break;
- case '\r': output[chars++] = 'r'; break;
- case '\t': output[chars++] = 't'; break;
- default:
-#ifdef Py_UNICODE_WIDE
- if (c >= 0x10000) {
- /* UTF-16 surrogate pair */
- Py_UNICODE v = c - 0x10000;
- c = 0xd800 | ((v >> 10) & 0x3ff);
- output[chars++] = 'u';
- x = (c & 0xf000) >> 12;
- output[chars++] = (x < 10) ? '0' + x : 'a' + (x - 10);
- x = (c & 0x0f00) >> 8;
- output[chars++] = (x < 10) ? '0' + x : 'a' + (x - 10);
- x = (c & 0x00f0) >> 4;
- output[chars++] = (x < 10) ? '0' + x : 'a' + (x - 10);
- x = (c & 0x000f);
- output[chars++] = (x < 10) ? '0' + x : 'a' + (x - 10);
- c = 0xdc00 | (v & 0x3ff);
- output[chars++] = '\\';
- }
-#endif
- output[chars++] = 'u';
- x = (c & 0xf000) >> 12;
- output[chars++] = (x < 10) ? '0' + x : 'a' + (x - 10);
- x = (c & 0x0f00) >> 8;
- output[chars++] = (x < 10) ? '0' + x : 'a' + (x - 10);
- x = (c & 0x00f0) >> 4;
- output[chars++] = (x < 10) ? '0' + x : 'a' + (x - 10);
- x = (c & 0x000f);
- output[chars++] = (x < 10) ? '0' + x : 'a' + (x - 10);
- }
- return chars;
-}
-
-static PyObject *
-ascii_escape_unicode(PyObject *pystr) {
- Py_ssize_t i;
- Py_ssize_t input_chars;
- Py_ssize_t output_size;
- Py_ssize_t chars;
- PyObject *rval;
- char *output;
- Py_UNICODE *input_unicode;
-
- input_chars = PyUnicode_GET_SIZE(pystr);
- input_unicode = PyUnicode_AS_UNICODE(pystr);
- /* One char input can be up to 6 chars output, estimate 4 of these */
- output_size = 2 + (MIN_EXPANSION * 4) + input_chars;
- rval = PyString_FromStringAndSize(NULL, output_size);
- if (rval == NULL) {
- return NULL;
- }
- output = PyString_AS_STRING(rval);
- chars = 0;
- output[chars++] = '"';
- for (i = 0; i < input_chars; i++) {
- Py_UNICODE c = input_unicode[i];
- if (S_CHAR(c)) {
- output[chars++] = (char)c;
- } else {
- chars = ascii_escape_char(c, output, chars);
- }
- if (output_size - chars < (1 + MAX_EXPANSION)) {
- /* There's more than four, so let's resize by a lot */
- output_size *= 2;
- /* This is an upper bound */
- if (output_size > 2 + (input_chars * MAX_EXPANSION)) {
- output_size = 2 + (input_chars * MAX_EXPANSION);
- }
- if (_PyString_Resize(&rval, output_size) == -1) {
- return NULL;
- }
- output = PyString_AS_STRING(rval);
- }
- }
- output[chars++] = '"';
- if (_PyString_Resize(&rval, chars) == -1) {
- return NULL;
- }
- return rval;
-}
-
-static PyObject *
-ascii_escape_str(PyObject *pystr) {
- Py_ssize_t i;
- Py_ssize_t input_chars;
- Py_ssize_t output_size;
- Py_ssize_t chars;
- PyObject *rval;
- char *output;
- char *input_str;
-
- input_chars = PyString_GET_SIZE(pystr);
- input_str = PyString_AS_STRING(pystr);
- /* One char input can be up to 6 chars output, estimate 4 of these */
- output_size = 2 + (MIN_EXPANSION * 4) + input_chars;
- rval = PyString_FromStringAndSize(NULL, output_size);
- if (rval == NULL) {
- return NULL;
- }
- output = PyString_AS_STRING(rval);
- chars = 0;
- output[chars++] = '"';
- for (i = 0; i < input_chars; i++) {
- Py_UNICODE c = (Py_UNICODE)input_str[i];
- if (S_CHAR(c)) {
- output[chars++] = (char)c;
- } else if (c > 0x7F) {
- /* We hit a non-ASCII character, bail to unicode mode */
- PyObject *uni;
- Py_DECREF(rval);
- uni = PyUnicode_DecodeUTF8(input_str, input_chars, "strict");
- if (uni == NULL) {
- return NULL;
- }
- rval = ascii_escape_unicode(uni);
- Py_DECREF(uni);
- return rval;
- } else {
- chars = ascii_escape_char(c, output, chars);
- }
- /* An ASCII char can't possibly expand to a surrogate! */
- if (output_size - chars < (1 + MIN_EXPANSION)) {
- /* There's more than four, so let's resize by a lot */
- output_size *= 2;
- if (output_size > 2 + (input_chars * MIN_EXPANSION)) {
- output_size = 2 + (input_chars * MIN_EXPANSION);
- }
- if (_PyString_Resize(&rval, output_size) == -1) {
- return NULL;
- }
- output = PyString_AS_STRING(rval);
- }
- }
- output[chars++] = '"';
- if (_PyString_Resize(&rval, chars) == -1) {
- return NULL;
- }
- return rval;
-}
-
-PyDoc_STRVAR(pydoc_encode_basestring_ascii,
- "encode_basestring_ascii(basestring) -> str\n"
- "\n"
- "..."
-);
-
-static PyObject *
-py_encode_basestring_ascii(PyObject* self __attribute__((__unused__)), PyObject *pystr) {
- /* METH_O */
- if (PyString_Check(pystr)) {
- return ascii_escape_str(pystr);
- } else if (PyUnicode_Check(pystr)) {
- return ascii_escape_unicode(pystr);
- }
- PyErr_SetString(PyExc_TypeError, "first argument must be a string");
- return NULL;
-}
-
-#define DEFN(n, k) \
- { \
- #n, \
- (PyCFunction)py_ ##n, \
- k, \
- pydoc_ ##n \
- }
-static PyMethodDef speedups_methods[] = {
- DEFN(encode_basestring_ascii, METH_O),
- {}
-};
-#undef DEFN
-
-void
-init_speedups(void)
-{
- PyObject *m;
- m = Py_InitModule4("_speedups", speedups_methods, NULL, NULL, PYTHON_API_VERSION);
-}
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/decoder.py b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/decoder.py
deleted file mode 100644
index 63f70cb..0000000
--- a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/decoder.py
+++ /dev/null
@@ -1,273 +0,0 @@
-"""
-Implementation of JSONDecoder
-"""
-import re
-
-from scanner import Scanner, pattern
-
-FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
-
-def _floatconstants():
- import struct
- import sys
- _BYTES = '7FF80000000000007FF0000000000000'.decode('hex')
- if sys.byteorder != 'big':
- _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
- nan, inf = struct.unpack('dd', _BYTES)
- return nan, inf, -inf
-
-NaN, PosInf, NegInf = _floatconstants()
-
-def linecol(doc, pos):
- lineno = doc.count('\n', 0, pos) + 1
- if lineno == 1:
- colno = pos
- else:
- colno = pos - doc.rindex('\n', 0, pos)
- return lineno, colno
-
-def errmsg(msg, doc, pos, end=None):
- lineno, colno = linecol(doc, pos)
- if end is None:
- return '%s: line %d column %d (char %d)' % (msg, lineno, colno, pos)
- endlineno, endcolno = linecol(doc, end)
- return '%s: line %d column %d - line %d column %d (char %d - %d)' % (
- msg, lineno, colno, endlineno, endcolno, pos, end)
-
-_CONSTANTS = {
- '-Infinity': NegInf,
- 'Infinity': PosInf,
- 'NaN': NaN,
- 'true': True,
- 'false': False,
- 'null': None,
-}
-
-def JSONConstant(match, context, c=_CONSTANTS):
- return c[match.group(0)], None
-pattern('(-?Infinity|NaN|true|false|null)')(JSONConstant)
-
-def JSONNumber(match, context):
- match = JSONNumber.regex.match(match.string, *match.span())
- integer, frac, exp = match.groups()
- if frac or exp:
- res = float(integer + (frac or '') + (exp or ''))
- else:
- res = int(integer)
- return res, None
-pattern(r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?')(JSONNumber)
-
-STRINGCHUNK = re.compile(r'(.*?)(["\\])', FLAGS)
-BACKSLASH = {
- '"': u'"', '\\': u'\\', '/': u'/',
- 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
-}
-
-DEFAULT_ENCODING = "utf-8"
-
-def scanstring(s, end, encoding=None, _b=BACKSLASH, _m=STRINGCHUNK.match):
- if encoding is None:
- encoding = DEFAULT_ENCODING
- chunks = []
- _append = chunks.append
- begin = end - 1
- while 1:
- chunk = _m(s, end)
- if chunk is None:
- raise ValueError(
- errmsg("Unterminated string starting at", s, begin))
- end = chunk.end()
- content, terminator = chunk.groups()
- if content:
- if not isinstance(content, unicode):
- content = unicode(content, encoding)
- _append(content)
- if terminator == '"':
- break
- try:
- esc = s[end]
- except IndexError:
- raise ValueError(
- errmsg("Unterminated string starting at", s, begin))
- if esc != 'u':
- try:
- m = _b[esc]
- except KeyError:
- raise ValueError(
- errmsg("Invalid \\escape: %r" % (esc,), s, end))
- end += 1
- else:
- esc = s[end + 1:end + 5]
- try:
- m = unichr(int(esc, 16))
- if len(esc) != 4 or not esc.isalnum():
- raise ValueError
- except ValueError:
- raise ValueError(errmsg("Invalid \\uXXXX escape", s, end))
- end += 5
- _append(m)
- return u''.join(chunks), end
-
-def JSONString(match, context):
- encoding = getattr(context, 'encoding', None)
- return scanstring(match.string, match.end(), encoding)
-pattern(r'"')(JSONString)
-
-WHITESPACE = re.compile(r'\s*', FLAGS)
-
-def JSONObject(match, context, _w=WHITESPACE.match):
- pairs = {}
- s = match.string
- end = _w(s, match.end()).end()
- nextchar = s[end:end + 1]
- # trivial empty object
- if nextchar == '}':
- return pairs, end + 1
- if nextchar != '"':
- raise ValueError(errmsg("Expecting property name", s, end))
- end += 1
- encoding = getattr(context, 'encoding', None)
- iterscan = JSONScanner.iterscan
- while True:
- key, end = scanstring(s, end, encoding)
- end = _w(s, end).end()
- if s[end:end + 1] != ':':
- raise ValueError(errmsg("Expecting : delimiter", s, end))
- end = _w(s, end + 1).end()
- try:
- value, end = iterscan(s, idx=end, context=context).next()
- except StopIteration:
- raise ValueError(errmsg("Expecting object", s, end))
- pairs[key] = value
- end = _w(s, end).end()
- nextchar = s[end:end + 1]
- end += 1
- if nextchar == '}':
- break
- if nextchar != ',':
- raise ValueError(errmsg("Expecting , delimiter", s, end - 1))
- end = _w(s, end).end()
- nextchar = s[end:end + 1]
- end += 1
- if nextchar != '"':
- raise ValueError(errmsg("Expecting property name", s, end - 1))
- object_hook = getattr(context, 'object_hook', None)
- if object_hook is not None:
- pairs = object_hook(pairs)
- return pairs, end
-pattern(r'{')(JSONObject)
-
-def JSONArray(match, context, _w=WHITESPACE.match):
- values = []
- s = match.string
- end = _w(s, match.end()).end()
- # look-ahead for trivial empty array
- nextchar = s[end:end + 1]
- if nextchar == ']':
- return values, end + 1
- iterscan = JSONScanner.iterscan
- while True:
- try:
- value, end = iterscan(s, idx=end, context=context).next()
- except StopIteration:
- raise ValueError(errmsg("Expecting object", s, end))
- values.append(value)
- end = _w(s, end).end()
- nextchar = s[end:end + 1]
- end += 1
- if nextchar == ']':
- break
- if nextchar != ',':
- raise ValueError(errmsg("Expecting , delimiter", s, end))
- end = _w(s, end).end()
- return values, end
-pattern(r'\[')(JSONArray)
-
-ANYTHING = [
- JSONObject,
- JSONArray,
- JSONString,
- JSONConstant,
- JSONNumber,
-]
-
-JSONScanner = Scanner(ANYTHING)
-
-class JSONDecoder(object):
- """
- Simple JSON <http://json.org> decoder
-
- Performs the following translations in decoding:
-
- +---------------+-------------------+
- | JSON | Python |
- +===============+===================+
- | object | dict |
- +---------------+-------------------+
- | array | list |
- +---------------+-------------------+
- | string | unicode |
- +---------------+-------------------+
- | number (int) | int, long |
- +---------------+-------------------+
- | number (real) | float |
- +---------------+-------------------+
- | true | True |
- +---------------+-------------------+
- | false | False |
- +---------------+-------------------+
- | null | None |
- +---------------+-------------------+
-
- It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
- their corresponding ``float`` values, which is outside the JSON spec.
- """
-
- _scanner = Scanner(ANYTHING)
- __all__ = ['__init__', 'decode', 'raw_decode']
-
- def __init__(self, encoding=None, object_hook=None):
- """
- ``encoding`` determines the encoding used to interpret any ``str``
- objects decoded by this instance (utf-8 by default). It has no
- effect when decoding ``unicode`` objects.
-
- Note that currently only encodings that are a superset of ASCII work,
- strings of other encodings should be passed in as ``unicode``.
-
- ``object_hook``, if specified, will be called with the result
- of every JSON object decoded and its return value will be used in
- place of the given ``dict``. This can be used to provide custom
- deserializations (e.g. to support JSON-RPC class hinting).
- """
- self.encoding = encoding
- self.object_hook = object_hook
-
- def decode(self, s, _w=WHITESPACE.match):
- """
- Return the Python representation of ``s`` (a ``str`` or ``unicode``
- instance containing a JSON document)
- """
- obj, end = self.raw_decode(s, idx=_w(s, 0).end())
- end = _w(s, end).end()
- if end != len(s):
- raise ValueError(errmsg("Extra data", s, end, len(s)))
- return obj
-
- def raw_decode(self, s, **kw):
- """
- Decode a JSON document from ``s`` (a ``str`` or ``unicode`` beginning
- with a JSON document) and return a 2-tuple of the Python
- representation and the index in ``s`` where the document ended.
-
- This can be used to decode a JSON document from a string that may
- have extraneous data at the end.
- """
- kw.setdefault('context', self)
- try:
- obj, end = self._scanner.iterscan(s, **kw).next()
- except StopIteration:
- raise ValueError("No JSON object could be decoded")
- return obj, end
-
-__all__ = ['JSONDecoder']
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/encoder.py b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/encoder.py
deleted file mode 100644
index d29919a..0000000
--- a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/encoder.py
+++ /dev/null
@@ -1,371 +0,0 @@
-"""
-Implementation of JSONEncoder
-"""
-import re
-try:
- from simplejson import _speedups
-except ImportError:
- _speedups = None
-
-ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
-ESCAPE_ASCII = re.compile(r'([\\"/]|[^\ -~])')
-ESCAPE_DCT = {
- # escape all forward slashes to prevent </script> attack
- '/': '\\/',
- '\\': '\\\\',
- '"': '\\"',
- '\b': '\\b',
- '\f': '\\f',
- '\n': '\\n',
- '\r': '\\r',
- '\t': '\\t',
-}
-for i in range(0x20):
- ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
-
-# assume this produces an infinity on all machines (probably not guaranteed)
-INFINITY = float('1e66666')
-
-def floatstr(o, allow_nan=True):
- # Check for specials. Note that this type of test is processor- and/or
- # platform-specific, so do tests which don't depend on the internals.
-
- if o != o:
- text = 'NaN'
- elif o == INFINITY:
- text = 'Infinity'
- elif o == -INFINITY:
- text = '-Infinity'
- else:
- return repr(o)
-
- if not allow_nan:
- raise ValueError("Out of range float values are not JSON compliant: %r"
- % (o,))
-
- return text
-
-
-def encode_basestring(s):
- """
- Return a JSON representation of a Python string
- """
- def replace(match):
- return ESCAPE_DCT[match.group(0)]
- return '"' + ESCAPE.sub(replace, s) + '"'
-
-def encode_basestring_ascii(s):
- def replace(match):
- s = match.group(0)
- try:
- return ESCAPE_DCT[s]
- except KeyError:
- n = ord(s)
- if n < 0x10000:
- return '\\u%04x' % (n,)
- else:
- # surrogate pair
- n -= 0x10000
- s1 = 0xd800 | ((n >> 10) & 0x3ff)
- s2 = 0xdc00 | (n & 0x3ff)
- return '\\u%04x\\u%04x' % (s1, s2)
- return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
-
-try:
- encode_basestring_ascii = _speedups.encode_basestring_ascii
- _need_utf8 = True
-except AttributeError:
- _need_utf8 = False
-
-class JSONEncoder(object):
- """
- Extensible JSON <http://json.org> encoder for Python data structures.
-
- Supports the following objects and types by default:
-
- +-------------------+---------------+
- | Python | JSON |
- +===================+===============+
- | dict | object |
- +-------------------+---------------+
- | list, tuple | array |
- +-------------------+---------------+
- | str, unicode | string |
- +-------------------+---------------+
- | int, long, float | number |
- +-------------------+---------------+
- | True | true |
- +-------------------+---------------+
- | False | false |
- +-------------------+---------------+
- | None | null |
- +-------------------+---------------+
-
- To extend this to recognize other objects, subclass and implement a
- ``.default()`` method with another method that returns a serializable
- object for ``o`` if possible, otherwise it should call the superclass
- implementation (to raise ``TypeError``).
- """
- __all__ = ['__init__', 'default', 'encode', 'iterencode']
- item_separator = ', '
- key_separator = ': '
- def __init__(self, skipkeys=False, ensure_ascii=True,
- check_circular=True, allow_nan=True, sort_keys=False,
- indent=None, separators=None, encoding='utf-8'):
- """
- Constructor for JSONEncoder, with sensible defaults.
-
- If skipkeys is False, then it is a TypeError to attempt
- encoding of keys that are not str, int, long, float or None. If
- skipkeys is True, such items are simply skipped.
-
- If ensure_ascii is True, the output is guaranteed to be str
- objects with all incoming unicode characters escaped. If
- ensure_ascii is false, the output will be unicode object.
-
- If check_circular is True, then lists, dicts, and custom encoded
- objects will be checked for circular references during encoding to
- prevent an infinite recursion (which would cause an OverflowError).
- Otherwise, no such check takes place.
-
- If allow_nan is True, then NaN, Infinity, and -Infinity will be
- encoded as such. This behavior is not JSON specification compliant,
- but is consistent with most JavaScript based encoders and decoders.
- Otherwise, it will be a ValueError to encode such floats.
-
- If sort_keys is True, then the output of dictionaries will be
- sorted by key; this is useful for regression tests to ensure
- that JSON serializations can be compared on a day-to-day basis.
-
- If indent is a non-negative integer, then JSON array
- elements and object members will be pretty-printed with that
- indent level. An indent level of 0 will only insert newlines.
- None is the most compact representation.
-
- If specified, separators should be a (item_separator, key_separator)
- tuple. The default is (', ', ': '). To get the most compact JSON
- representation you should specify (',', ':') to eliminate whitespace.
-
- If encoding is not None, then all input strings will be
- transformed into unicode using that encoding prior to JSON-encoding.
- The default is UTF-8.
- """
-
- self.skipkeys = skipkeys
- self.ensure_ascii = ensure_ascii
- self.check_circular = check_circular
- self.allow_nan = allow_nan
- self.sort_keys = sort_keys
- self.indent = indent
- self.current_indent_level = 0
- if separators is not None:
- self.item_separator, self.key_separator = separators
- self.encoding = encoding
-
- def _newline_indent(self):
- return '\n' + (' ' * (self.indent * self.current_indent_level))
-
- def _iterencode_list(self, lst, markers=None):
- if not lst:
- yield '[]'
- return
- if markers is not None:
- markerid = id(lst)
- if markerid in markers:
- raise ValueError("Circular reference detected")
- markers[markerid] = lst
- yield '['
- if self.indent is not None:
- self.current_indent_level += 1
- newline_indent = self._newline_indent()
- separator = self.item_separator + newline_indent
- yield newline_indent
- else:
- newline_indent = None
- separator = self.item_separator
- first = True
- for value in lst:
- if first:
- first = False
- else:
- yield separator
- for chunk in self._iterencode(value, markers):
- yield chunk
- if newline_indent is not None:
- self.current_indent_level -= 1
- yield self._newline_indent()
- yield ']'
- if markers is not None:
- del markers[markerid]
-
- def _iterencode_dict(self, dct, markers=None):
- if not dct:
- yield '{}'
- return
- if markers is not None:
- markerid = id(dct)
- if markerid in markers:
- raise ValueError("Circular reference detected")
- markers[markerid] = dct
- yield '{'
- key_separator = self.key_separator
- if self.indent is not None:
- self.current_indent_level += 1
- newline_indent = self._newline_indent()
- item_separator = self.item_separator + newline_indent
- yield newline_indent
- else:
- newline_indent = None
- item_separator = self.item_separator
- first = True
- if self.ensure_ascii:
- encoder = encode_basestring_ascii
- else:
- encoder = encode_basestring
- allow_nan = self.allow_nan
- if self.sort_keys:
- keys = dct.keys()
- keys.sort()
- items = [(k, dct[k]) for k in keys]
- else:
- items = dct.iteritems()
- _encoding = self.encoding
- _do_decode = (_encoding is not None
- and not (_need_utf8 and _encoding == 'utf-8'))
- for key, value in items:
- if isinstance(key, str):
- if _do_decode:
- key = key.decode(_encoding)
- elif isinstance(key, basestring):
- pass
- # JavaScript is weakly typed for these, so it makes sense to
- # also allow them. Many encoders seem to do something like this.
- elif isinstance(key, float):
- key = floatstr(key, allow_nan)
- elif isinstance(key, (int, long)):
- key = str(key)
- elif key is True:
- key = 'true'
- elif key is False:
- key = 'false'
- elif key is None:
- key = 'null'
- elif self.skipkeys:
- continue
- else:
- raise TypeError("key %r is not a string" % (key,))
- if first:
- first = False
- else:
- yield item_separator
- yield encoder(key)
- yield key_separator
- for chunk in self._iterencode(value, markers):
- yield chunk
- if newline_indent is not None:
- self.current_indent_level -= 1
- yield self._newline_indent()
- yield '}'
- if markers is not None:
- del markers[markerid]
-
- def _iterencode(self, o, markers=None):
- if isinstance(o, basestring):
- if self.ensure_ascii:
- encoder = encode_basestring_ascii
- else:
- encoder = encode_basestring
- _encoding = self.encoding
- if (_encoding is not None and isinstance(o, str)
- and not (_need_utf8 and _encoding == 'utf-8')):
- o = o.decode(_encoding)
- yield encoder(o)
- elif o is None:
- yield 'null'
- elif o is True:
- yield 'true'
- elif o is False:
- yield 'false'
- elif isinstance(o, (int, long)):
- yield str(o)
- elif isinstance(o, float):
- yield floatstr(o, self.allow_nan)
- elif isinstance(o, (list, tuple)):
- for chunk in self._iterencode_list(o, markers):
- yield chunk
- elif isinstance(o, dict):
- for chunk in self._iterencode_dict(o, markers):
- yield chunk
- else:
- if markers is not None:
- markerid = id(o)
- if markerid in markers:
- raise ValueError("Circular reference detected")
- markers[markerid] = o
- for chunk in self._iterencode_default(o, markers):
- yield chunk
- if markers is not None:
- del markers[markerid]
-
- def _iterencode_default(self, o, markers=None):
- newobj = self.default(o)
- return self._iterencode(newobj, markers)
-
- def default(self, o):
- """
- Implement this method in a subclass such that it returns
- a serializable object for ``o``, or calls the base implementation
- (to raise a ``TypeError``).
-
- For example, to support arbitrary iterators, you could
- implement default like this::
-
- def default(self, o):
- try:
- iterable = iter(o)
- except TypeError:
- pass
- else:
- return list(iterable)
- return JSONEncoder.default(self, o)
- """
- raise TypeError("%r is not JSON serializable" % (o,))
-
- def encode(self, o):
- """
- Return a JSON string representation of a Python data structure.
-
- >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
- '{"foo":["bar", "baz"]}'
- """
- # This is for extremely simple cases and benchmarks...
- if isinstance(o, basestring):
- if isinstance(o, str):
- _encoding = self.encoding
- if (_encoding is not None
- and not (_encoding == 'utf-8' and _need_utf8)):
- o = o.decode(_encoding)
- return encode_basestring_ascii(o)
- # This doesn't pass the iterator directly to ''.join() because it
- # sucks at reporting exceptions. It's going to do this internally
- # anyway because it uses PySequence_Fast or similar.
- chunks = list(self.iterencode(o))
- return ''.join(chunks)
-
- def iterencode(self, o):
- """
- Encode the given object and yield each string
- representation as available.
-
- For example::
-
- for chunk in JSONEncoder().iterencode(bigobject):
- mysocket.write(chunk)
- """
- if self.check_circular:
- markers = {}
- else:
- markers = None
- return self._iterencode(o, markers)
-
-__all__ = ['JSONEncoder']
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/jsonfilter.py b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/jsonfilter.py
deleted file mode 100644
index 01ca21d..0000000
--- a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/jsonfilter.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import simplejson
-import cgi
-
-class JSONFilter(object):
- def __init__(self, app, mime_type='text/x-json'):
- self.app = app
- self.mime_type = mime_type
-
- def __call__(self, environ, start_response):
- # Read JSON POST input to jsonfilter.json if matching mime type
- response = {'status': '200 OK', 'headers': []}
- def json_start_response(status, headers):
- response['status'] = status
- response['headers'].extend(headers)
- environ['jsonfilter.mime_type'] = self.mime_type
- if environ.get('REQUEST_METHOD', '') == 'POST':
- if environ.get('CONTENT_TYPE', '') == self.mime_type:
- args = [_ for _ in [environ.get('CONTENT_LENGTH')] if _]
- data = environ['wsgi.input'].read(*map(int, args))
- environ['jsonfilter.json'] = simplejson.loads(data)
- res = simplejson.dumps(self.app(environ, json_start_response))
- jsonp = cgi.parse_qs(environ.get('QUERY_STRING', '')).get('jsonp')
- if jsonp:
- content_type = 'text/javascript'
- res = ''.join(jsonp + ['(', res, ')'])
- elif 'Opera' in environ.get('HTTP_USER_AGENT', ''):
- # Opera has bunk XMLHttpRequest support for most mime types
- content_type = 'text/plain'
- else:
- content_type = self.mime_type
- headers = [
- ('Content-type', content_type),
- ('Content-length', len(res)),
- ]
- headers.extend(response['headers'])
- start_response(response['status'], headers)
- return [res]
-
-def factory(app, global_conf, **kw):
- return JSONFilter(app, **kw)
diff --git a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/scanner.py b/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/scanner.py
deleted file mode 100644
index 64f4999..0000000
--- a/WebKitTools/Scripts/webkitpy/thirdparty/simplejson/scanner.py
+++ /dev/null
@@ -1,63 +0,0 @@
-"""
-Iterator based sre token scanner
-"""
-import sre_parse, sre_compile, sre_constants
-from sre_constants import BRANCH, SUBPATTERN
-from re import VERBOSE, MULTILINE, DOTALL
-import re
-
-__all__ = ['Scanner', 'pattern']
-
-FLAGS = (VERBOSE | MULTILINE | DOTALL)
-class Scanner(object):
- def __init__(self, lexicon, flags=FLAGS):
- self.actions = [None]
- # combine phrases into a compound pattern
- s = sre_parse.Pattern()
- s.flags = flags
- p = []
- for idx, token in enumerate(lexicon):
- phrase = token.pattern
- try:
- subpattern = sre_parse.SubPattern(s,
- [(SUBPATTERN, (idx + 1, sre_parse.parse(phrase, flags)))])
- except sre_constants.error:
- raise
- p.append(subpattern)
- self.actions.append(token)
-
- p = sre_parse.SubPattern(s, [(BRANCH, (None, p))])
- self.scanner = sre_compile.compile(p)
-
-
- def iterscan(self, string, idx=0, context=None):
- """
- Yield match, end_idx for each match
- """
- match = self.scanner.scanner(string, idx).match
- actions = self.actions
- lastend = idx
- end = len(string)
- while True:
- m = match()
- if m is None:
- break
- matchbegin, matchend = m.span()
- if lastend == matchend:
- break
- action = actions[m.lastindex]
- if action is not None:
- rval, next_pos = action(m, context)
- if next_pos is not None and next_pos != matchend:
- # "fast forward" the scanner
- matchend = next_pos
- match = self.scanner.scanner(string, matchend).match
- yield rval, matchend
- lastend = matchend
-
-def pattern(pattern, flags=FLAGS):
- def decorator(fn):
- fn.pattern = pattern
- fn.regex = re.compile(pattern, flags)
- return fn
- return decorator
diff --git a/WebKitTools/Scripts/webkitpy/tool/__init__.py b/WebKitTools/Scripts/webkitpy/tool/__init__.py
deleted file mode 100644
index ef65bee..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/__init__.py b/WebKitTools/Scripts/webkitpy/tool/bot/__init__.py
deleted file mode 100644
index ef65bee..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/bot/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Required for Python to search this directory for module files
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask.py b/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask.py
deleted file mode 100644
index ea12702..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask.py
+++ /dev/null
@@ -1,196 +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.
-
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.common.net.layouttestresults import LayoutTestResults
-
-
-class CommitQueueTaskDelegate(object):
- def run_command(self, command):
- raise NotImplementedError("subclasses must implement")
-
- def command_passed(self, message, patch):
- raise NotImplementedError("subclasses must implement")
-
- def command_failed(self, message, script_error, patch):
- raise NotImplementedError("subclasses must implement")
-
- def refetch_patch(self, patch):
- raise NotImplementedError("subclasses must implement")
-
- def layout_test_results(self):
- raise NotImplementedError("subclasses must implement")
-
- def report_flaky_tests(self, patch, flaky_tests):
- raise NotImplementedError("subclasses must implement")
-
-
-class CommitQueueTask(object):
- def __init__(self, delegate, patch):
- self._delegate = delegate
- self._patch = patch
- self._script_error = None
-
- def _validate(self):
- # Bugs might get closed, or patches might be obsoleted or r-'d while the
- # commit-queue is processing.
- self._patch = self._delegate.refetch_patch(self._patch)
- if self._patch.is_obsolete():
- return False
- if self._patch.bug().is_closed():
- return False
- if not self._patch.committer():
- return False
- # Reviewer is not required. Missing reviewers will be caught during
- # the ChangeLog check during landing.
- return True
-
- def _run_command(self, command, success_message, failure_message):
- try:
- self._delegate.run_command(command)
- self._delegate.command_passed(success_message, patch=self._patch)
- return True
- except ScriptError, e:
- self._script_error = e
- self.failure_status_id = self._delegate.command_failed(failure_message, script_error=self._script_error, patch=self._patch)
- return False
-
- def _apply(self):
- return self._run_command([
- "apply-attachment",
- "--force-clean",
- "--non-interactive",
- self._patch.id(),
- ],
- "Applied patch",
- "Patch does not apply")
-
- def _build(self):
- return self._run_command([
- "build",
- "--no-clean",
- "--no-update",
- "--build-style=both",
- ],
- "Built patch",
- "Patch does not build")
-
- def _build_without_patch(self):
- return self._run_command([
- "build",
- "--force-clean",
- "--no-update",
- "--build-style=both",
- ],
- "Able to build without patch",
- "Unable to build without patch")
-
- def _test(self):
- return self._run_command([
- "build-and-test",
- "--no-clean",
- "--no-update",
- # Notice that we don't pass --build, which means we won't build!
- "--test",
- "--non-interactive",
- ],
- "Passed tests",
- "Patch does not pass tests")
-
- def _build_and_test_without_patch(self):
- return self._run_command([
- "build-and-test",
- "--force-clean",
- "--no-update",
- "--build",
- "--test",
- "--non-interactive",
- ],
- "Able to pass tests without patch",
- "Unable to pass tests without patch (tree is red?)")
-
- def _failing_tests_from_last_run(self):
- results = self._delegate.layout_test_results()
- if not results:
- return None
- return results.failing_tests()
-
- def _land(self):
- # Unclear if this should pass --quiet or not. If --parent-command always does the reporting, then it should.
- return self._run_command([
- "land-attachment",
- "--force-clean",
- "--ignore-builders",
- "--non-interactive",
- "--parent-command=commit-queue",
- self._patch.id(),
- ],
- "Landed patch",
- "Unable to land patch")
-
- def _report_flaky_tests(self, flaky_tests):
- self._delegate.report_flaky_tests(self._patch, flaky_tests)
-
- def _test_patch(self):
- if self._patch.is_rollout():
- return True
- if self._test():
- return True
-
- first_failing_tests = self._failing_tests_from_last_run()
- if self._test():
- self._report_flaky_tests(first_failing_tests)
- return True
-
- second_failing_tests = self._failing_tests_from_last_run()
- if first_failing_tests != second_failing_tests:
- self._report_flaky_tests(first_failing_tests + second_failing_tests)
- return False
-
- if self._build_and_test_without_patch():
- raise self._script_error # The error from the previous ._test() run is real, report it.
- return False # Tree must be red, just retry later.
-
- def run(self):
- if not self._validate():
- return False
- if not self._apply():
- raise self._script_error
- if not self._build():
- if not self._build_without_patch():
- return False
- raise self._script_error
- if not self._test_patch():
- return False
- # Make sure the patch is still valid before landing (e.g., make sure
- # no one has set commit-queue- since we started working on the patch.)
- if not self._validate():
- return False
- if not self._land():
- raise self._script_error
- return True
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py
deleted file mode 100644
index 15a4a6b..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py
+++ /dev/null
@@ -1,204 +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.
-
-from datetime import datetime
-import unittest
-
-from webkitpy.common.system.deprecated_logging import error, log
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.tool.bot.commitqueuetask import *
-from webkitpy.tool.mocktool import MockTool
-
-
-class MockCommitQueue(CommitQueueTaskDelegate):
- def __init__(self, error_plan):
- self._error_plan = error_plan
-
- def run_command(self, command):
- log("run_webkit_patch: %s" % command)
- if self._error_plan:
- error = self._error_plan.pop(0)
- if error:
- raise error
-
- def command_passed(self, success_message, patch):
- log("command_passed: success_message='%s' patch='%s'" % (
- success_message, patch.id()))
-
- def command_failed(self, failure_message, script_error, patch):
- log("command_failed: failure_message='%s' script_error='%s' patch='%s'" % (
- failure_message, script_error, patch.id()))
- return 3947
-
- def refetch_patch(self, patch):
- return patch
-
- def layout_test_results(self):
- return None
-
- def report_flaky_tests(self, patch, flaky_tests):
- log("report_flaky_tests: patch='%s' flaky_tests='%s'" % (patch.id(), flaky_tests))
-
-
-class CommitQueueTaskTest(unittest.TestCase):
- def _run_through_task(self, commit_queue, expected_stderr, expected_exception=None):
- tool = MockTool(log_executive=True)
- patch = tool.bugs.fetch_attachment(197)
- task = CommitQueueTask(commit_queue, patch)
- OutputCapture().assert_outputs(self, task.run, expected_stderr=expected_stderr, expected_exception=expected_exception)
-
- def test_success_case(self):
- commit_queue = MockCommitQueue([])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
-command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
-command_passed: success_message='Built patch' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
-command_passed: success_message='Passed tests' patch='197'
-run_webkit_patch: ['land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 197]
-command_passed: success_message='Landed patch' patch='197'
-"""
- self._run_through_task(commit_queue, expected_stderr)
-
- def test_apply_failure(self):
- commit_queue = MockCommitQueue([
- ScriptError("MOCK apply failure"),
- ])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
-command_failed: failure_message='Patch does not apply' script_error='MOCK apply failure' patch='197'
-"""
- self._run_through_task(commit_queue, expected_stderr, ScriptError)
-
- def test_build_failure(self):
- commit_queue = MockCommitQueue([
- None,
- ScriptError("MOCK build failure"),
- ])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
-command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
-command_failed: failure_message='Patch does not build' script_error='MOCK build failure' patch='197'
-run_webkit_patch: ['build', '--force-clean', '--no-update', '--build-style=both']
-command_passed: success_message='Able to build without patch' patch='197'
-"""
- self._run_through_task(commit_queue, expected_stderr, ScriptError)
-
- def test_red_build_failure(self):
- commit_queue = MockCommitQueue([
- None,
- ScriptError("MOCK build failure"),
- ScriptError("MOCK clean build failure"),
- ])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
-command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
-command_failed: failure_message='Patch does not build' script_error='MOCK build failure' patch='197'
-run_webkit_patch: ['build', '--force-clean', '--no-update', '--build-style=both']
-command_failed: failure_message='Unable to build without patch' script_error='MOCK clean build failure' patch='197'
-"""
- self._run_through_task(commit_queue, expected_stderr)
-
- def test_flaky_test_failure(self):
- commit_queue = MockCommitQueue([
- None,
- None,
- ScriptError("MOCK tests failure"),
- ])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
-command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
-command_passed: success_message='Built patch' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
-command_failed: failure_message='Patch does not pass tests' script_error='MOCK tests failure' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
-command_passed: success_message='Passed tests' patch='197'
-report_flaky_tests: patch='197' flaky_tests='None'
-run_webkit_patch: ['land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 197]
-command_passed: success_message='Landed patch' patch='197'
-"""
- self._run_through_task(commit_queue, expected_stderr)
-
- def test_test_failure(self):
- commit_queue = MockCommitQueue([
- None,
- None,
- ScriptError("MOCK test failure"),
- ScriptError("MOCK test failure again"),
- ])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
-command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
-command_passed: success_message='Built patch' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
-command_failed: failure_message='Patch does not pass tests' script_error='MOCK test failure' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
-command_failed: failure_message='Patch does not pass tests' script_error='MOCK test failure again' patch='197'
-run_webkit_patch: ['build-and-test', '--force-clean', '--no-update', '--build', '--test', '--non-interactive']
-command_passed: success_message='Able to pass tests without patch' patch='197'
-"""
- self._run_through_task(commit_queue, expected_stderr, ScriptError)
-
- def test_red_test_failure(self):
- commit_queue = MockCommitQueue([
- None,
- None,
- ScriptError("MOCK test failure"),
- ScriptError("MOCK test failure again"),
- ScriptError("MOCK clean test failure"),
- ])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
-command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
-command_passed: success_message='Built patch' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
-command_failed: failure_message='Patch does not pass tests' script_error='MOCK test failure' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
-command_failed: failure_message='Patch does not pass tests' script_error='MOCK test failure again' patch='197'
-run_webkit_patch: ['build-and-test', '--force-clean', '--no-update', '--build', '--test', '--non-interactive']
-command_failed: failure_message='Unable to pass tests without patch (tree is red?)' script_error='MOCK clean test failure' patch='197'
-"""
- self._run_through_task(commit_queue, expected_stderr)
-
- def test_land_failure(self):
- commit_queue = MockCommitQueue([
- None,
- None,
- None,
- ScriptError("MOCK land failure"),
- ])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
-command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
-command_passed: success_message='Built patch' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
-command_passed: success_message='Passed tests' patch='197'
-run_webkit_patch: ['land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 197]
-command_failed: failure_message='Unable to land patch' script_error='MOCK land failure' patch='197'
-"""
- self._run_through_task(commit_queue, expected_stderr, ScriptError)
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/feeders.py b/WebKitTools/Scripts/webkitpy/tool/bot/feeders.py
deleted file mode 100644
index 046c4c1..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/bot/feeders.py
+++ /dev/null
@@ -1,90 +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.
-
-from webkitpy.common.config.committervalidator import CommitterValidator
-from webkitpy.common.system.deprecated_logging import log
-from webkitpy.tool.grammar import pluralize
-
-
-class AbstractFeeder(object):
- def __init__(self, tool):
- self._tool = tool
-
- def feed(self):
- raise NotImplementedError("subclasses must implement")
-
-
-class CommitQueueFeeder(AbstractFeeder):
- queue_name = "commit-queue"
-
- def __init__(self, tool):
- AbstractFeeder.__init__(self, tool)
- self.committer_validator = CommitterValidator(self._tool.bugs)
-
- def _update_work_items(self, item_ids):
- # FIXME: This is the last use of update_work_items, the commit-queue
- # should move to feeding patches one at a time like the EWS does.
- self._tool.status_server.update_work_items(self.queue_name, item_ids)
- log("Feeding %s items %s" % (self.queue_name, item_ids))
-
- def feed(self):
- patches = self._validate_patches()
- patches = sorted(patches, self._patch_cmp)
- patch_ids = [patch.id() for patch in patches]
- self._update_work_items(patch_ids)
-
- def _patches_for_bug(self, bug_id):
- return self._tool.bugs.fetch_bug(bug_id).commit_queued_patches(include_invalid=True)
-
- def _validate_patches(self):
- # 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._patches_for_bug(bug_id) for bug_id in bug_ids], [])
- return self.committer_validator.patches_after_rejecting_invalid_commiters_and_reviewers(all_patches)
-
- def _patch_cmp(self, a, b):
- # Sort first by is_rollout, then by attach_date.
- # Reversing the order so that is_rollout is first.
- rollout_cmp = cmp(b.is_rollout(), a.is_rollout())
- if rollout_cmp != 0:
- return rollout_cmp
- return cmp(a.attach_date(), b.attach_date())
-
-
-class EWSFeeder(AbstractFeeder):
- def __init__(self, tool):
- self._ids_sent_to_server = set()
- AbstractFeeder.__init__(self, tool)
-
- def feed(self):
- ids_needing_review = set(self._tool.bugs.queries.fetch_attachment_ids_from_review_queue())
- new_ids = ids_needing_review.difference(self._ids_sent_to_server)
- log("Feeding EWS (%s, %s new)" % (pluralize("r? patch", len(ids_needing_review)), len(new_ids)))
- for attachment_id in new_ids: # Order doesn't really matter for the EWS.
- self._tool.status_server.submit_to_ews(attachment_id)
- self._ids_sent_to_server.add(attachment_id)
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/feeders_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/feeders_unittest.py
deleted file mode 100644
index 5ce00b4..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/bot/feeders_unittest.py
+++ /dev/null
@@ -1,70 +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.
-
-from datetime import datetime
-import unittest
-
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.tool.bot.feeders import *
-from webkitpy.tool.mocktool import MockTool
-
-
-class FeedersTest(unittest.TestCase):
- def test_commit_queue_feeder(self):
- feeder = CommitQueueFeeder(MockTool())
- expected_stderr = u"""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 setting flag 'commit-queue' to '-' on attachment '128' with comment 'Rejecting patch 128 from commit-queue.' and additional comment 'non-committer@example.com does not have committer permissions according to http://trac.webkit.org/browser/trunk/WebKitTools/Scripts/webkitpy/common/config/committers.py.
-
-- If you do not have committer rights please read http://webkit.org/coding/contributing.html for instructions on how to use bugzilla flags.
-
-- If you have committer rights please correct the error in WebKitTools/Scripts/webkitpy/common/config/committers.py by adding yourself to the file (no review needed). The commit-queue restarts itself every 2 hours. After restart the commit-queue will correctly respect your committer rights.'
-MOCK: update_work_items: commit-queue [106, 197]
-Feeding commit-queue items [106, 197]
-"""
- OutputCapture().assert_outputs(self, feeder.feed, expected_stderr=expected_stderr)
-
- def _mock_attachment(self, is_rollout, attach_date):
- attachment = Mock()
- attachment.is_rollout = lambda: is_rollout
- attachment.attach_date = lambda: attach_date
- return attachment
-
- def test_patch_cmp(self):
- long_ago_date = datetime(1900, 1, 21)
- recent_date = datetime(2010, 1, 21)
- attachment1 = self._mock_attachment(is_rollout=False, attach_date=recent_date)
- attachment2 = self._mock_attachment(is_rollout=False, attach_date=long_ago_date)
- attachment3 = self._mock_attachment(is_rollout=True, attach_date=recent_date)
- attachment4 = self._mock_attachment(is_rollout=True, attach_date=long_ago_date)
- attachments = [attachment1, attachment2, attachment3, attachment4]
- expected_sort = [attachment4, attachment3, attachment2, attachment1]
- queue = CommitQueueFeeder(MockTool())
- attachments.sort(queue._patch_cmp)
- self.assertEqual(attachments, expected_sort)
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/irc_command.py b/WebKitTools/Scripts/webkitpy/tool/bot/irc_command.py
deleted file mode 100644
index a848472..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/bot/irc_command.py
+++ /dev/null
@@ -1,109 +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.
-
-import random
-import webkitpy.common.config.irc as config_irc
-
-from webkitpy.common.checkout.changelog import view_source_url
-from webkitpy.tool.bot.queueengine import TerminateQueue
-from webkitpy.common.net.bugzilla import parse_bug_id
-from webkitpy.common.system.executive import ScriptError
-
-# FIXME: Merge with Command?
-class IRCCommand(object):
- def execute(self, nick, args, tool, sheriff):
- raise NotImplementedError, "subclasses must implement"
-
-
-class LastGreenRevision(IRCCommand):
- def execute(self, nick, args, tool, sheriff):
- return "%s: %s" % (nick,
- view_source_url(tool.buildbot.last_green_revision()))
-
-
-class Restart(IRCCommand):
- def execute(self, nick, args, tool, sheriff):
- tool.irc().post("Restarting...")
- raise TerminateQueue()
-
-
-class Rollout(IRCCommand):
- def execute(self, nick, args, tool, sheriff):
- if len(args) < 2:
- tool.irc().post("%s: Usage: SVN_REVISION REASON" % nick)
- return
- svn_revision = args[0].lstrip("r")
- rollout_reason = " ".join(args[1:])
- tool.irc().post("Preparing rollout for r%s..." % svn_revision)
- try:
- complete_reason = "%s (Requested by %s on %s)." % (
- rollout_reason, nick, config_irc.channel)
- bug_id = sheriff.post_rollout_patch(svn_revision, complete_reason)
- bug_url = tool.bugs.bug_url_for_bug_id(bug_id)
- tool.irc().post("%s: Created rollout: %s" % (nick, bug_url))
- except ScriptError, e:
- tool.irc().post("%s: Failed to create rollout patch:" % nick)
- tool.irc().post("%s" % e)
- bug_id = parse_bug_id(e.output)
- if bug_id:
- tool.irc().post("Ugg... Might have created %s" %
- tool.bugs.bug_url_for_bug_id(bug_id))
-
-
-class Help(IRCCommand):
- def execute(self, nick, args, tool, sheriff):
- return "%s: Available commands: %s" % (nick, ", ".join(commands.keys()))
-
-
-class Hi(IRCCommand):
- def execute(self, nick, args, tool, sheriff):
- quips = tool.bugs.quips()
- quips.append('"Only you can prevent forest fires." -- Smokey the Bear')
- return random.choice(quips)
-
-
-class Eliza(IRCCommand):
- therapist = None
-
- def __init__(self):
- if not self.therapist:
- import webkitpy.thirdparty.autoinstalled.eliza as eliza
- Eliza.therapist = eliza.eliza()
-
- def execute(self, nick, args, tool, sheriff):
- return "%s: %s" % (nick, self.therapist.respond(" ".join(args)))
-
-
-# FIXME: Lame. We should have an auto-registering CommandCenter.
-commands = {
- "last-green-revision": LastGreenRevision,
- "restart": Restart,
- "rollout": Rollout,
- "help": Help,
- "hi": Hi,
-}
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/irc_command_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/irc_command_unittest.py
deleted file mode 100644
index 7aeb6a0..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/bot/irc_command_unittest.py
+++ /dev/null
@@ -1,38 +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.
-
-import unittest
-
-from webkitpy.tool.bot.irc_command import *
-
-
-class IRCCommandTest(unittest.TestCase):
- def test_eliza(self):
- eliza = Eliza()
- eliza.execute("tom", "hi", None, None)
- eliza.execute("tom", "bye", None, None)
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py
deleted file mode 100644
index 8b016e8..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py
+++ /dev/null
@@ -1,165 +0,0 @@
-# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple 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 os
-import time
-import traceback
-
-from datetime import datetime, timedelta
-
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.common.system.deprecated_logging import log, OutputTee
-
-
-class TerminateQueue(Exception):
- pass
-
-
-class QueueEngineDelegate:
- def queue_log_path(self):
- raise NotImplementedError, "subclasses must implement"
-
- def work_item_log_path(self, work_item):
- raise NotImplementedError, "subclasses must implement"
-
- def begin_work_queue(self):
- raise NotImplementedError, "subclasses must implement"
-
- def should_continue_work_queue(self):
- raise NotImplementedError, "subclasses must implement"
-
- def next_work_item(self):
- raise NotImplementedError, "subclasses must implement"
-
- def should_proceed_with_work_item(self, work_item):
- # returns (safe_to_proceed, waiting_message, patch)
- raise NotImplementedError, "subclasses must implement"
-
- def process_work_item(self, work_item):
- raise NotImplementedError, "subclasses must implement"
-
- def handle_unexpected_error(self, work_item, message):
- raise NotImplementedError, "subclasses must implement"
-
-
-class QueueEngine:
- def __init__(self, name, delegate, wakeup_event):
- self._name = name
- self._delegate = delegate
- self._wakeup_event = wakeup_event
- self._output_tee = OutputTee()
-
- log_date_format = "%Y-%m-%d %H:%M:%S"
- 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.
- @classmethod
- def exit_after_handled_error(cls, error):
- log(error)
- exit(cls.handled_error_code)
-
- def run(self):
- self._begin_logging()
-
- self._delegate.begin_work_queue()
- while (self._delegate.should_continue_work_queue()):
- try:
- self._ensure_work_log_closed()
- work_item = self._delegate.next_work_item()
- if not work_item:
- self._sleep("No work item.")
- continue
- if not self._delegate.should_proceed_with_work_item(work_item):
- self._sleep("Not proceeding with work item.")
- continue
-
- # FIXME: Work logs should not depend on bug_id specificaly.
- # This looks fixed, no?
- self._open_work_log(work_item)
- try:
- if not self._delegate.process_work_item(work_item):
- log("Unable to process work item.")
- continue
- except ScriptError, e:
- # Use a special exit code to indicate that the error was already
- # handled in the child process and we should just keep looping.
- if e.exit_code == self.handled_error_code:
- continue
- message = "Unexpected failure when processing patch! Please file a bug against webkit-patch.\n%s" % e.message_with_output()
- self._delegate.handle_unexpected_error(work_item, message)
- except TerminateQueue, e:
- self._stopping("TerminateQueue exception received.")
- return 0
- except KeyboardInterrupt, e:
- self._stopping("User terminated queue.")
- return 1
- except Exception, e:
- traceback.print_exc()
- # Don't try tell the status bot, in case telling it causes an exception.
- self._sleep("Exception while preparing queue")
- self._stopping("Delegate terminated queue.")
- return 0
-
- def _stopping(self, message):
- log("\n%s" % message)
- self._delegate.stop_work_queue(message)
- # Be careful to shut down our OutputTee or the unit tests will be unhappy.
- self._ensure_work_log_closed()
- self._output_tee.remove_log(self._queue_log)
-
- def _begin_logging(self):
- self._queue_log = self._output_tee.add_log(self._delegate.queue_log_path())
- self._work_log = None
-
- def _open_work_log(self, work_item):
- work_item_log_path = self._delegate.work_item_log_path(work_item)
- if not work_item_log_path:
- return
- self._work_log = self._output_tee.add_log(work_item_log_path)
-
- def _ensure_work_log_closed(self):
- # If we still have a bug log open, close it.
- if self._work_log:
- 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 = 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):
- log(self._sleep_message(message))
- self._wakeup_event.wait(self.seconds_to_sleep)
- self._wakeup_event.clear()
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py
deleted file mode 100644
index 37d8502..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py
+++ /dev/null
@@ -1,209 +0,0 @@
-# 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
-# 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 datetime
-import os
-import shutil
-import tempfile
-import threading
-import unittest
-
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.tool.bot.queueengine import QueueEngine, QueueEngineDelegate, TerminateQueue
-
-
-class LoggingDelegate(QueueEngineDelegate):
- def __init__(self, test):
- self._test = test
- self._callbacks = []
- self._run_before = False
- self.stop_message = None
-
- expected_callbacks = [
- 'queue_log_path',
- 'begin_work_queue',
- 'should_continue_work_queue',
- 'next_work_item',
- 'should_proceed_with_work_item',
- 'work_item_log_path',
- 'process_work_item',
- 'should_continue_work_queue',
- 'stop_work_queue',
- ]
-
- def record(self, method_name):
- self._callbacks.append(method_name)
-
- def queue_log_path(self):
- self.record("queue_log_path")
- return os.path.join(self._test.temp_dir, "queue_log_path")
-
- def work_item_log_path(self, work_item):
- self.record("work_item_log_path")
- return os.path.join(self._test.temp_dir, "work_log_path", "%s.log" % work_item)
-
- def begin_work_queue(self):
- self.record("begin_work_queue")
-
- def should_continue_work_queue(self):
- self.record("should_continue_work_queue")
- if not self._run_before:
- self._run_before = True
- return True
- return False
-
- def next_work_item(self):
- self.record("next_work_item")
- return "work_item"
-
- def should_proceed_with_work_item(self, work_item):
- self.record("should_proceed_with_work_item")
- self._test.assertEquals(work_item, "work_item")
- fake_patch = { 'bug_id' : 42 }
- return (True, "waiting_message", fake_patch)
-
- def process_work_item(self, work_item):
- self.record("process_work_item")
- self._test.assertEquals(work_item, "work_item")
- return True
-
- def handle_unexpected_error(self, work_item, message):
- self.record("handle_unexpected_error")
- self._test.assertEquals(work_item, "work_item")
-
- def stop_work_queue(self, message):
- self.record("stop_work_queue")
- self.stop_message = message
-
-
-class RaisingDelegate(LoggingDelegate):
- def __init__(self, test, exception):
- LoggingDelegate.__init__(self, test)
- self._exception = exception
-
- def process_work_item(self, work_item):
- self.record("process_work_item")
- raise self._exception
-
-
-class NotSafeToProceedDelegate(LoggingDelegate):
- def should_proceed_with_work_item(self, work_item):
- self.record("should_proceed_with_work_item")
- self._test.assertEquals(work_item, "work_item")
- return False
-
-
-class FastQueueEngine(QueueEngine):
- def __init__(self, delegate):
- QueueEngine.__init__(self, "fast-queue", delegate, threading.Event())
-
- # No sleep for the wicked.
- seconds_to_sleep = 0
-
- def _sleep(self, message):
- pass
-
-
-class QueueEngineTest(unittest.TestCase):
- def test_trivial(self):
- delegate = LoggingDelegate(self)
- self._run_engine(delegate)
- self.assertEquals(delegate.stop_message, "Delegate terminated queue.")
- self.assertEquals(delegate._callbacks, LoggingDelegate.expected_callbacks)
- self.assertTrue(os.path.exists(os.path.join(self.temp_dir, "queue_log_path")))
- self.assertTrue(os.path.exists(os.path.join(self.temp_dir, "work_log_path", "work_item.log")))
-
- def test_unexpected_error(self):
- delegate = RaisingDelegate(self, ScriptError(exit_code=3))
- self._run_engine(delegate)
- expected_callbacks = LoggingDelegate.expected_callbacks[:]
- work_item_index = expected_callbacks.index('process_work_item')
- # The unexpected error should be handled right after process_work_item starts
- # but before any other callback. Otherwise callbacks should be normal.
- expected_callbacks.insert(work_item_index + 1, 'handle_unexpected_error')
- self.assertEquals(delegate._callbacks, expected_callbacks)
-
- def test_handled_error(self):
- delegate = RaisingDelegate(self, ScriptError(exit_code=QueueEngine.handled_error_code))
- self._run_engine(delegate)
- self.assertEquals(delegate._callbacks, LoggingDelegate.expected_callbacks)
-
- def _run_engine(self, delegate, engine=None, termination_message=None):
- if not engine:
- engine = QueueEngine("test-queue", delegate, threading.Event())
- if not termination_message:
- termination_message = "Delegate terminated queue."
- expected_stderr = "\n%s\n" % termination_message
- OutputCapture().assert_outputs(self, engine.run, [], expected_stderr=expected_stderr)
-
- def _test_terminating_queue(self, exception, termination_message):
- work_item_index = LoggingDelegate.expected_callbacks.index('process_work_item')
- # The terminating error should be handled right after process_work_item.
- # There should be no other callbacks after stop_work_queue.
- expected_callbacks = LoggingDelegate.expected_callbacks[:work_item_index + 1]
- expected_callbacks.append("stop_work_queue")
-
- delegate = RaisingDelegate(self, exception)
- self._run_engine(delegate, termination_message=termination_message)
-
- self.assertEquals(delegate._callbacks, expected_callbacks)
- self.assertEquals(delegate.stop_message, termination_message)
-
- def test_terminating_error(self):
- self._test_terminating_queue(KeyboardInterrupt(), "User terminated queue.")
- self._test_terminating_queue(TerminateQueue(), "TerminateQueue exception received.")
-
- def test_not_safe_to_proceed(self):
- delegate = NotSafeToProceedDelegate(self)
- self._run_engine(delegate, engine=FastQueueEngine(delegate))
- expected_callbacks = LoggingDelegate.expected_callbacks[:]
- expected_callbacks.remove('work_item_log_path')
- expected_callbacks.remove('process_work_item')
- 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")
-
- def tearDown(self):
- shutil.rmtree(self.temp_dir)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/sheriff.py b/WebKitTools/Scripts/webkitpy/tool/bot/sheriff.py
deleted file mode 100644
index da506bc..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/bot/sheriff.py
+++ /dev/null
@@ -1,91 +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.
-
-from webkitpy.common.checkout.changelog import view_source_url
-from webkitpy.common.net.bugzilla import parse_bug_id
-from webkitpy.common.system.deprecated_logging import log
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.tool.grammar import join_with_separators
-
-
-class Sheriff(object):
- def __init__(self, tool, sheriffbot):
- self._tool = tool
- self._sheriffbot = sheriffbot
-
- def post_irc_warning(self, commit_info, builders):
- irc_nicknames = sorted([party.irc_nickname for
- party in commit_info.responsible_parties()
- if party.irc_nickname])
- irc_prefix = ": " if irc_nicknames else ""
- irc_message = "%s%s%s might have broken %s" % (
- ", ".join(irc_nicknames),
- irc_prefix,
- view_source_url(commit_info.revision()),
- join_with_separators([builder.name() for builder in builders]))
-
- self._tool.irc().post(irc_message)
-
- def post_rollout_patch(self, svn_revision, rollout_reason):
- # Ensure that svn_revision is a number (and not an option to
- # create-rollout).
- try:
- svn_revision = int(svn_revision)
- except:
- raise ScriptError(message="Invalid svn revision number \"%s\"."
- % svn_revision)
-
- if rollout_reason.startswith("-"):
- raise ScriptError(message="The rollout reason may not begin "
- "with - (\"%s\")." % rollout_reason)
-
- output = self._sheriffbot.run_webkit_patch([
- "create-rollout",
- "--force-clean",
- # In principle, we should pass --non-interactive here, but it
- # turns out that create-rollout doesn't need it yet. We can't
- # pass it prophylactically because we reject unrecognized command
- # line switches.
- "--parent-command=sheriff-bot",
- svn_revision,
- rollout_reason,
- ])
- return parse_bug_id(output)
-
- def post_blame_comment_on_bug(self, commit_info, builders, tests):
- if not commit_info.bug_id():
- return
- comment = "%s might have broken %s" % (
- view_source_url(commit_info.revision()),
- join_with_separators([builder.name() for builder in builders]))
- if tests:
- comment += "\nThe following tests are not passing:\n"
- comment += "\n".join(tests)
- self._tool.bugs.post_comment_to_bug(commit_info.bug_id(),
- comment,
- cc=self._sheriffbot.watchers)
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py
deleted file mode 100644
index 690af1f..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py
+++ /dev/null
@@ -1,90 +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.
-
-import os
-import unittest
-
-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
-
-
-class MockSheriffBot(object):
- name = "mock-sheriff-bot"
- watchers = [
- "watcher@example.com",
- ]
-
- def run_webkit_patch(self, args):
- return "Created bug https://bugs.webkit.org/show_bug.cgi?id=36936\n"
-
-
-class SheriffTest(unittest.TestCase):
- def test_post_blame_comment_on_bug(self):
- def run():
- sheriff = Sheriff(MockTool(), MockSheriffBot())
- builders = [
- Builder("Foo", None),
- Builder("Bar", None),
- ]
- commit_info = Mock()
- commit_info.bug_id = lambda: None
- commit_info.revision = lambda: 4321
- # Should do nothing with no bug_id
- sheriff.post_blame_comment_on_bug(commit_info, builders, [])
- sheriff.post_blame_comment_on_bug(commit_info, builders, ["mock-test-1", "mock-test-2"])
- # Should try to post a comment to the bug, but MockTool.bugs does nothing.
- commit_info.bug_id = lambda: 1234
- sheriff.post_blame_comment_on_bug(commit_info, builders, [])
- sheriff.post_blame_comment_on_bug(commit_info, builders, ["mock-test-1"])
- sheriff.post_blame_comment_on_bug(commit_info, builders, ["mock-test-1", "mock-test-2"])
-
- expected_stderr = u"""MOCK bug comment: bug_id=1234, cc=['watcher@example.com']
---- Begin comment ---
-http://trac.webkit.org/changeset/4321 might have broken Foo and Bar
---- End comment ---
-
-MOCK bug comment: bug_id=1234, cc=['watcher@example.com']
---- Begin comment ---
-http://trac.webkit.org/changeset/4321 might have broken Foo and Bar
-The following tests are not passing:
-mock-test-1
---- End comment ---
-
-MOCK bug comment: bug_id=1234, cc=['watcher@example.com']
---- Begin comment ---
-http://trac.webkit.org/changeset/4321 might have broken Foo and Bar
-The following tests are not passing:
-mock-test-1
-mock-test-2
---- End comment ---
-
-"""
- OutputCapture().assert_outputs(self, run, expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot.py b/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot.py
deleted file mode 100644
index de77222..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot.py
+++ /dev/null
@@ -1,83 +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.
-
-import webkitpy.tool.bot.irc_command as irc_command
-
-from webkitpy.common.net.irc.ircbot import IRCBotDelegate
-from webkitpy.common.thread.threadedmessagequeue import ThreadedMessageQueue
-
-
-class _IRCThreadTearoff(IRCBotDelegate):
- def __init__(self, password, message_queue, wakeup_event):
- self._password = password
- self._message_queue = message_queue
- self._wakeup_event = wakeup_event
-
- # IRCBotDelegate methods
-
- def irc_message_received(self, nick, message):
- self._message_queue.post([nick, message])
- self._wakeup_event.set()
-
- def irc_nickname(self):
- return "sheriffbot"
-
- def irc_password(self):
- return self._password
-
-
-class SheriffIRCBot(object):
- def __init__(self, tool, sheriff):
- self._tool = tool
- self._sheriff = sheriff
- self._message_queue = ThreadedMessageQueue()
-
- def irc_delegate(self):
- return _IRCThreadTearoff(self._tool.irc_password,
- self._message_queue,
- self._tool.wakeup_event)
-
- def process_message(self, message):
- (nick, request) = message
- tokenized_request = request.strip().split(" ")
- if not tokenized_request:
- return
- command = irc_command.commands.get(tokenized_request[0])
- args = tokenized_request[1:]
- if not command:
- # Give the peoples someone to talk with.
- command = irc_command.Eliza
- args = tokenized_request
- response = command().execute(nick, args, self._tool, self._sheriff)
- if response:
- self._tool.irc().post(response)
-
- def process_pending_messages(self):
- (messages, is_running) = self._message_queue.take_all()
- for message in messages:
- self.process_message(message)
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py
deleted file mode 100644
index 08023bd..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py
+++ /dev/null
@@ -1,95 +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.
-
-import unittest
-import random
-
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.tool.bot.sheriff import Sheriff
-from webkitpy.tool.bot.sheriffircbot import SheriffIRCBot
-from webkitpy.tool.bot.sheriff_unittest import MockSheriffBot
-from webkitpy.tool.mocktool import MockTool
-
-
-def run(message):
- tool = MockTool()
- tool.ensure_irc_connected(None)
- bot = SheriffIRCBot(tool, Sheriff(tool, MockSheriffBot()))
- bot._message_queue.post(["mock_nick", message])
- bot.process_pending_messages()
-
-
-class SheriffIRCBotTest(unittest.TestCase):
- def test_hi(self):
- random.seed(23324)
- expected_stderr = 'MOCK: irc.post: "Only you can prevent forest fires." -- Smokey the Bear\n'
- OutputCapture().assert_outputs(self, run, args=["hi"], expected_stderr=expected_stderr)
-
- def test_help(self):
- expected_stderr = "MOCK: irc.post: mock_nick: Available commands: rollout, hi, help, restart, last-green-revision\n"
- OutputCapture().assert_outputs(self, run, args=["help"], expected_stderr=expected_stderr)
-
- def test_lgr(self):
- expected_stderr = "MOCK: irc.post: mock_nick: http://trac.webkit.org/changeset/9479\n"
- OutputCapture().assert_outputs(self, run, args=["last-green-revision"], expected_stderr=expected_stderr)
-
- def test_rollout(self):
- expected_stderr = "MOCK: irc.post: Preparing rollout for r21654...\nMOCK: irc.post: mock_nick: Created rollout: http://example.com/36936\n"
- OutputCapture().assert_outputs(self, run, args=["rollout 21654 This patch broke the world"], expected_stderr=expected_stderr)
-
- def test_rollout_with_r_in_svn_revision(self):
- expected_stderr = "MOCK: irc.post: Preparing rollout for r21654...\nMOCK: irc.post: mock_nick: Created rollout: http://example.com/36936\n"
- OutputCapture().assert_outputs(self, run, args=["rollout r21654 This patch broke the world"], expected_stderr=expected_stderr)
-
- def test_rollout_bananas(self):
- expected_stderr = "MOCK: irc.post: mock_nick: Usage: SVN_REVISION REASON\n"
- OutputCapture().assert_outputs(self, run, args=["rollout bananas"], expected_stderr=expected_stderr)
-
- def test_rollout_invalidate_revision(self):
- expected_stderr = ("MOCK: irc.post: Preparing rollout for r--component=Tools...\n"
- "MOCK: irc.post: mock_nick: Failed to create rollout patch:\n"
- "MOCK: irc.post: Invalid svn revision number \"--component=Tools\".\n")
- OutputCapture().assert_outputs(self, run,
- args=["rollout "
- "--component=Tools 21654"],
- expected_stderr=expected_stderr)
-
- def test_rollout_invalidate_reason(self):
- expected_stderr = ("MOCK: irc.post: Preparing rollout for "
- "r21654...\nMOCK: irc.post: mock_nick: Failed to "
- "create rollout patch:\nMOCK: irc.post: The rollout"
- " reason may not begin with - (\"-bad (Requested "
- "by mock_nick on #webkit).\").\n")
- OutputCapture().assert_outputs(self, run,
- args=["rollout "
- "21654 -bad"],
- expected_stderr=expected_stderr)
-
- def test_rollout_no_reason(self):
- expected_stderr = "MOCK: irc.post: mock_nick: Usage: SVN_REVISION REASON\n"
- OutputCapture().assert_outputs(self, run, args=["rollout 21654"], expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/__init__.py b/WebKitTools/Scripts/webkitpy/tool/commands/__init__.py
deleted file mode 100644
index d2aa503..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/__init__.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Required for Python to search this directory for module files
-
-from webkitpy.tool.commands.download import *
-from webkitpy.tool.commands.earlywarningsystem import *
-from webkitpy.tool.commands.openbugs import OpenBugs
-from webkitpy.tool.commands.prettydiff import PrettyDiff
-from webkitpy.tool.commands.queries import *
-from webkitpy.tool.commands.queues import *
-from webkitpy.tool.commands.rebaseline import Rebaseline
-from webkitpy.tool.commands.rebaselineserver import RebaselineServer
-from webkitpy.tool.commands.sheriffbot import *
-from webkitpy.tool.commands.upload import *
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/abstractsequencedcommand.py b/WebKitTools/Scripts/webkitpy/tool/commands/abstractsequencedcommand.py
deleted file mode 100644
index fc5a794..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/abstractsequencedcommand.py
+++ /dev/null
@@ -1,43 +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.
-
-from webkitpy.tool.commands.stepsequence import StepSequence
-from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand
-
-
-class AbstractSequencedCommand(AbstractDeclarativeCommand):
- steps = None
- def __init__(self):
- self._sequence = StepSequence(self.steps)
- AbstractDeclarativeCommand.__init__(self, self._sequence.options())
-
- def _prepare_state(self, options, args, tool):
- return None
-
- def execute(self, options, args, tool):
- self._sequence.run_and_handle_errors(tool, options, self._prepare_state(options, args, tool))
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/commandtest.py b/WebKitTools/Scripts/webkitpy/tool/commands/commandtest.py
deleted file mode 100644
index adc6d81..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/commandtest.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# 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
-# 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 unittest
-
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.tool.mocktool import MockOptions, MockTool
-
-class CommandsTest(unittest.TestCase):
- def assert_execute_outputs(self, command, args, expected_stdout="", expected_stderr="", options=MockOptions(), tool=MockTool()):
- options.blocks = True
- options.cc = 'MOCK cc'
- options.component = 'MOCK component'
- options.confirm = True
- options.email = 'MOCK email'
- options.git_commit = 'MOCK git commit'
- options.obsolete_patches = True
- options.open_bug = True
- options.port = 'MOCK port'
- options.quiet = True
- options.reviewer = 'MOCK reviewer'
- command.bind_to_tool(tool)
- OutputCapture().assert_outputs(self, command.execute, [options, args, tool], expected_stdout=expected_stdout, expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html
deleted file mode 100644
index 5b58301..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/index.html
+++ /dev/null
@@ -1,180 +0,0 @@
-<!DOCTYPE html>
-<!--
- 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.
--->
-<html>
-<head>
- <title>Layout Test Rebaseline Server</title>
- <link rel="stylesheet" href="/main.css" type="text/css">
- <script src="/util.js"></script>
- <script src="/loupe.js"></script>
- <script src="/main.js"></script>
- <script src="/queue.js"></script>
-</head>
-<body class="loading">
-
-<pre id="log" style="display: none"></pre>
-<div id="queue" style="display: none">
- Queue:
- <select id="queue-select" size="10"></select>
- <button id="remove-queue-selection">Remove selection</button>
- <button id="rebaseline-queue">Rebaseline queue</button>
-</div>
-
-<div id="header">
- <div id="controls">
- <!-- Add a dummy <select> node so that this lines up with the text on the left -->
- <select style="visibility: hidden"></select>
- <span id="toggle-log" class="link">Log</span>
- <span class="divider">|</span>
- <a href="/quitquitquit">Exit</a>
- </div>
-
- <span id="selectors">
- <label>
- Failure type:
- <select id="failure-type-selector"></select>
- </label>
-
- <label>
- Directory:
- <select id="directory-selector"></select>
- </label>
-
- <label>
- Test:
- <select id="test-selector"></select>
- </label>
- </span>
-
- <a id="test-link" target="_blank">View test</a>
-
- <span id="nav-buttons">
- <button id="previous-test">&laquo;</button>
- <span id="test-index"></span> of <span id="test-count"></span>
- <button id="next-test">&raquo;</button>
- </span>
-</div>
-
-<table id="test-output">
- <thead id="labels">
- <tr>
- <th>Expected</th>
- <th>Actual</th>
- <th>Diff</th>
- </tr>
- </thead>
- <tbody id="image-outputs" style="display: none">
- <tr>
- <td colspan="3"><h2>Image</h2></td>
- </tr>
- <tr>
- <td><img id="expected-image"></td>
- <td><img id="actual-image"></td>
- <td>
- <canvas id="diff-canvas" width="800" height="600"></canvas>
- <div id="diff-checksum" style="display: none">
- <h3>Checksum mismatch</h3>
- Expected: <span id="expected-checksum"></span><br>
- Actual: <span id="actual-checksum"></span>
- </div>
- </td>
- </tr>
- </tbody>
- <tbody id="text-outputs" style="display: none">
- <tr>
- <td colspan="3"><h2>Text</h2></td>
- </tr>
- <tr>
- <td><pre id="expected-text"></pre></td>
- <td><pre id="actual-text"></pre></td>
- <td><pre id="diff-text"><pre></td>
- </tr>
- </tbody>
-</table>
-
-<div id="footer">
- <label>State: <span id="state"></span></label>
- <label>Existing baselines: <span id="current-baselines"></span></label>
- <label>
- Baseline target:
- <select id="baseline-target"></select>
- </label>
- <label>
- Move current baselines to:
- <select id="baseline-move-to">
- <option value="none">Nowhere (replace)</option>
- </select>
- </label>
-
- <!-- Add a dummy <button> node so that this lines up with the text on the right -->
- <button style="visibility: hidden; padding-left: 0; padding-right: 0;"></button>
-
- <div id="action-buttons">
- <span id="toggle-queue" class="link">Queue</span>
- <button id="add-to-rebaseline-queue">Add to rebaseline queue</button>
- </div>
-</div>
-
-<table id="loupe" style="display: none">
- <tr>
- <td colspan="3" id="loupe-info">
- <span id="loupe-close" class="link">Close</span>
- <label>Coordinate: <span id="loupe-coordinate"></span></label>
- </td>
- </tr>
- <tr>
- <td>
- <div class="loupe-container">
- <canvas id="expected-loupe" width="210" height="210"></canvas>
- <div class="center-highlight"></div>
- </div>
- </td>
- <td>
- <div class="loupe-container">
- <canvas id="actual-loupe" width="210" height="210"></canvas>
- <div class="center-highlight"></div>
- </div>
- </td>
- <td>
- <div class="loupe-container">
- <canvas id="diff-loupe" width="210" height="210"></canvas>
- <div class="center-highlight"></div>
- </div>
- </td>
- </tr>
- <tr id="loupe-colors">
- <td><label>Exp. color: <span id="expected-loupe-color"></span></label></td>
- <td><label>Actual color: <span id="actual-loupe-color"></span></label></td>
- <td><label>Diff color: <span id="diff-loupe-color"></span></label></td>
- </tr>
-</table>
-
-</body>
-</html>
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/loupe.js b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/loupe.js
deleted file mode 100644
index 41f977a..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/loupe.js
+++ /dev/null
@@ -1,144 +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.
- */
-
-var LOUPE_MAGNIFICATION_FACTOR = 10;
-
-function Loupe()
-{
- this._node = $('loupe');
- this._currentCornerX = -1;
- this._currentCornerY = -1;
-
- var self = this;
-
- function handleOutputClick(event) { self._handleOutputClick(event); }
- $('expected-image').addEventListener('click', handleOutputClick);
- $('actual-image').addEventListener('click', handleOutputClick);
- $('diff-canvas').addEventListener('click', handleOutputClick);
-
- function handleLoupeClick(event) { self._handleLoupeClick(event); }
- $('expected-loupe').addEventListener('click', handleLoupeClick);
- $('actual-loupe').addEventListener('click', handleLoupeClick);
- $('diff-loupe').addEventListener('click', handleLoupeClick);
-
- function hide(event) { self.hide(); }
- $('loupe-close').addEventListener('click', hide);
-}
-
-Loupe.prototype._handleOutputClick = function(event)
-{
- // The -1 compensates for the border around the image/canvas.
- this._showFor(event.offsetX - 1, event.offsetY - 1);
-};
-
-Loupe.prototype._handleLoupeClick = function(event)
-{
- var deltaX = Math.floor(event.offsetX/LOUPE_MAGNIFICATION_FACTOR);
- var deltaY = Math.floor(event.offsetY/LOUPE_MAGNIFICATION_FACTOR);
-
- this._showFor(
- this._currentCornerX + deltaX, this._currentCornerY + deltaY);
-}
-
-Loupe.prototype.hide = function()
-{
- this._node.style.display = 'none';
-};
-
-Loupe.prototype._showFor = function(x, y)
-{
- this._fillFromImage(x, y, 'expected', $('expected-image'));
- this._fillFromImage(x, y, 'actual', $('actual-image'));
- this._fillFromCanvas(x, y, 'diff', $('diff-canvas'));
-
- this._node.style.display = '';
-};
-
-Loupe.prototype._fillFromImage = function(x, y, type, sourceImage)
-{
- var tempCanvas = document.createElement('canvas');
- tempCanvas.width = sourceImage.width;
- tempCanvas.height = sourceImage.height;
- var tempContext = tempCanvas.getContext('2d');
-
- tempContext.drawImage(sourceImage, 0, 0);
-
- this._fillFromCanvas(x, y, type, tempCanvas);
-};
-
-Loupe.prototype._fillFromCanvas = function(x, y, type, canvas)
-{
- var context = canvas.getContext('2d');
- var sourceImageData =
- context.getImageData(0, 0, canvas.width, canvas.height);
-
- var targetCanvas = $(type + '-loupe');
- var targetContext = targetCanvas.getContext('2d');
- targetContext.fillStyle = 'rgba(255, 255, 255, 1)';
- targetContext.fillRect(0, 0, targetCanvas.width, targetCanvas.height);
-
- var sourceXOffset = (targetCanvas.width/LOUPE_MAGNIFICATION_FACTOR - 1)/2;
- var sourceYOffset = (targetCanvas.height/LOUPE_MAGNIFICATION_FACTOR - 1)/2;
-
- function readPixelComponent(x, y, component) {
- var offset = (y * sourceImageData.width + x) * 4 + component;
- return sourceImageData.data[offset];
- }
-
- for (var i = -sourceXOffset; i <= sourceXOffset; i++) {
- for (var j = -sourceYOffset; j <= sourceYOffset; j++) {
- var sourceX = x + i;
- var sourceY = y + j;
-
- var sourceR = readPixelComponent(sourceX, sourceY, 0);
- var sourceG = readPixelComponent(sourceX, sourceY, 1);
- var sourceB = readPixelComponent(sourceX, sourceY, 2);
- var sourceA = readPixelComponent(sourceX, sourceY, 3)/255;
- sourceA = Math.round(sourceA * 10)/10;
-
- var targetX = (i + sourceXOffset) * LOUPE_MAGNIFICATION_FACTOR;
- var targetY = (j + sourceYOffset) * LOUPE_MAGNIFICATION_FACTOR;
- var colorString =
- sourceR + ', ' + sourceG + ', ' + sourceB + ', ' + sourceA;
- targetContext.fillStyle = 'rgba(' + colorString + ')';
- targetContext.fillRect(
- targetX, targetY,
- LOUPE_MAGNIFICATION_FACTOR, LOUPE_MAGNIFICATION_FACTOR);
-
- if (i == 0 && j == 0) {
- $('loupe-coordinate').textContent = sourceX + ', ' + sourceY;
- $(type + '-loupe-color').textContent = colorString;
- }
- }
- }
-
- this._currentCornerX = x - sourceXOffset;
- this._currentCornerY = y - sourceYOffset;
-};
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css
deleted file mode 100644
index 62afda6..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.css
+++ /dev/null
@@ -1,292 +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.
- */
-
-body {
- font-size: 12px;
- font-family: Helvetica, Arial, sans-serif;
- padding: 0;
- margin: 0;
-}
-
-.loading {
- opacity: 0.5;
-}
-
-div {
- margin: 0;
-}
-
-a, .link {
- color: #aaf;
- text-decoration: underline;
- cursor: pointer;
-}
-
-.link.selected {
- color: #fff;
- font-weight: bold;
- text-decoration: none;
-}
-
-#log,
-#queue {
- padding: .25em 0 0 .25em;
- position: absolute;
- right: 0;
- height: 200px;
- overflow: auto;
- background: #fff;
- -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, .5);
-}
-
-#log {
- top: 2em;
- width: 500px;
-}
-
-#queue {
- bottom: 3em;
- width: 400px;
-}
-
-#queue-select {
- display: block;
- width: 390px;
-}
-
-#header,
-#footer {
- padding: .5em 1em;
- background: #333;
- color: #fff;
- -webkit-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.5);
-}
-
-#header {
- margin-bottom: 1em;
-}
-
-#header .divider,
-#footer .divider {
- opacity: .3;
- padding: 0 .5em;
-}
-
-#header label,
-#footer label {
- padding-right: 1em;
- color: #ccc;
-}
-
-#test-link {
- margin-right: 1em;
-}
-
-#header label span,
-#footer label span {
- color: #fff;
- font-weight: bold;
-}
-
-#nav-buttons {
- white-space: nowrap;
-}
-
-#nav-buttons button {
- background: #fff;
- border: 0;
- border-radius: 10px;
-}
-
-#nav-buttons button:active {
- -webkit-box-shadow: 0 0 5px #33f inset;
- background: #aaa;
-}
-
-#nav-buttons button[disabled] {
- opacity: .5;
-}
-
-#controls {
- float: right;
-}
-
-#test-output {
- border-spacing: 0;
- border-collapse: collapse;
- margin: 0 auto;
- width: 100%;
-}
-
-#test-output td,
-#test-output th {
- padding: 0;
- vertical-align: top;
-}
-
-#image-outputs img,
-#image-outputs canvas,
-#image-outputs #diff-checksum {
- width: 800px;
- height: 600px;
- border: solid 1px #ddd;
- -webkit-user-select: none;
- -webkit-user-drag: none;
-}
-
-#image-outputs img,
-#image-outputs canvas {
- cursor: crosshair;
-}
-
-#image-outputs img.loading,
-#image-outputs canvas.loading {
- opacity: .5;
-}
-
-#image-outputs #actual-image {
- margin: 0 1em;
-}
-
-#test-output #labels th {
- text-align: center;
- color: #666;
-}
-
-#text-outputs pre {
- height: 600px;
- width: 800px;
- overflow: auto;
-}
-
-#test-output h2 {
- border-bottom: solid 1px #ccc;
- font-weight: bold;
- margin: 0;
- background: #eee;
-}
-
-#footer {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- margin-top: 1em;
-}
-
-#state.needs_rebaseline {
- color: yellow;
-}
-
-#state.rebaseline_failed {
- color: red;
-}
-
-#state.rebaseline_succeeded {
- color: green;
-}
-
-#state.in_queue {
- color: gray;
-}
-
-#action-buttons {
- float: right;
-}
-
-#action-buttons .link {
- margin-right: 1em;
-}
-
-#footer button {
- padding: 1em;
-}
-
-#loupe {
- -webkit-box-shadow: 2px 2px 5px rgba(0, 0, 0, .5);
- position: absolute;
- width: 634px;
- top: 50%;
- left: 50%;
- margin-left: -151px;
- margin-top: -50px;
- background: #fff;
- border-spacing: 0;
- border-collapse: collapse;
-}
-
-#loupe td {
- padding: 0;
- border: solid 1px #ccc;
-}
-
-#loupe label {
- color: #999;
- padding-right: 1em;
-}
-
-#loupe span {
- color: #000;
- font-weight: bold;
-}
-
-#loupe canvas {
- cursor: crosshair;
-}
-
-#loupe #loupe-close {
- float: right;
-}
-
-#loupe #loupe-info {
- background: #eee;
- padding: .3em .5em;
-}
-
-#loupe #loupe-colors td {
- text-align: center;
-}
-
-#loupe .loupe-container {
- position: relative;
- width: 210px;
- height: 210px;
-}
-
-#loupe .center-highlight {
- position: absolute;
- width: 10px;
- height: 10px;
- top: 50%;
- left: 50%;
- margin-left: -5px;
- margin-top: -5px;
- outline: solid 1px #999;
-}
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js
deleted file mode 100644
index 25f9146..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/main.js
+++ /dev/null
@@ -1,498 +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.
- */
-
-var ALL_DIRECTORY_PATH = '[all]';
-
-var STATE_NEEDS_REBASELINE = 'needs_rebaseline';
-var STATE_REBASELINE_FAILED = 'rebaseline_failed';
-var STATE_REBASELINE_SUCCEEDED = 'rebaseline_succeeded';
-var STATE_IN_QUEUE = 'in_queue';
-var STATE_TO_DISPLAY_STATE = {};
-STATE_TO_DISPLAY_STATE[STATE_NEEDS_REBASELINE] = 'Needs rebaseline';
-STATE_TO_DISPLAY_STATE[STATE_REBASELINE_FAILED] = 'Rebaseline failed';
-STATE_TO_DISPLAY_STATE[STATE_REBASELINE_SUCCEEDED] = 'Rebaseline succeeded';
-STATE_TO_DISPLAY_STATE[STATE_IN_QUEUE] = 'In queue';
-
-var results;
-var testsByFailureType = {};
-var testsByDirectory = {};
-var selectedTests = [];
-var loupe;
-var queue;
-
-function main()
-{
- $('failure-type-selector').addEventListener('change', selectFailureType);
- $('directory-selector').addEventListener('change', selectDirectory);
- $('test-selector').addEventListener('change', selectTest);
- $('next-test').addEventListener('click', nextTest);
- $('previous-test').addEventListener('click', previousTest);
-
- $('toggle-log').addEventListener('click', function() { toggle('log'); });
-
- loupe = new Loupe();
- queue = new RebaselineQueue();
-
- document.addEventListener('keydown', function(event) {
- if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
- return;
- }
-
- switch (event.keyIdentifier) {
- case 'Left':
- event.preventDefault();
- previousTest();
- break;
- case 'Right':
- event.preventDefault();
- nextTest();
- break;
- case 'U+0051': // q
- queue.addCurrentTest();
- break;
- case 'U+0058': // x
- queue.removeCurrentTest();
- break;
- case 'U+0052': // r
- queue.rebaseline();
- break;
- }
- });
-
- loadText('/platforms.json', function(text) {
- var platforms = JSON.parse(text);
- platforms.platforms.forEach(function(platform) {
- var platformOption = document.createElement('option');
- platformOption.value = platform;
- platformOption.textContent = platform;
-
- var targetOption = platformOption.cloneNode(true);
- targetOption.selected = platform == platforms.defaultPlatform;
- $('baseline-target').appendChild(targetOption);
- $('baseline-move-to').appendChild(platformOption.cloneNode(true));
- });
- });
-
- loadText('/results.json', function(text) {
- results = JSON.parse(text);
- displayResults();
- });
-}
-
-/**
- * Groups test results by failure type.
- */
-function displayResults()
-{
- var failureTypeSelector = $('failure-type-selector');
- var failureTypes = [];
-
- for (var testName in results.tests) {
- var test = results.tests[testName];
- if (test.actual == 'PASS') {
- continue;
- }
- var failureType = test.actual + ' (expected ' + test.expected + ')';
- if (!(failureType in testsByFailureType)) {
- testsByFailureType[failureType] = [];
- failureTypes.push(failureType);
- }
- testsByFailureType[failureType].push(testName);
- }
-
- // Sort by number of failures
- failureTypes.sort(function(a, b) {
- return testsByFailureType[b].length - testsByFailureType[a].length;
- });
-
- for (var i = 0, failureType; failureType = failureTypes[i]; i++) {
- var failureTypeOption = document.createElement('option');
- failureTypeOption.value = failureType;
- failureTypeOption.textContent = failureType + ' - ' + testsByFailureType[failureType].length + ' tests';
- failureTypeSelector.appendChild(failureTypeOption);
- }
-
- selectFailureType();
-
- document.body.classList.remove('loading');
-}
-
-/**
- * For a given failure type, gets all the tests and groups them by directory
- * (populating the directory selector with them).
- */
-function selectFailureType()
-{
- var selectedFailureType = getSelectValue('failure-type-selector');
- var tests = testsByFailureType[selectedFailureType];
-
- testsByDirectory = {}
- var displayDirectoryNamesByDirectory = {};
- var directories = [];
-
- // Include a special option for all tests
- testsByDirectory[ALL_DIRECTORY_PATH] = tests;
- displayDirectoryNamesByDirectory[ALL_DIRECTORY_PATH] = 'all';
- directories.push(ALL_DIRECTORY_PATH);
-
- // Roll up tests by ancestor directories
- tests.forEach(function(test) {
- var pathPieces = test.split('/');
- var pathDirectories = pathPieces.slice(0, pathPieces.length -1);
- var ancestorDirectory = '';
-
- pathDirectories.forEach(function(pathDirectory, index) {
- ancestorDirectory += pathDirectory + '/';
- if (!(ancestorDirectory in testsByDirectory)) {
- testsByDirectory[ancestorDirectory] = [];
- var displayDirectoryName = new Array(index * 6).join('&nbsp;') + pathDirectory;
- displayDirectoryNamesByDirectory[ancestorDirectory] = displayDirectoryName;
- directories.push(ancestorDirectory);
- }
-
- testsByDirectory[ancestorDirectory].push(test);
- });
- });
-
- directories.sort();
-
- var directorySelector = $('directory-selector');
- directorySelector.innerHTML = '';
-
- directories.forEach(function(directory) {
- var directoryOption = document.createElement('option');
- directoryOption.value = directory;
- directoryOption.innerHTML =
- displayDirectoryNamesByDirectory[directory] + ' - ' +
- testsByDirectory[directory].length + ' tests';
- directorySelector.appendChild(directoryOption);
- });
-
- selectDirectory();
-}
-
-/**
- * For a given failure type and directory and failure type, gets all the tests
- * in that directory and populatest the test selector with them.
- */
-function selectDirectory()
-{
- var selectedDirectory = getSelectValue('directory-selector');
- selectedTests = testsByDirectory[selectedDirectory];
-
- selectedTests.sort();
-
- var testSelector = $('test-selector');
- testSelector.innerHTML = '';
-
- selectedTests.forEach(function(testName) {
- var testOption = document.createElement('option');
- testOption.value = testName;
- var testDisplayName = testName;
- if (testName.lastIndexOf(selectedDirectory) == 0) {
- testDisplayName = testName.substring(selectedDirectory.length);
- }
- testOption.innerHTML = '&nbsp;&nbsp;' + testDisplayName;
- testSelector.appendChild(testOption);
- });
-
- selectTest();
-}
-
-function getSelectedTest()
-{
- return getSelectValue('test-selector');
-}
-
-function selectTest()
-{
- var selectedTest = getSelectedTest();
-
- if (results.tests[selectedTest].actual.indexOf('IMAGE') != -1) {
- $('image-outputs').style.display = '';
- displayImageResults(selectedTest);
- } else {
- $('image-outputs').style.display = 'none';
- }
-
- if (results.tests[selectedTest].actual.indexOf('TEXT') != -1) {
- $('text-outputs').style.display = '';
- displayTextResults(selectedTest);
- } else {
- $('text-outputs').style.display = 'none';
- }
-
- var currentBaselines = $('current-baselines');
- currentBaselines.textContent = '';
- var baselines = results.tests[selectedTest].baselines;
- var testName = selectedTest.split('.').slice(0, -1).join('.');
- for (var platform in baselines) {
- if (currentBaselines.firstChild) {
- currentBaselines.appendChild(document.createTextNode('; '));
- }
- currentBaselines.appendChild(document.createTextNode(platform + ' ('));
- for (var i = 0, extension; extension = baselines[platform][i]; i++) {
- if (i != 0) {
- currentBaselines.appendChild(document.createTextNode(', '));
- }
- var link = document.createElement('a');
- var baselinePath = '';
- if (platform != 'base') {
- baselinePath += 'platform/' + platform + '/';
- }
- baselinePath += testName + '-expected' + extension;
- link.href = getTracUrl(baselinePath);
- if (extension == '.checksum') {
- link.textContent = 'chk';
- } else {
- link.textContent = extension.substring(1);
- }
- link.target = '_blank';
- currentBaselines.appendChild(link);
- }
- currentBaselines.appendChild(document.createTextNode(')'));
- }
-
- updateState();
- loupe.hide();
-
- prefetchNextImageTest();
-}
-
-function prefetchNextImageTest()
-{
- var testSelector = $('test-selector');
- if (testSelector.selectedIndex == testSelector.options.length - 1) {
- return;
- }
- var nextTest = testSelector.options[testSelector.selectedIndex + 1].value;
- if (results.tests[nextTest].actual.indexOf('IMAGE') != -1) {
- new Image().src = getTestResultUrl(nextTest, 'expected-image');
- new Image().src = getTestResultUrl(nextTest, 'actual-image');
- }
-}
-
-function updateState()
-{
- var testName = getSelectedTest();
- var testIndex = selectedTests.indexOf(testName);
- var testCount = selectedTests.length
- $('test-index').textContent = testIndex + 1;
- $('test-count').textContent = testCount;
-
- $('next-test').disabled = testIndex == testCount - 1;
- $('previous-test').disabled = testIndex == 0;
-
- $('test-link').href = getTracUrl(testName);
-
- var state = results.tests[testName].state;
- $('state').className = state;
- $('state').innerHTML = STATE_TO_DISPLAY_STATE[state];
-
- queue.updateState();
-}
-
-function getTestResultUrl(testName, mode)
-{
- return '/test_result?test=' + testName + '&mode=' + mode;
-}
-
-var currentExpectedImageTest;
-var currentActualImageTest;
-
-function displayImageResults(testName)
-{
- if (currentExpectedImageTest == currentActualImageTest
- && currentExpectedImageTest == testName) {
- return;
- }
-
- function displayImageResult(mode, callback) {
- var image = $(mode);
- image.className = 'loading';
- image.src = getTestResultUrl(testName, mode);
- image.onload = function() {
- image.className = '';
- callback();
- updateImageDiff();
- };
- }
-
- displayImageResult(
- 'expected-image',
- function() { currentExpectedImageTest = testName; });
- displayImageResult(
- 'actual-image',
- function() { currentActualImageTest = testName; });
-
- $('diff-canvas').className = 'loading';
- $('diff-canvas').style.display = '';
- $('diff-checksum').style.display = 'none';
-}
-
-/**
- * Computes a graphical a diff between the expected and actual images by
- * rendering each to a canvas, getting the image data, and comparing the RGBA
- * components of each pixel. The output is put into the diff canvas, with
- * identical pixels appearing at 12.5% opacity and different pixels being
- * highlighted in red.
- */
-function updateImageDiff() {
- if (currentExpectedImageTest != currentActualImageTest)
- return;
-
- var expectedImage = $('expected-image');
- var actualImage = $('actual-image');
-
- function getImageData(image) {
- var imageCanvas = document.createElement('canvas');
- imageCanvas.width = image.width;
- imageCanvas.height = image.height;
- imageCanvasContext = imageCanvas.getContext('2d');
-
- imageCanvasContext.fillStyle = 'rgba(255, 255, 255, 1)';
- imageCanvasContext.fillRect(
- 0, 0, image.width, image.height);
-
- imageCanvasContext.drawImage(image, 0, 0);
- return imageCanvasContext.getImageData(
- 0, 0, image.width, image.height);
- }
-
- var expectedImageData = getImageData(expectedImage);
- var actualImageData = getImageData(actualImage);
-
- var diffCanvas = $('diff-canvas');
- var diffCanvasContext = diffCanvas.getContext('2d');
- var diffImageData =
- diffCanvasContext.createImageData(diffCanvas.width, diffCanvas.height);
-
- // Avoiding property lookups for all these during the per-pixel loop below
- // provides a significant performance benefit.
- var expectedWidth = expectedImage.width;
- var expectedHeight = expectedImage.height;
- var expected = expectedImageData.data;
-
- var actualWidth = actualImage.width;
- var actual = actualImageData.data;
-
- var diffWidth = diffImageData.width;
- var diff = diffImageData.data;
-
- var hadDiff = false;
- for (var x = 0; x < expectedWidth; x++) {
- for (var y = 0; y < expectedHeight; y++) {
- var expectedOffset = (y * expectedWidth + x) * 4;
- var actualOffset = (y * actualWidth + x) * 4;
- var diffOffset = (y * diffWidth + x) * 4;
- if (expected[expectedOffset] != actual[actualOffset] ||
- expected[expectedOffset + 1] != actual[actualOffset + 1] ||
- expected[expectedOffset + 2] != actual[actualOffset + 2] ||
- expected[expectedOffset + 3] != actual[actualOffset + 3]) {
- hadDiff = true;
- diff[diffOffset] = 255;
- diff[diffOffset + 1] = 0;
- diff[diffOffset + 2] = 0;
- diff[diffOffset + 3] = 255;
- } else {
- diff[diffOffset] = expected[expectedOffset];
- diff[diffOffset + 1] = expected[expectedOffset + 1];
- diff[diffOffset + 2] = expected[expectedOffset + 2];
- diff[diffOffset + 3] = 32;
- }
- }
- }
-
- diffCanvasContext.putImageData(
- diffImageData,
- 0, 0,
- 0, 0,
- diffImageData.width, diffImageData.height);
- diffCanvas.className = '';
-
- if (!hadDiff) {
- diffCanvas.style.display = 'none';
- $('diff-checksum').style.display = '';
- loadTextResult(currentExpectedImageTest, 'expected-checksum');
- loadTextResult(currentExpectedImageTest, 'actual-checksum');
- }
-}
-
-function loadTextResult(testName, mode)
-{
- loadText(getTestResultUrl(testName, mode), function(text) {
- $(mode).textContent = text;
- });
-}
-
-function displayTextResults(testName)
-{
- loadTextResult(testName, 'expected-text');
- loadTextResult(testName, 'actual-text');
- loadTextResult(testName, 'diff-text');
-}
-
-function nextTest()
-{
- var testSelector = $('test-selector');
- var nextTestIndex = testSelector.selectedIndex + 1;
- while (true) {
- if (nextTestIndex == testSelector.options.length) {
- return;
- }
- if (testSelector.options[nextTestIndex].disabled) {
- nextTestIndex++;
- } else {
- testSelector.selectedIndex = nextTestIndex;
- selectTest();
- return;
- }
- }
-}
-
-function previousTest()
-{
- var testSelector = $('test-selector');
- var previousTestIndex = testSelector.selectedIndex - 1;
- while (true) {
- if (previousTestIndex == -1) {
- return;
- }
- if (testSelector.options[previousTestIndex].disabled) {
- previousTestIndex--;
- } else {
- testSelector.selectedIndex = previousTestIndex;
- selectTest();
- return
- }
- }
-}
-
-window.addEventListener('DOMContentLoaded', main);
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/queue.js b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/queue.js
deleted file mode 100644
index f57c919..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/queue.js
+++ /dev/null
@@ -1,158 +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.
- */
-
-function RebaselineQueue()
-{
- this._selectNode = $('queue-select');
- this._rebaselineButtonNode = $('rebaseline-queue');
- this._toggleNode = $('toggle-queue');
- this._removeSelectionButtonNode = $('remove-queue-selection');
-
- this._inProgressRebaselineCount = 0;
-
- var self = this;
- $('add-to-rebaseline-queue').addEventListener(
- 'click', function() { self.addCurentTest(); });
- this._selectNode.addEventListener('change', updateState);
- this._removeSelectionButtonNode.addEventListener(
- 'click', function() { self._removeSelection(); });
- this._rebaselineButtonNode.addEventListener(
- 'click', function() { self.rebaseline(); });
- this._toggleNode.addEventListener(
- 'click', function() { toggle('queue'); });
-}
-
-RebaselineQueue.prototype.updateState = function()
-{
- var testName = getSelectedTest();
-
- var state = results.tests[testName].state;
- $('add-to-rebaseline-queue').disabled = state != STATE_NEEDS_REBASELINE;
-
- var queueLength = this._selectNode.options.length;
- if (this._inProgressRebaselineCount > 0) {
- this._rebaselineButtonNode.disabled = true;
- this._rebaselineButtonNode.textContent =
- 'Rebaseline in progress (' + this._inProgressRebaselineCount +
- ' tests left)';
- } else if (queueLength == 0) {
- this._rebaselineButtonNode.disabled = true;
- this._rebaselineButtonNode.textContent = 'Rebaseline queue';
- this._toggleNode.textContent = 'Queue';
- } else {
- this._rebaselineButtonNode.disabled = false;
- this._rebaselineButtonNode.textContent =
- 'Rebaseline queue (' + queueLength + ' tests)';
- this._toggleNode.textContent = 'Queue (' + queueLength + ' tests)';
- }
- this._removeSelectionButtonNode.disabled =
- this._selectNode.selectedIndex == -1;
-};
-
-RebaselineQueue.prototype.addCurrentTest = function()
-{
- var testName = getSelectedTest();
- var test = results.tests[testName];
-
- if (test.state != STATE_NEEDS_REBASELINE) {
- log('Cannot add test with state "' + test.state + '" to queue.',
- log.WARNING);
- return;
- }
-
- var queueOption = document.createElement('option');
- queueOption.value = testName;
- queueOption.textContent = testName;
- this._selectNode.appendChild(queueOption);
- test.state = STATE_IN_QUEUE;
- updateState();
-};
-
-RebaselineQueue.prototype.removeCurrentTest = function()
-{
- this._removeTest(getSelectedTest());
-};
-
-RebaselineQueue.prototype._removeSelection = function()
-{
- if (this._selectNode.selectedIndex == -1)
- return;
-
- this._removeTest(
- this._selectNode.options[this._selectNode.selectedIndex].value);
-};
-
-RebaselineQueue.prototype._removeTest = function(testName)
-{
- var queueOption = this._selectNode.firstChild;
-
- while (queueOption && queueOption.value != testName) {
- queueOption = queueOption.nextSibling;
- }
-
- if (!queueOption)
- return;
-
- this._selectNode.removeChild(queueOption);
- var test = results.tests[testName];
- test.state = STATE_NEEDS_REBASELINE;
- updateState();
-};
-
-RebaselineQueue.prototype.rebaseline = function()
-{
- var testNames = [];
- for (var queueOption = this._selectNode.firstChild;
- queueOption;
- queueOption = queueOption.nextSibling) {
- testNames.push(queueOption.value);
- }
-
- this._inProgressRebaselineCount = testNames.length;
- updateState();
-
- testNames.forEach(this._rebaselineTest, this);
-};
-
-RebaselineQueue.prototype._rebaselineTest = function(testName)
-{
- var baselineTarget = getSelectValue('baseline-target');
- var baselineMoveTo = getSelectValue('baseline-move-to');
-
- // FIXME: actually rebaseline
- log('Rebaselining ' + testName + ' for platform ' + baselineTarget + '...');
- var test = results.tests[testName];
- this._removeTest(testName);
- this._inProgressRebaselineCount--;
- test.state = STATE_REBASELINE_SUCCEEDED;
- updateState();
- log('Rebaselined add test with state ' + test.state + ' to queue',
- log.SUCCESS);
-};
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js b/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js
deleted file mode 100644
index b348462..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/data/rebaselineserver/util.js
+++ /dev/null
@@ -1,94 +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.
- */
-
-var results;
-var testsByFailureType = {};
-var testsByDirectory = {};
-var selectedTests = [];
-
-function $(id)
-{
- return document.getElementById(id);
-}
-
-function getSelectValue(id)
-{
- var select = $(id);
- if (select.selectedIndex == -1) {
- return null;
- } else {
- return select.options[select.selectedIndex].value;
- }
-}
-
-function loadText(url, callback)
-{
- var xhr = new XMLHttpRequest();
- xhr.open('GET', url);
- xhr.addEventListener('load', function() { callback(xhr.responseText); });
- xhr.send();
-}
-
-function log(text, type)
-{
- var node = $('log');
-
- if (type) {
- var typeNode = document.createElement('span');
- typeNode.textContent = type.text;
- typeNode.style.color = type.color;
- node.appendChild(typeNode);
- }
-
- node.appendChild(document.createTextNode(text + '\n'));
- node.scrollTop = node.scrollHeight;
-}
-
-log.WARNING = {text: 'Warning: ', color: '#aa3'};
-log.SUCCESS = {text: 'Success: ', color: 'green'};
-log.ERROR = {text: 'Error: ', color: 'red'};
-
-function toggle(id)
-{
- var element = $(id);
- var toggler = $('toggle-' + id);
- if (element.style.display == 'none') {
- element.style.display = '';
- toggler.className = 'link selected';
- } else {
- element.style.display = 'none';
- toggler.className = 'link';
- }
-}
-
-function getTracUrl(layoutTestPath)
-{
- return 'http://trac.webkit.org/browser/trunk/LayoutTests/' + layoutTestPath;
-} \ No newline at end of file
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/download.py b/WebKitTools/Scripts/webkitpy/tool/commands/download.py
deleted file mode 100644
index 457c050..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/download.py
+++ /dev/null
@@ -1,394 +0,0 @@
-# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple 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 os
-
-import webkitpy.tool.steps as steps
-
-from webkitpy.common.checkout.changelog import ChangeLog, view_source_url
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.tool.commands.abstractsequencedcommand import AbstractSequencedCommand
-from webkitpy.tool.commands.stepsequence import StepSequence
-from webkitpy.tool.comments import bug_comment_from_commit_text
-from webkitpy.tool.grammar import pluralize
-from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand
-from webkitpy.common.system.deprecated_logging import error, log
-
-
-class Clean(AbstractSequencedCommand):
- name = "clean"
- help_text = "Clean the working copy"
- steps = [
- steps.CleanWorkingDirectory,
- ]
-
- def _prepare_state(self, options, args, tool):
- options.force_clean = True
-
-
-class Update(AbstractSequencedCommand):
- name = "update"
- help_text = "Update working copy (used internally)"
- steps = [
- steps.CleanWorkingDirectory,
- steps.Update,
- ]
-
-
-class Build(AbstractSequencedCommand):
- name = "build"
- help_text = "Update working copy and build"
- steps = [
- steps.CleanWorkingDirectory,
- steps.Update,
- steps.Build,
- ]
-
- def _prepare_state(self, options, args, tool):
- options.build = True
-
-
-class BuildAndTest(AbstractSequencedCommand):
- name = "build-and-test"
- help_text = "Update working copy, build, and run the tests"
- steps = [
- steps.CleanWorkingDirectory,
- steps.Update,
- steps.Build,
- steps.RunTests,
- ]
-
-
-class Land(AbstractSequencedCommand):
- name = "land"
- help_text = "Land the current working directory diff and updates the associated bug if any"
- argument_names = "[BUGID]"
- show_in_main_help = True
- steps = [
- steps.EnsureBuildersAreGreen,
- steps.UpdateChangeLogsWithReviewer,
- steps.ValidateReviewer,
- steps.Build,
- steps.RunTests,
- steps.Commit,
- steps.CloseBugForLandDiff,
- ]
- long_help = """land commits the current working copy diff (just as svn or git commit would).
-land will NOT build and run the tests before committing, but you can use the --build option for that.
-If a bug id is provided, or one can be found in the ChangeLog land will update the bug after committing."""
-
- def _prepare_state(self, options, args, tool):
- changed_files = self._tool.scm().changed_files(options.git_commit)
- return {
- "changed_files": changed_files,
- "bug_id": (args and args[0]) or tool.checkout().bug_id_for_this_commit(options.git_commit, changed_files),
- }
-
-
-class LandCowboy(AbstractSequencedCommand):
- name = "land-cowboy"
- help_text = "Prepares a ChangeLog and lands the current working directory diff."
- steps = [
- steps.PrepareChangeLog,
- steps.EditChangeLog,
- steps.ConfirmDiff,
- steps.Build,
- steps.RunTests,
- steps.Commit,
- ]
-
-
-class AbstractPatchProcessingCommand(AbstractDeclarativeCommand):
- # Subclasses must implement the methods below. We don't declare them here
- # because we want to be able to implement them with mix-ins.
- #
- # def _fetch_list_of_patches_to_process(self, options, args, tool):
- # def _prepare_to_process(self, options, args, tool):
-
- @staticmethod
- def _collect_patches_by_bug(patches):
- bugs_to_patches = {}
- for patch in patches:
- bugs_to_patches[patch.bug_id()] = bugs_to_patches.get(patch.bug_id(), []) + [patch]
- return bugs_to_patches
-
- def execute(self, options, args, tool):
- self._prepare_to_process(options, args, tool)
- patches = self._fetch_list_of_patches_to_process(options, args, tool)
-
- # It's nice to print out total statistics.
- bugs_to_patches = self._collect_patches_by_bug(patches)
- log("Processing %s from %s." % (pluralize("patch", len(patches)), pluralize("bug", len(bugs_to_patches))))
-
- for patch in patches:
- self._process_patch(patch, options, args, tool)
-
-
-class AbstractPatchSequencingCommand(AbstractPatchProcessingCommand):
- prepare_steps = None
- main_steps = None
-
- def __init__(self):
- options = []
- self._prepare_sequence = StepSequence(self.prepare_steps)
- self._main_sequence = StepSequence(self.main_steps)
- options = sorted(set(self._prepare_sequence.options() + self._main_sequence.options()))
- AbstractPatchProcessingCommand.__init__(self, options)
-
- def _prepare_to_process(self, options, args, tool):
- self._prepare_sequence.run_and_handle_errors(tool, options)
-
- def _process_patch(self, patch, options, args, tool):
- state = { "patch" : patch }
- self._main_sequence.run_and_handle_errors(tool, options, state)
-
-
-class ProcessAttachmentsMixin(object):
- def _fetch_list_of_patches_to_process(self, options, args, tool):
- return map(lambda patch_id: tool.bugs.fetch_attachment(patch_id), args)
-
-
-class ProcessBugsMixin(object):
- def _fetch_list_of_patches_to_process(self, options, args, tool):
- all_patches = []
- for bug_id in args:
- patches = tool.bugs.fetch_bug(bug_id).reviewed_patches()
- log("%s found on bug %s." % (pluralize("reviewed patch", len(patches)), bug_id))
- all_patches += patches
- return all_patches
-
-
-class CheckStyle(AbstractPatchSequencingCommand, ProcessAttachmentsMixin):
- name = "check-style"
- help_text = "Run check-webkit-style on the specified attachments"
- argument_names = "ATTACHMENT_ID [ATTACHMENT_IDS]"
- main_steps = [
- steps.CleanWorkingDirectory,
- steps.Update,
- steps.ApplyPatch,
- steps.CheckStyle,
- ]
-
-
-class BuildAttachment(AbstractPatchSequencingCommand, ProcessAttachmentsMixin):
- name = "build-attachment"
- help_text = "Apply and build patches from bugzilla"
- argument_names = "ATTACHMENT_ID [ATTACHMENT_IDS]"
- main_steps = [
- steps.CleanWorkingDirectory,
- steps.Update,
- steps.ApplyPatch,
- steps.Build,
- ]
-
-
-class BuildAndTestAttachment(AbstractPatchSequencingCommand, ProcessAttachmentsMixin):
- name = "build-and-test-attachment"
- help_text = "Apply, build, and test patches from bugzilla"
- argument_names = "ATTACHMENT_ID [ATTACHMENT_IDS]"
- main_steps = [
- steps.CleanWorkingDirectory,
- steps.Update,
- steps.ApplyPatch,
- steps.Build,
- steps.RunTests,
- ]
-
-
-class AbstractPatchApplyingCommand(AbstractPatchSequencingCommand):
- prepare_steps = [
- steps.EnsureLocalCommitIfNeeded,
- steps.CleanWorkingDirectoryWithLocalCommits,
- steps.Update,
- ]
- main_steps = [
- steps.ApplyPatchWithLocalCommit,
- ]
- long_help = """Updates the working copy.
-Downloads and applies the patches, creating local commits if necessary."""
-
-
-class ApplyAttachment(AbstractPatchApplyingCommand, ProcessAttachmentsMixin):
- name = "apply-attachment"
- help_text = "Apply an attachment to the local working directory"
- argument_names = "ATTACHMENT_ID [ATTACHMENT_IDS]"
- show_in_main_help = True
-
-
-class ApplyFromBug(AbstractPatchApplyingCommand, ProcessBugsMixin):
- name = "apply-from-bug"
- help_text = "Apply reviewed patches from provided bugs to the local working directory"
- argument_names = "BUGID [BUGIDS]"
- show_in_main_help = True
-
-
-class AbstractPatchLandingCommand(AbstractPatchSequencingCommand):
- prepare_steps = [
- steps.EnsureBuildersAreGreen,
- ]
- main_steps = [
- steps.CleanWorkingDirectory,
- steps.Update,
- steps.ApplyPatch,
- steps.ValidateReviewer,
- steps.Build,
- steps.RunTests,
- steps.Commit,
- steps.ClosePatch,
- steps.CloseBug,
- ]
- long_help = """Checks to make sure builders are green.
-Updates the working copy.
-Applies the patch.
-Builds.
-Runs the layout tests.
-Commits the patch.
-Clears the flags on the patch.
-Closes the bug if no patches are marked for review."""
-
-
-class LandAttachment(AbstractPatchLandingCommand, ProcessAttachmentsMixin):
- name = "land-attachment"
- help_text = "Land patches from bugzilla, optionally building and testing them first"
- argument_names = "ATTACHMENT_ID [ATTACHMENT_IDS]"
- show_in_main_help = True
-
-
-class LandFromBug(AbstractPatchLandingCommand, ProcessBugsMixin):
- name = "land-from-bug"
- help_text = "Land all patches on the given bugs, optionally building and testing them first"
- argument_names = "BUGID [BUGIDS]"
- show_in_main_help = True
-
-
-class AbstractRolloutPrepCommand(AbstractSequencedCommand):
- argument_names = "REVISION REASON"
-
- def _commit_info(self, revision):
- commit_info = self._tool.checkout().commit_info_for_revision(revision)
- if commit_info and commit_info.bug_id():
- # Note: Don't print a bug URL here because it will confuse the
- # SheriffBot because the SheriffBot just greps the output
- # of create-rollout for bug URLs. It should do better
- # parsing instead.
- log("Preparing rollout for bug %s." % commit_info.bug_id())
- else:
- log("Unable to parse bug number from diff.")
- return commit_info
-
- def _prepare_state(self, options, args, tool):
- revision = args[0]
- commit_info = self._commit_info(revision)
- cc_list = sorted([party.bugzilla_email()
- for party in commit_info.responsible_parties()
- if party.bugzilla_email()])
- return {
- "revision": revision,
- "bug_id": commit_info.bug_id(),
- # FIXME: We should used the list as the canonical representation.
- "bug_cc": ",".join(cc_list),
- "reason": args[1],
- }
-
-
-class PrepareRollout(AbstractRolloutPrepCommand):
- name = "prepare-rollout"
- help_text = "Revert the given revision in the working copy and prepare ChangeLogs with revert reason"
- long_help = """Updates the working copy.
-Applies the inverse diff for the provided revision.
-Creates an appropriate rollout ChangeLog, including a trac link and bug link.
-"""
- steps = [
- steps.CleanWorkingDirectory,
- steps.Update,
- steps.RevertRevision,
- steps.PrepareChangeLogForRevert,
- ]
-
-
-class CreateRollout(AbstractRolloutPrepCommand):
- name = "create-rollout"
- help_text = "Creates a bug to track a broken SVN revision and uploads a rollout patch."
- steps = [
- steps.CleanWorkingDirectory,
- steps.Update,
- steps.RevertRevision,
- steps.CreateBug,
- steps.PrepareChangeLogForRevert,
- steps.PostDiffForRevert,
- ]
-
- def _prepare_state(self, options, args, tool):
- state = AbstractRolloutPrepCommand._prepare_state(self, options, args, tool)
- # Currently, state["bug_id"] points to the bug that caused the
- # regression. We want to create a new bug that blocks the old bug
- # so we move state["bug_id"] to state["bug_blocked"] and delete the
- # old state["bug_id"] so that steps.CreateBug will actually create
- # the new bug that we want (and subsequently store its bug id into
- # state["bug_id"])
- state["bug_blocked"] = state["bug_id"]
- del state["bug_id"]
- state["bug_title"] = "REGRESSION(r%s): %s" % (state["revision"], state["reason"])
- state["bug_description"] = "%s broke the build:\n%s" % (view_source_url(state["revision"]), state["reason"])
- # FIXME: If we had more context here, we could link to other open bugs
- # that mention the test that regressed.
- if options.parent_command == "sheriff-bot":
- state["bug_description"] += """
-
-This is an automatic bug report generated by the sheriff-bot. If this bug
-report was created because of a flaky test, please file a bug for the flaky
-test (if we don't already have one on file) and dup this bug against that bug
-so that we can track how often these flaky tests case pain.
-
-"Only you can prevent forest fires." -- Smokey the Bear
-"""
- return state
-
-
-class Rollout(AbstractRolloutPrepCommand):
- name = "rollout"
- show_in_main_help = True
- help_text = "Revert the given revision in the working copy and optionally commit the revert and re-open the original bug"
- long_help = """Updates the working copy.
-Applies the inverse diff for the provided revision.
-Creates an appropriate rollout ChangeLog, including a trac link and bug link.
-Opens the generated ChangeLogs in $EDITOR.
-Shows the prepared diff for confirmation.
-Commits the revert and updates the bug (including re-opening the bug if necessary)."""
- steps = [
- steps.CleanWorkingDirectory,
- steps.Update,
- steps.RevertRevision,
- steps.PrepareChangeLogForRevert,
- steps.EditChangeLog,
- steps.ConfirmDiff,
- steps.Build,
- steps.Commit,
- steps.ReopenBugAfterRollout,
- ]
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py
deleted file mode 100644
index 9ca343b..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py
+++ /dev/null
@@ -1,190 +0,0 @@
-# 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
-# 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 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 MockOptions, 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):
- def _default_options(self):
- options = MockOptions()
- options.build = True
- options.build_style = True
- options.check_builders = True
- options.check_style = True
- options.clean = True
- options.close_bug = True
- options.force_clean = False
- options.force_patch = True
- options.non_interactive = False
- options.parent_command = 'MOCK parent command'
- options.quiet = False
- options.test = True
- options.update = True
- return options
-
- def test_build(self):
- expected_stderr = "Updating working directory\nBuilding WebKit\n"
- self.assert_execute_outputs(Build(), [], options=self._default_options(), expected_stderr=expected_stderr)
-
- def test_build_and_test(self):
- expected_stderr = "Updating working directory\nBuilding WebKit\nRunning Python unit tests\nRunning Perl unit tests\nRunning JavaScriptCore tests\nRunning run-webkit-tests\n"
- self.assert_execute_outputs(BuildAndTest(), [], options=self._default_options(), expected_stderr=expected_stderr)
-
- def test_apply_attachment(self):
- options = self._default_options()
- options.update = True
- options.local_commit = True
- expected_stderr = "Updating working directory\nProcessing 1 patch from 1 bug.\nProcessing patch 197 from bug 42.\n"
- self.assert_execute_outputs(ApplyAttachment(), [197], options=options, expected_stderr=expected_stderr)
-
- def test_apply_patches(self):
- options = self._default_options()
- options.update = True
- options.local_commit = True
- expected_stderr = "Updating working directory\n2 reviewed patches found on bug 42.\nProcessing 2 patches from 1 bug.\nProcessing patch 197 from bug 42.\nProcessing patch 128 from bug 42.\n"
- self.assert_execute_outputs(ApplyFromBug(), [42], options=options, expected_stderr=expected_stderr)
-
- def test_land_diff(self):
- expected_stderr = "Building WebKit\nRunning Python unit tests\nRunning Perl unit tests\nRunning JavaScriptCore tests\nRunning run-webkit-tests\nCommitted r49824: <http://trac.webkit.org/changeset/49824>\nUpdating bug 42\n"
- mock_tool = MockTool()
- mock_tool.scm().create_patch = Mock()
- mock_tool.checkout().modified_changelogs = Mock(return_value=[])
- self.assert_execute_outputs(Land(), [42], options=self._default_options(), expected_stderr=expected_stderr, tool=mock_tool)
- # Make sure we're not calling expensive calls too often.
- self.assertEqual(mock_tool.scm().create_patch.call_count, 0)
- self.assertEqual(mock_tool.checkout().modified_changelogs.call_count, 1)
-
- def test_land_red_builders(self):
- expected_stderr = '\nWARNING: Builders ["Builder2"] are red, please watch your commit carefully.\nSee http://dummy_buildbot_host/console?category=core\n\nBuilding WebKit\nRunning Python unit tests\nRunning Perl unit tests\nRunning JavaScriptCore tests\nRunning run-webkit-tests\nCommitted r49824: <http://trac.webkit.org/changeset/49824>\nUpdating bug 42\n'
- mock_tool = MockTool()
- mock_tool.buildbot.light_tree_on_fire()
- self.assert_execute_outputs(Land(), [42], options=self._default_options(), expected_stderr=expected_stderr, tool=mock_tool)
-
- def test_check_style(self):
- expected_stderr = "Processing 1 patch from 1 bug.\nUpdating working directory\nProcessing patch 197 from bug 42.\nRunning check-webkit-style\n"
- self.assert_execute_outputs(CheckStyle(), [197], options=self._default_options(), expected_stderr=expected_stderr)
-
- def test_build_attachment(self):
- expected_stderr = "Processing 1 patch from 1 bug.\nUpdating working directory\nProcessing patch 197 from bug 42.\nBuilding WebKit\n"
- self.assert_execute_outputs(BuildAttachment(), [197], options=self._default_options(), expected_stderr=expected_stderr)
-
- def test_land_attachment(self):
- # FIXME: This expected result is imperfect, notice how it's seeing the same patch as still there after it thought it would have cleared the flags.
- expected_stderr = """Processing 1 patch from 1 bug.
-Updating working directory
-Processing patch 197 from bug 42.
-Building WebKit
-Running Python unit tests
-Running Perl unit tests
-Running JavaScriptCore tests
-Running run-webkit-tests
-Committed r49824: <http://trac.webkit.org/changeset/49824>
-Not closing bug 42 as attachment 197 has review=+. Assuming there are more patches to land from this bug.
-"""
- self.assert_execute_outputs(LandAttachment(), [197], options=self._default_options(), expected_stderr=expected_stderr)
-
- def test_land_patches(self):
- # FIXME: This expected result is imperfect, notice how it's seeing the same patch as still there after it thought it would have cleared the flags.
- expected_stderr = """2 reviewed patches found on bug 42.
-Processing 2 patches from 1 bug.
-Updating working directory
-Processing patch 197 from bug 42.
-Building WebKit
-Running Python unit tests
-Running Perl unit tests
-Running JavaScriptCore tests
-Running run-webkit-tests
-Committed r49824: <http://trac.webkit.org/changeset/49824>
-Not closing bug 42 as attachment 197 has review=+. Assuming there are more patches to land from this bug.
-Updating working directory
-Processing patch 128 from bug 42.
-Building WebKit
-Running Python unit tests
-Running Perl unit tests
-Running JavaScriptCore tests
-Running run-webkit-tests
-Committed r49824: <http://trac.webkit.org/changeset/49824>
-Not closing bug 42 as attachment 197 has review=+. Assuming there are more patches to land from this bug.
-"""
- self.assert_execute_outputs(LandFromBug(), [42], options=self._default_options(), expected_stderr=expected_stderr)
-
- def test_prepare_rollout(self):
- expected_stderr = "Preparing rollout for bug 42.\nUpdating working directory\nRunning prepare-ChangeLog\n"
- self.assert_execute_outputs(PrepareRollout(), [852, "Reason"], options=self._default_options(), expected_stderr=expected_stderr)
-
- def test_create_rollout(self):
- expected_stderr = """Preparing rollout for bug 42.
-Updating working directory
-MOCK create_bug
-bug_title: REGRESSION(r852): Reason
-bug_description: http://trac.webkit.org/changeset/852 broke the build:
-Reason
-Running prepare-ChangeLog
-MOCK add_patch_to_bug: bug_id=None, description=ROLLOUT of r852, mark_for_review=False, mark_for_commit_queue=True, mark_for_landing=False
--- Begin comment --
-Any committer can land this patch automatically by marking it commit-queue+. The commit-queue will build and test the patch before landing to ensure that the rollout will be successful. This process takes approximately 15 minutes.
-
-If you would like to land the rollout faster, you can use the following command:
-
- webkit-patch land-attachment ATTACHMENT_ID --ignore-builders
-
-where ATTACHMENT_ID is the ID of this attachment.
--- End comment --
-"""
- self.assert_execute_outputs(CreateRollout(), [852, "Reason"], options=self._default_options(), expected_stderr=expected_stderr)
-
- def test_rollout(self):
- expected_stderr = "Preparing rollout for bug 42.\nUpdating working directory\nRunning prepare-ChangeLog\nMOCK: user.open_url: file://...\nBuilding WebKit\nCommitted r49824: <http://trac.webkit.org/changeset/49824>\n"
- expected_stdout = "Was that diff correct?\n"
- self.assert_execute_outputs(Rollout(), [852, "Reason"], options=self._default_options(), expected_stdout=expected_stdout, expected_stderr=expected_stderr)
-
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py
deleted file mode 100644
index 3b53d1a..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py
+++ /dev/null
@@ -1,182 +0,0 @@
-# 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
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from webkitpy.tool.commands.queues import AbstractReviewQueue
-from webkitpy.common.config.committers import CommitterList
-from webkitpy.common.config.ports import WebKitPort
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.tool.bot.queueengine import QueueEngine
-
-
-class AbstractEarlyWarningSystem(AbstractReviewQueue):
- _build_style = "release"
-
- def __init__(self):
- AbstractReviewQueue.__init__(self)
- self.port = WebKitPort.port(self.port_name)
-
- def should_proceed_with_work_item(self, patch):
- return True
-
- def _can_build(self):
- try:
- self.run_webkit_patch([
- "build",
- self.port.flag(),
- "--build-style=%s" % self._build_style,
- "--force-clean",
- "--no-update"])
- return True
- except ScriptError, e:
- failure_log = self._log_from_script_error_for_upload(e)
- self._update_status("Unable to perform a build", results_file=failure_log)
- return False
-
- def _build(self, patch, first_run=False):
- try:
- args = [
- "build-attachment",
- self.port.flag(),
- "--build",
- "--build-style=%s" % self._build_style,
- "--force-clean",
- "--quiet",
- "--non-interactive",
- patch.id()]
- if not first_run:
- # See commit-queue for an explanation of what we're doing here.
- args.append("--no-update")
- args.append("--parent-command=%s" % self.name)
- self.run_webkit_patch(args)
- return True
- except ScriptError, e:
- if first_run:
- return False
- raise
-
- def review_patch(self, patch):
- if patch.is_obsolete():
- self._did_error(patch, "%s does not process obsolete patches." % self.name)
- return False
-
- if patch.bug().is_closed():
- self._did_error(patch, "%s does not process patches on closed bugs." % self.name)
- return False
-
- if not self._build(patch, first_run=True):
- if not self._can_build():
- return False
- self._build(patch)
- return True
-
- @classmethod
- def handle_script_error(cls, tool, state, script_error):
- is_svn_apply = script_error.command_name() == "svn-apply"
- status_id = cls._update_status_for_script_error(tool, state, script_error, is_error=is_svn_apply)
- if is_svn_apply:
- QueueEngine.exit_after_handled_error(script_error)
- results_link = tool.status_server.results_url_for_status(status_id)
- message = "Attachment %s did not build on %s:\nBuild output: %s" % (state["patch"].id(), cls.port_name, results_link)
- tool.bugs.post_comment_to_bug(state["patch"].bug_id(), message, cc=cls.watchers)
- exit(1)
-
-
-class GtkEWS(AbstractEarlyWarningSystem):
- name = "gtk-ews"
- port_name = "gtk"
- watchers = AbstractEarlyWarningSystem.watchers + [
- "gns@gnome.org",
- "xan.lopez@gmail.com",
- ]
-
-
-class EflEWS(AbstractEarlyWarningSystem):
- name = "efl-ews"
- port_name = "efl"
- watchers = AbstractEarlyWarningSystem.watchers + [
- "leandro@profusion.mobi",
- "antognolli@profusion.mobi",
- "lucas.demarchi@profusion.mobi",
- ]
-
-
-class QtEWS(AbstractEarlyWarningSystem):
- name = "qt-ews"
- port_name = "qt"
-
-
-class WinEWS(AbstractEarlyWarningSystem):
- name = "win-ews"
- port_name = "win"
- # Use debug, the Apple Win port fails to link Release on 32-bit Windows.
- # https://bugs.webkit.org/show_bug.cgi?id=39197
- _build_style = "debug"
-
-
-class AbstractChromiumEWS(AbstractEarlyWarningSystem):
- port_name = "chromium"
- watchers = AbstractEarlyWarningSystem.watchers + [
- "dglazkov@chromium.org",
- ]
-
-
-class ChromiumLinuxEWS(AbstractChromiumEWS):
- # FIXME: We should rename this command to cr-linux-ews, but that requires
- # a database migration. :(
- name = "chromium-ews"
-
-
-class ChromiumWindowsEWS(AbstractChromiumEWS):
- name = "cr-win-ews"
-
-
-# For platforms that we can't run inside a VM (like Mac OS X), we require
-# patches to be uploaded by committers, who are generally trustworthy folk. :)
-class AbstractCommitterOnlyEWS(AbstractEarlyWarningSystem):
- def __init__(self, committers=CommitterList()):
- AbstractEarlyWarningSystem.__init__(self)
- self._committers = committers
-
- def process_work_item(self, patch):
- if not self._committers.committer_by_email(patch.attacher_email()):
- self._did_error(patch, "%s cannot process patches from non-committers :(" % self.name)
- return False
- return AbstractEarlyWarningSystem.process_work_item(self, patch)
-
-
-# FIXME: Inheriting from AbstractCommitterOnlyEWS is kinda a hack, but it
-# happens to work because AbstractChromiumEWS and AbstractCommitterOnlyEWS
-# provide disjoint sets of functionality, and Python is otherwise smart
-# enough to handle the diamond inheritance.
-class ChromiumMacEWS(AbstractChromiumEWS, AbstractCommitterOnlyEWS):
- name = "cr-mac-ews"
-
-
-class MacEWS(AbstractCommitterOnlyEWS):
- name = "mac-ews"
- port_name = "mac"
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py
deleted file mode 100644
index 830e11c..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py
+++ /dev/null
@@ -1,132 +0,0 @@
-# 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
-# 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 os
-
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.tool.bot.queueengine import QueueEngine
-from webkitpy.tool.commands.earlywarningsystem import *
-from webkitpy.tool.commands.queuestest import QueuesTest
-from webkitpy.tool.mocktool import MockTool, MockOptions
-
-
-class AbstractEarlyWarningSystemTest(QueuesTest):
- def test_can_build(self):
- # Needed to define port_name, used in AbstractEarlyWarningSystem.__init__
- class TestEWS(AbstractEarlyWarningSystem):
- port_name = "win" # Needs to be a port which port/factory understands.
-
- queue = TestEWS()
- queue.bind_to_tool(MockTool(log_executive=True))
- queue._options = MockOptions(port=None)
- expected_stderr = "MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build', '--port=win', '--build-style=release', '--force-clean', '--no-update']\n"
- OutputCapture().assert_outputs(self, queue._can_build, [], expected_stderr=expected_stderr)
-
- def mock_run_webkit_patch(args):
- raise ScriptError("MOCK script error")
-
- queue.run_webkit_patch = mock_run_webkit_patch
- expected_stderr = "MOCK: update_status: None Unable to perform a build\n"
- OutputCapture().assert_outputs(self, queue._can_build, [], expected_stderr=expected_stderr)
-
- # FIXME: This belongs on an AbstractReviewQueueTest object in queues_unittest.py
- def test_subprocess_handled_error(self):
- queue = AbstractReviewQueue()
- queue.bind_to_tool(MockTool())
-
- def mock_review_patch(patch):
- raise ScriptError('MOCK script error', exit_code=QueueEngine.handled_error_code)
-
- queue.review_patch = mock_review_patch
- mock_patch = queue._tool.bugs.fetch_attachment(197)
- expected_stderr = "MOCK: release_work_item: None 197\n"
- OutputCapture().assert_outputs(self, queue.process_work_item, [mock_patch], expected_stderr=expected_stderr, expected_exception=ScriptError)
-
-
-class EarlyWarningSytemTest(QueuesTest):
- def test_failed_builds(self):
- ews = ChromiumLinuxEWS()
- ews.bind_to_tool(MockTool())
- ews._build = lambda patch, first_run=False: False
- ews._can_build = lambda: True
- mock_patch = ews._tool.bugs.fetch_attachment(197)
- ews.review_patch(mock_patch)
-
- def _default_expected_stderr(self, ews):
- string_replacemnts = {
- "name": ews.name,
- "port": ews.port_name,
- "watchers": ews.watchers,
- }
- expected_stderr = {
- "begin_work_queue": self._default_begin_work_queue_stderr(ews.name, ews._tool.scm().checkout_root),
- "handle_unexpected_error": "Mock error message\n",
- "next_work_item": "",
- "process_work_item": "MOCK: update_status: %(name)s Pass\nMOCK: release_work_item: %(name)s 197\n" % string_replacemnts,
- "handle_script_error": "MOCK: update_status: %(name)s ScriptError error message\nMOCK bug comment: bug_id=42, cc=%(watchers)s\n--- Begin comment ---\nAttachment 197 did not build on %(port)s:\nBuild output: http://dummy_url\n--- End comment ---\n\n" % string_replacemnts,
- }
- return expected_stderr
-
- def _test_ews(self, ews):
- ews.bind_to_tool(MockTool())
- expected_exceptions = {
- "handle_script_error": SystemExit,
- }
- self.assert_queue_outputs(ews, expected_stderr=self._default_expected_stderr(ews), expected_exceptions=expected_exceptions)
-
- def _test_committer_only_ews(self, ews):
- ews.bind_to_tool(MockTool())
- expected_stderr = self._default_expected_stderr(ews)
- string_replacemnts = {"name": ews.name}
- expected_stderr["process_work_item"] = "MOCK: update_status: %(name)s Error: %(name)s cannot process patches from non-committers :(\nMOCK: release_work_item: %(name)s 197\n" % string_replacemnts
- expected_exceptions = {"handle_script_error": SystemExit}
- self.assert_queue_outputs(ews, expected_stderr=expected_stderr, expected_exceptions=expected_exceptions)
-
- # 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):
- self._test_ews(ChromiumWindowsEWS())
-
- def test_qt_ews(self):
- self._test_ews(QtEWS())
-
- def test_gtk_ews(self):
- self._test_ews(GtkEWS())
-
- def test_efl_ews(self):
- self._test_ews(EflEWS())
-
- def test_mac_ews(self):
- self._test_committer_only_ews(MacEWS())
-
- def test_chromium_mac_ews(self):
- self._test_committer_only_ews(ChromiumMacEWS())
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/openbugs.py b/WebKitTools/Scripts/webkitpy/tool/commands/openbugs.py
deleted file mode 100644
index 1b51c9f..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/openbugs.py
+++ /dev/null
@@ -1,63 +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.
-
-import re
-import sys
-
-from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand
-from webkitpy.common.system.deprecated_logging import log
-
-
-class OpenBugs(AbstractDeclarativeCommand):
- name = "open-bugs"
- help_text = "Finds all bug numbers passed in arguments (or stdin if no args provided) and opens them in a web browser"
-
- bug_number_regexp = re.compile(r"\b\d{4,6}\b")
-
- def _open_bugs(self, bug_ids):
- for bug_id in bug_ids:
- bug_url = self._tool.bugs.bug_url_for_bug_id(bug_id)
- self._tool.user.open_url(bug_url)
-
- # _find_bugs_in_string mostly exists for easy unit testing.
- def _find_bugs_in_string(self, string):
- return self.bug_number_regexp.findall(string)
-
- def _find_bugs_in_iterable(self, iterable):
- return sum([self._find_bugs_in_string(string) for string in iterable], [])
-
- def execute(self, options, args, tool):
- if args:
- bug_ids = self._find_bugs_in_iterable(args)
- else:
- # This won't open bugs until stdin is closed but could be made to easily. That would just make unit testing slightly harder.
- bug_ids = self._find_bugs_in_iterable(sys.stdin)
-
- log("%s bugs found in input." % len(bug_ids))
-
- self._open_bugs(bug_ids)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/openbugs_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/openbugs_unittest.py
deleted file mode 100644
index 40a6e1b..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/openbugs_unittest.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# 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
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from webkitpy.tool.commands.commandtest import CommandsTest
-from webkitpy.tool.commands.openbugs import OpenBugs
-
-class OpenBugsTest(CommandsTest):
-
- find_bugs_in_string_expectations = [
- ["123", []],
- ["1234", ["1234"]],
- ["12345", ["12345"]],
- ["123456", ["123456"]],
- ["1234567", []],
- [" 123456 234567", ["123456", "234567"]],
- ]
-
- def test_find_bugs_in_string(self):
- openbugs = OpenBugs()
- for expectation in self.find_bugs_in_string_expectations:
- self.assertEquals(openbugs._find_bugs_in_string(expectation[0]), expectation[1])
-
- def test_args_parsing(self):
- expected_stderr = "2 bugs found in input.\nMOCK: user.open_url: http://example.com/12345\nMOCK: user.open_url: http://example.com/23456\n"
- self.assert_execute_outputs(OpenBugs(), ["12345\n23456"], expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/prettydiff.py b/WebKitTools/Scripts/webkitpy/tool/commands/prettydiff.py
deleted file mode 100644
index e3fc00c..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/prettydiff.py
+++ /dev/null
@@ -1,38 +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.
-
-from webkitpy.tool.commands.abstractsequencedcommand import AbstractSequencedCommand
-import webkitpy.tool.steps as steps
-
-
-class PrettyDiff(AbstractSequencedCommand):
- name = "pretty-diff"
- help_text = "Shows the pretty diff in the default browser"
- steps = [
- steps.ConfirmDiff,
- ]
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queries.py b/WebKitTools/Scripts/webkitpy/tool/commands/queries.py
deleted file mode 100644
index 16ddc2c..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/queries.py
+++ /dev/null
@@ -1,393 +0,0 @@
-# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-from optparse import make_option
-
-import webkitpy.tool.steps as steps
-
-from webkitpy.common.checkout.commitinfo import CommitInfo
-from webkitpy.common.config.committers import CommitterList
-from webkitpy.common.net.buildbot import BuildBot
-from webkitpy.common.net.regressionwindow import RegressionWindow
-from webkitpy.common.system.user import User
-from webkitpy.tool.grammar import pluralize
-from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand
-from webkitpy.common.system.deprecated_logging import log
-from webkitpy.layout_tests import port
-
-
-class SuggestReviewers(AbstractDeclarativeCommand):
- name = "suggest-reviewers"
- help_text = "Suggest reviewers for a patch based on recent changes to the modified files."
-
- def __init__(self):
- options = [
- steps.Options.git_commit,
- ]
- AbstractDeclarativeCommand.__init__(self, options=options)
-
- def execute(self, options, args, tool):
- reviewers = tool.checkout().suggested_reviewers(options.git_commit)
- print "\n".join([reviewer.full_name for reviewer in reviewers])
-
-
-class BugsToCommit(AbstractDeclarativeCommand):
- name = "bugs-to-commit"
- help_text = "List bugs in the commit-queue"
-
- def execute(self, options, args, tool):
- # FIXME: This command is poorly named. It's fetching the commit-queue list here. The name implies it's fetching pending-commit (all r+'d patches).
- bug_ids = tool.bugs.queries.fetch_bug_ids_from_commit_queue()
- for bug_id in bug_ids:
- print "%s" % bug_id
-
-
-class PatchesInCommitQueue(AbstractDeclarativeCommand):
- name = "patches-in-commit-queue"
- help_text = "List patches in the commit-queue"
-
- def execute(self, options, args, tool):
- patches = tool.bugs.queries.fetch_patches_from_commit_queue()
- log("Patches in commit queue:")
- for patch in patches:
- print patch.url()
-
-
-class PatchesToCommitQueue(AbstractDeclarativeCommand):
- name = "patches-to-commit-queue"
- help_text = "Patches which should be added to the commit queue"
- def __init__(self):
- options = [
- make_option("--bugs", action="store_true", dest="bugs", help="Output bug links instead of patch links"),
- ]
- AbstractDeclarativeCommand.__init__(self, options=options)
-
- @staticmethod
- def _needs_commit_queue(patch):
- if patch.commit_queue() == "+": # If it's already cq+, ignore the patch.
- log("%s already has cq=%s" % (patch.id(), patch.commit_queue()))
- return False
-
- # We only need to worry about patches from contributers who are not yet committers.
- committer_record = CommitterList().committer_by_email(patch.attacher_email())
- if committer_record:
- log("%s committer = %s" % (patch.id(), committer_record))
- return not committer_record
-
- def execute(self, options, args, tool):
- patches = tool.bugs.queries.fetch_patches_from_pending_commit_list()
- patches_needing_cq = filter(self._needs_commit_queue, patches)
- if options.bugs:
- bugs_needing_cq = map(lambda patch: patch.bug_id(), patches_needing_cq)
- bugs_needing_cq = sorted(set(bugs_needing_cq))
- for bug_id in bugs_needing_cq:
- print "%s" % tool.bugs.bug_url_for_bug_id(bug_id)
- else:
- for patch in patches_needing_cq:
- print "%s" % tool.bugs.attachment_url_for_id(patch.id(), action="edit")
-
-
-class PatchesToReview(AbstractDeclarativeCommand):
- name = "patches-to-review"
- help_text = "List patches that are pending review"
-
- def execute(self, options, args, tool):
- patch_ids = tool.bugs.queries.fetch_attachment_ids_from_review_queue()
- log("Patches pending review:")
- for patch_id in patch_ids:
- print patch_id
-
-
-class LastGreenRevision(AbstractDeclarativeCommand):
- name = "last-green-revision"
- help_text = "Prints the last known good revision"
-
- def execute(self, options, args, tool):
- print self._tool.buildbot.last_green_revision()
-
-
-class WhatBroke(AbstractDeclarativeCommand):
- name = "what-broke"
- help_text = "Print failing buildbots (%s) and what revisions broke them" % BuildBot.default_host
-
- def _print_builder_line(self, builder_name, max_name_width, status_message):
- print "%s : %s" % (builder_name.ljust(max_name_width), status_message)
-
- def _print_blame_information_for_builder(self, builder_status, name_width, avoid_flakey_tests=True):
- builder = self._tool.buildbot.builder_with_name(builder_status["name"])
- red_build = builder.build(builder_status["build_number"])
- regression_window = builder.find_regression_window(red_build)
- if not regression_window.failing_build():
- self._print_builder_line(builder.name(), name_width, "FAIL (error loading build information)")
- return
- if not regression_window.build_before_failure():
- self._print_builder_line(builder.name(), name_width, "FAIL (blame-list: sometime before %s?)" % regression_window.failing_build().revision())
- return
-
- revisions = regression_window.revisions()
- first_failure_message = ""
- if (regression_window.failing_build() == builder.build(builder_status["build_number"])):
- first_failure_message = " FIRST FAILURE, possibly a flaky test"
- self._print_builder_line(builder.name(), name_width, "FAIL (blame-list: %s%s)" % (revisions, first_failure_message))
- for revision in revisions:
- commit_info = self._tool.checkout().commit_info_for_revision(revision)
- if commit_info:
- print commit_info.blame_string(self._tool.bugs)
- else:
- print "FAILED to fetch CommitInfo for r%s, likely missing ChangeLog" % revision
-
- def execute(self, options, args, tool):
- builder_statuses = tool.buildbot.builder_statuses()
- longest_builder_name = max(map(len, map(lambda builder: builder["name"], builder_statuses)))
- failing_builders = 0
- for builder_status in builder_statuses:
- # If the builder is green, print OK, exit.
- if builder_status["is_green"]:
- continue
- self._print_blame_information_for_builder(builder_status, name_width=longest_builder_name)
- failing_builders += 1
- if failing_builders:
- print "%s of %s are failing" % (failing_builders, pluralize("builder", len(builder_statuses)))
- else:
- print "All builders are passing!"
-
-
-class ResultsFor(AbstractDeclarativeCommand):
- name = "results-for"
- help_text = "Print a list of failures for the passed revision from bots on %s" % BuildBot.default_host
- argument_names = "REVISION"
-
- def _print_layout_test_results(self, results):
- if not results:
- print " No results."
- return
- for title, files in results.parsed_results().items():
- print " %s" % title
- for filename in files:
- print " %s" % filename
-
- def execute(self, options, args, tool):
- builders = self._tool.buildbot.builders()
- for builder in builders:
- print "%s:" % builder.name()
- build = builder.build_for_revision(args[0], allow_failed_lookups=True)
- self._print_layout_test_results(build.layout_test_results())
-
-
-class FailureReason(AbstractDeclarativeCommand):
- name = "failure-reason"
- help_text = "Lists revisions where individual test failures started at %s" % BuildBot.default_host
-
- def _blame_line_for_revision(self, revision):
- try:
- commit_info = self._tool.checkout().commit_info_for_revision(revision)
- except Exception, e:
- return "FAILED to fetch CommitInfo for r%s, exception: %s" % (revision, e)
- if not commit_info:
- return "FAILED to fetch CommitInfo for r%s, likely missing ChangeLog" % revision
- return commit_info.blame_string(self._tool.bugs)
-
- def _print_blame_information_for_transition(self, regression_window, failing_tests):
- red_build = regression_window.failing_build()
- print "SUCCESS: Build %s (r%s) was the first to show failures: %s" % (red_build._number, red_build.revision(), failing_tests)
- print "Suspect revisions:"
- for revision in regression_window.revisions():
- print self._blame_line_for_revision(revision)
-
- def _explain_failures_for_builder(self, builder, start_revision):
- print "Examining failures for \"%s\", starting at r%s" % (builder.name(), start_revision)
- revision_to_test = start_revision
- build = builder.build_for_revision(revision_to_test, allow_failed_lookups=True)
- layout_test_results = build.layout_test_results()
- if not layout_test_results:
- # FIXME: This could be made more user friendly.
- print "Failed to load layout test results; can't continue. (start revision = r%s)" % start_revision
- return 1
-
- results_to_explain = set(layout_test_results.failing_tests())
- last_build_with_results = build
- print "Starting at %s" % revision_to_test
- while results_to_explain:
- revision_to_test -= 1
- new_build = builder.build_for_revision(revision_to_test, allow_failed_lookups=True)
- if not new_build:
- print "No build for %s" % revision_to_test
- continue
- build = new_build
- latest_results = build.layout_test_results()
- if not latest_results:
- print "No results build %s (r%s)" % (build._number, build.revision())
- continue
- failures = set(latest_results.failing_tests())
- if len(failures) >= 20:
- # FIXME: We may need to move this logic into the LayoutTestResults class.
- # The buildbot stops runs after 20 failures so we don't have full results to work with here.
- print "Too many failures in build %s (r%s), ignoring." % (build._number, build.revision())
- continue
- fixed_results = results_to_explain - failures
- if not fixed_results:
- print "No change in build %s (r%s), %s unexplained failures (%s in this build)" % (build._number, build.revision(), len(results_to_explain), len(failures))
- last_build_with_results = build
- continue
- regression_window = RegressionWindow(build, last_build_with_results)
- self._print_blame_information_for_transition(regression_window, fixed_results)
- last_build_with_results = build
- results_to_explain -= fixed_results
- if results_to_explain:
- print "Failed to explain failures: %s" % results_to_explain
- return 1
- print "Explained all results for %s" % builder.name()
- return 0
-
- def _builder_to_explain(self):
- builder_statuses = self._tool.buildbot.builder_statuses()
- red_statuses = [status for status in builder_statuses if not status["is_green"]]
- print "%s failing" % (pluralize("builder", len(red_statuses)))
- builder_choices = [status["name"] for status in red_statuses]
- # We could offer an "All" choice here.
- chosen_name = User.prompt_with_list("Which builder to diagnose:", builder_choices)
- # FIXME: prompt_with_list should really take a set of objects and a set of names and then return the object.
- for status in red_statuses:
- if status["name"] == chosen_name:
- return (self._tool.buildbot.builder_with_name(chosen_name), status["built_revision"])
-
- def execute(self, options, args, tool):
- (builder, latest_revision) = self._builder_to_explain()
- start_revision = self._tool.user.prompt("Revision to walk backwards from? [%s] " % latest_revision) or latest_revision
- if not start_revision:
- print "Revision required."
- return 1
- return self._explain_failures_for_builder(builder, start_revision=int(start_revision))
-
-
-class FindFlakyTests(AbstractDeclarativeCommand):
- name = "find-flaky-tests"
- help_text = "Lists tests that often fail for a single build at %s" % BuildBot.default_host
-
- def _find_failures(self, builder, revision):
- build = builder.build_for_revision(revision, allow_failed_lookups=True)
- if not build:
- print "No build for %s" % revision
- return (None, None)
- results = build.layout_test_results()
- if not results:
- print "No results build %s (r%s)" % (build._number, build.revision())
- return (None, None)
- failures = set(results.failing_tests())
- if len(failures) >= 20:
- # FIXME: We may need to move this logic into the LayoutTestResults class.
- # The buildbot stops runs after 20 failures so we don't have full results to work with here.
- print "Too many failures in build %s (r%s), ignoring." % (build._number, build.revision())
- return (None, None)
- return (build, failures)
-
- def _increment_statistics(self, flaky_tests, flaky_test_statistics):
- for test in flaky_tests:
- count = flaky_test_statistics.get(test, 0)
- flaky_test_statistics[test] = count + 1
-
- def _print_statistics(self, statistics):
- print "=== Results ==="
- print "Occurances Test name"
- for value, key in sorted([(value, key) for key, value in statistics.items()]):
- print "%10d %s" % (value, key)
-
- def _walk_backwards_from(self, builder, start_revision, limit):
- flaky_test_statistics = {}
- all_previous_failures = set([])
- one_time_previous_failures = set([])
- previous_build = None
- for i in range(limit):
- revision = start_revision - i
- print "Analyzing %s ... " % revision,
- (build, failures) = self._find_failures(builder, revision)
- if failures == None:
- # Notice that we don't loop on the empty set!
- continue
- print "has %s failures" % len(failures)
- flaky_tests = one_time_previous_failures - failures
- if flaky_tests:
- print "Flaky tests: %s %s" % (sorted(flaky_tests),
- previous_build.results_url())
- self._increment_statistics(flaky_tests, flaky_test_statistics)
- one_time_previous_failures = failures - all_previous_failures
- all_previous_failures = failures
- previous_build = build
- self._print_statistics(flaky_test_statistics)
-
- def _builder_to_analyze(self):
- statuses = self._tool.buildbot.builder_statuses()
- choices = [status["name"] for status in statuses]
- chosen_name = User.prompt_with_list("Which builder to analyze:", choices)
- for status in statuses:
- if status["name"] == chosen_name:
- return (self._tool.buildbot.builder_with_name(chosen_name), status["built_revision"])
-
- def execute(self, options, args, tool):
- (builder, latest_revision) = self._builder_to_analyze()
- limit = self._tool.user.prompt("How many revisions to look through? [10000] ") or 10000
- return self._walk_backwards_from(builder, latest_revision, limit=int(limit))
-
-
-class TreeStatus(AbstractDeclarativeCommand):
- name = "tree-status"
- help_text = "Print the status of the %s buildbots" % BuildBot.default_host
- long_help = """Fetches build status from http://build.webkit.org/one_box_per_builder
-and displayes the status of each builder."""
-
- def execute(self, options, args, tool):
- for builder in tool.buildbot.builder_statuses():
- status_string = "ok" if builder["is_green"] else "FAIL"
- print "%s : %s" % (status_string.ljust(4), builder["name"])
-
-
-class SkippedPorts(AbstractDeclarativeCommand):
- name = "skipped-ports"
- help_text = "Print the list of ports skipping the given layout test(s)"
- long_help = """Scans the the Skipped file of each port and figure
-out what ports are skipping the test(s). Categories are taken in account too."""
- argument_names = "TEST_NAME"
-
- def execute(self, options, args, tool):
- class Options:
- # Required for chromium port.
- use_drt = True
-
- results = dict([(test_name, []) for test_name in args])
- for port_name, port_object in tool.port_factory.get_all(options=Options).iteritems():
- for test_name in args:
- if port_object.skips_layout_test(test_name):
- results[test_name].append(port_name)
-
- for test_name, ports in results.iteritems():
- if ports:
- print "Ports skipping test %r: %s" % (test_name, ', '.join(ports))
- else:
- print "Test %r is not skipped by any port." % test_name
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py
deleted file mode 100644
index 05a4a5c..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/queries_unittest.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# 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
-# 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 unittest
-
-from webkitpy.common.net.bugzilla import Bugzilla
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.tool.commands.commandtest import CommandsTest
-from webkitpy.tool.commands.queries import *
-from webkitpy.tool.mocktool import MockTool
-
-
-class QueryCommandsTest(CommandsTest):
- def test_bugs_to_commit(self):
- expected_stderr = "Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)\n"
- self.assert_execute_outputs(BugsToCommit(), None, "42\n77\n", expected_stderr)
-
- def test_patches_in_commit_queue(self):
- expected_stdout = "http://example.com/197\nhttp://example.com/103\n"
- expected_stderr = "Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)\nPatches in commit queue:\n"
- self.assert_execute_outputs(PatchesInCommitQueue(), None, expected_stdout, expected_stderr)
-
- def test_patches_to_commit_queue(self):
- expected_stdout = "http://example.com/104&action=edit\n"
- expected_stderr = "197 already has cq=+\n128 already has cq=+\n105 committer = \"Eric Seidel\" <eric@webkit.org>\n"
- options = Mock()
- options.bugs = False
- self.assert_execute_outputs(PatchesToCommitQueue(), None, expected_stdout, expected_stderr, options=options)
-
- expected_stdout = "http://example.com/77\n"
- options.bugs = True
- self.assert_execute_outputs(PatchesToCommitQueue(), None, expected_stdout, expected_stderr, options=options)
-
- def test_patches_to_review(self):
- expected_stdout = "103\n"
- expected_stderr = "Patches pending review:\n"
- self.assert_execute_outputs(PatchesToReview(), None, expected_stdout, expected_stderr)
-
- def test_tree_status(self):
- expected_stdout = "ok : Builder1\nok : Builder2\n"
- self.assert_execute_outputs(TreeStatus(), None, expected_stdout)
-
- def test_skipped_ports(self):
- expected_stdout = "Ports skipping test 'media/foo/bar.html': test_port1, test_port2\n"
- self.assert_execute_outputs(SkippedPorts(), ("media/foo/bar.html",), expected_stdout)
-
- expected_stdout = "Ports skipping test 'foo': test_port1\n"
- self.assert_execute_outputs(SkippedPorts(), ("foo",), expected_stdout)
-
- expected_stdout = "Test 'media' is not skipped by any port.\n"
- self.assert_execute_outputs(SkippedPorts(), ("media",), expected_stdout)
-
-
-class FailureReasonTest(unittest.TestCase):
- def test_blame_line_for_revision(self):
- tool = MockTool()
- command = FailureReason()
- command.bind_to_tool(tool)
- # This is an artificial example, mostly to test the CommitInfo lookup failure case.
- self.assertEquals(command._blame_line_for_revision(None), "FAILED to fetch CommitInfo for rNone, likely missing ChangeLog")
-
- def raising_mock(self):
- raise Exception("MESSAGE")
- tool.checkout().commit_info_for_revision = raising_mock
- self.assertEquals(command._blame_line_for_revision(None), "FAILED to fetch CommitInfo for rNone, exception: MESSAGE")
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queues.py b/WebKitTools/Scripts/webkitpy/tool/commands/queues.py
deleted file mode 100644
index bfaeb08..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/queues.py
+++ /dev/null
@@ -1,417 +0,0 @@
-# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import with_statement
-
-import codecs
-import time
-import traceback
-import os
-
-from datetime import datetime
-from optparse import make_option
-from StringIO import StringIO
-
-from webkitpy.common.config.committervalidator import CommitterValidator
-from webkitpy.common.net.bugzilla import Attachment
-from webkitpy.common.net.layouttestresults import path_for_layout_test, LayoutTestResults
-from webkitpy.common.net.statusserver import StatusServer
-from webkitpy.common.system.deprecated_logging import error, log
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.tool.bot.commitqueuetask import CommitQueueTask, CommitQueueTaskDelegate
-from webkitpy.tool.bot.feeders import CommitQueueFeeder, EWSFeeder
-from webkitpy.tool.bot.queueengine import QueueEngine, QueueEngineDelegate
-from webkitpy.tool.commands.stepsequence import StepSequenceErrorHandler
-from webkitpy.tool.grammar import pluralize, join_with_separators
-from webkitpy.tool.multicommandtool import Command, TryAgain
-
-
-class AbstractQueue(Command, QueueEngineDelegate):
- watchers = [
- ]
-
- _pass_status = "Pass"
- _fail_status = "Fail"
- _retry_status = "Retry"
- _error_status = "Error"
-
- def __init__(self, options=None): # Default values should never be collections (like []) as default values are shared between invocations
- options_list = (options or []) + [
- make_option("--no-confirm", action="store_false", dest="confirm", default=True, help="Do not ask the user for confirmation before running the queue. Dangerous!"),
- make_option("--exit-after-iteration", action="store", type="int", dest="iterations", default=None, help="Stop running the queue after iterating this number of times."),
- ]
- Command.__init__(self, "Run the %s" % self.name, options=options_list)
- self._iteration_count = 0
-
- def _cc_watchers(self, bug_id):
- try:
- self._tool.bugs.add_cc_to_bug(bug_id, self.watchers)
- except Exception, e:
- traceback.print_exc()
- log("Failed to CC watchers.")
-
- def run_webkit_patch(self, args):
- webkit_patch_args = [self._tool.path()]
- # FIXME: This is a hack, we should have a more general way to pass global options.
- # FIXME: We must always pass global options and their value in one argument
- # because our global option code looks for the first argument which does
- # not begin with "-" and assumes that is the command name.
- webkit_patch_args += ["--status-host=%s" % self._tool.status_server.host]
- if self._tool.status_server.bot_id:
- webkit_patch_args += ["--bot-id=%s" % self._tool.status_server.bot_id]
- if self._options.port:
- webkit_patch_args += ["--port=%s" % self._options.port]
- webkit_patch_args.extend(args)
- # FIXME: There is probably no reason to use run_and_throw_if_fail anymore.
- # run_and_throw_if_fail was invented to support tee'd output
- # (where we write both to a log file and to the console at once),
- # but the queues don't need live-progress, a dump-of-output at the
- # end should be sufficient.
- return self._tool.executive.run_and_throw_if_fail(webkit_patch_args)
-
- def _log_directory(self):
- return "%s-logs" % self.name
-
- # QueueEngineDelegate methods
-
- def queue_log_path(self):
- return os.path.join(self._log_directory(), "%s.log" % self.name)
-
- def work_item_log_path(self, work_item):
- raise NotImplementedError, "subclasses must implement"
-
- def begin_work_queue(self):
- log("CAUTION: %s will discard all local changes in \"%s\"" % (self.name, self._tool.scm().checkout_root))
- if self._options.confirm:
- response = self._tool.user.prompt("Are you sure? Type \"yes\" to continue: ")
- if (response != "yes"):
- error("User declined.")
- log("Running WebKit %s." % self.name)
- self._tool.status_server.update_status(self.name, "Starting Queue")
-
- def stop_work_queue(self, reason):
- self._tool.status_server.update_status(self.name, "Stopping Queue, reason: %s" % reason)
-
- def should_continue_work_queue(self):
- self._iteration_count += 1
- return not self._options.iterations or self._iteration_count <= self._options.iterations
-
- def next_work_item(self):
- raise NotImplementedError, "subclasses must implement"
-
- def should_proceed_with_work_item(self, work_item):
- raise NotImplementedError, "subclasses must implement"
-
- def process_work_item(self, work_item):
- raise NotImplementedError, "subclasses must implement"
-
- def handle_unexpected_error(self, work_item, message):
- raise NotImplementedError, "subclasses must implement"
-
- # Command methods
-
- def execute(self, options, args, tool, engine=QueueEngine):
- self._options = options # FIXME: This code is wrong. Command.options is a list, this assumes an Options element!
- self._tool = tool # FIXME: This code is wrong too! Command.bind_to_tool handles this!
- return engine(self.name, self, self._tool.wakeup_event).run()
-
- @classmethod
- def _log_from_script_error_for_upload(cls, script_error, output_limit=None):
- # We have seen request timeouts with app engine due to large
- # log uploads. Trying only the last 512k.
- if not output_limit:
- output_limit = 512 * 1024 # 512k
- output = script_error.message_with_output(output_limit=output_limit)
- # We pre-encode the string to a byte array before passing it
- # to status_server, because ClientForm (part of mechanize)
- # wants a file-like object with pre-encoded data.
- return StringIO(output.encode("utf-8"))
-
- @classmethod
- def _update_status_for_script_error(cls, tool, state, script_error, is_error=False):
- message = str(script_error)
- if is_error:
- message = "Error: %s" % message
- failure_log = cls._log_from_script_error_for_upload(script_error)
- return tool.status_server.update_status(cls.name, message, state["patch"], failure_log)
-
-
-class FeederQueue(AbstractQueue):
- name = "feeder-queue"
-
- _sleep_duration = 30 # seconds
-
- # AbstractPatchQueue methods
-
- def begin_work_queue(self):
- AbstractQueue.begin_work_queue(self)
- self.feeders = [
- CommitQueueFeeder(self._tool),
- EWSFeeder(self._tool),
- ]
-
- def next_work_item(self):
- # This really show inherit from some more basic class that doesn't
- # understand work items, but the base class in the heirarchy currently
- # understands work items.
- return "synthetic-work-item"
-
- def should_proceed_with_work_item(self, work_item):
- return True
-
- def process_work_item(self, work_item):
- for feeder in self.feeders:
- feeder.feed()
- time.sleep(self._sleep_duration)
- return True
-
- def work_item_log_path(self, work_item):
- return None
-
- def handle_unexpected_error(self, work_item, message):
- log(message)
-
-
-class AbstractPatchQueue(AbstractQueue):
- def _update_status(self, message, patch=None, results_file=None):
- return self._tool.status_server.update_status(self.name, message, patch, results_file)
-
- def _next_patch(self):
- patch_id = self._tool.status_server.next_work_item(self.name)
- if not patch_id:
- return None
- patch = self._tool.bugs.fetch_attachment(patch_id)
- if not patch:
- # FIXME: Using a fake patch because release_work_item has the wrong API.
- # We also don't really need to release the lock (although that's fine),
- # mostly we just need to remove this bogus patch from our queue.
- # If for some reason bugzilla is just down, then it will be re-fed later.
- patch = Attachment({'id': patch_id}, None)
- self._release_work_item(patch)
- return None
- return patch
-
- def _release_work_item(self, patch):
- self._tool.status_server.release_work_item(self.name, patch)
-
- def _did_pass(self, patch):
- self._update_status(self._pass_status, patch)
- self._release_work_item(patch)
-
- def _did_fail(self, patch):
- self._update_status(self._fail_status, patch)
- self._release_work_item(patch)
-
- def _did_retry(self, patch):
- self._update_status(self._retry_status, patch)
- self._release_work_item(patch)
-
- def _did_error(self, patch, reason):
- message = "%s: %s" % (self._error_status, reason)
- self._update_status(message, patch)
- self._release_work_item(patch)
-
- def work_item_log_path(self, patch):
- return os.path.join(self._log_directory(), "%s.log" % patch.bug_id())
-
-
-class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler, CommitQueueTaskDelegate):
- name = "commit-queue"
-
- # AbstractPatchQueue methods
-
- def begin_work_queue(self):
- AbstractPatchQueue.begin_work_queue(self)
- self.committer_validator = CommitterValidator(self._tool.bugs)
-
- def next_work_item(self):
- return self._next_patch()
-
- def should_proceed_with_work_item(self, patch):
- patch_text = "rollout patch" if patch.is_rollout() else "patch"
- self._update_status("Processing %s" % patch_text, patch)
- return True
-
- def process_work_item(self, patch):
- self._cc_watchers(patch.bug_id())
- task = CommitQueueTask(self, patch)
- try:
- if task.run():
- self._did_pass(patch)
- return True
- self._did_retry(patch)
- except ScriptError, e:
- validator = CommitterValidator(self._tool.bugs)
- validator.reject_patch_from_commit_queue(patch.id(), self._error_message_for_bug(task.failure_status_id, e))
- self._did_fail(patch)
-
- def _error_message_for_bug(self, status_id, script_error):
- if not script_error.output:
- return script_error.message_with_output()
- results_link = self._tool.status_server.results_url_for_status(status_id)
- return "%s\nFull output: %s" % (script_error.message_with_output(), results_link)
-
- def handle_unexpected_error(self, patch, message):
- self.committer_validator.reject_patch_from_commit_queue(patch.id(), message)
-
- # CommitQueueTaskDelegate methods
-
- def run_command(self, command):
- self.run_webkit_patch(command)
-
- def command_passed(self, message, patch):
- self._update_status(message, patch=patch)
-
- def command_failed(self, message, script_error, patch):
- failure_log = self._log_from_script_error_for_upload(script_error)
- return self._update_status(message, patch=patch, results_file=failure_log)
-
- # FIXME: This exists for mocking, but should instead be mocked via
- # some sort of tool.filesystem() object.
- def _read_file_contents(self, path):
- try:
- with codecs.open(path, "r", "utf-8") as open_file:
- return open_file.read()
- except OSError, e: # File does not exist or can't be read.
- return None
-
- # FIXME: This may belong on the Port object.
- def layout_test_results(self):
- results_path = self._tool.port().layout_tests_results_path()
- results_html = self._read_file_contents(results_path)
- if not results_html:
- return None
- return LayoutTestResults.results_from_string(results_html)
-
- def refetch_patch(self, patch):
- return self._tool.bugs.fetch_attachment(patch.id())
-
- def _author_emails_for_tests(self, flaky_tests):
- test_paths = map(path_for_layout_test, flaky_tests)
- commit_infos = self._tool.checkout().recent_commit_infos_for_files(test_paths)
- return set([commit_info.author().bugzilla_email() for commit_info in commit_infos if commit_info.author()])
-
- def report_flaky_tests(self, patch, flaky_tests):
- message = "The %s encountered the following flaky tests while processing attachment %s:" % (self.name, patch.id())
- message += "\n\n%s\n\n" % ("\n".join(flaky_tests))
- message += "Please file bugs against the tests. "
- author_emails = self._author_emails_for_tests(flaky_tests)
- if author_emails:
- message += "These tests were authored by %s. " % (join_with_separators(sorted(author_emails)))
- message += "The commit-queue is continuing to process your patch."
- self._tool.bugs.post_comment_to_bug(patch.bug_id(), message)
-
- # StepSequenceErrorHandler methods
-
- def handle_script_error(cls, tool, state, script_error):
- # Hitting this error handler should be pretty rare. It does occur,
- # however, when a patch no longer applies to top-of-tree in the final
- # land step.
- log(script_error.message_with_output())
-
- @classmethod
- def handle_checkout_needs_update(cls, tool, state, options, error):
- message = "Tests passed, but commit failed (checkout out of date). Updating, then landing without building or re-running tests."
- tool.status_server.update_status(cls.name, message, state["patch"])
- # The only time when we find out that out checkout needs update is
- # when we were ready to actually pull the trigger and land the patch.
- # Rather than spinning in the master process, we retry without
- # building or testing, which is much faster.
- options.build = False
- options.test = False
- options.update = True
- raise TryAgain()
-
-
-class AbstractReviewQueue(AbstractPatchQueue, StepSequenceErrorHandler):
- """This is the base-class for the EWS queues and the style-queue."""
- def __init__(self, options=None):
- AbstractPatchQueue.__init__(self, options)
-
- def review_patch(self, patch):
- raise NotImplementedError("subclasses must implement")
-
- # AbstractPatchQueue methods
-
- def begin_work_queue(self):
- AbstractPatchQueue.begin_work_queue(self)
-
- def next_work_item(self):
- return self._next_patch()
-
- def should_proceed_with_work_item(self, patch):
- raise NotImplementedError("subclasses must implement")
-
- def process_work_item(self, patch):
- try:
- if not self.review_patch(patch):
- return False
- self._did_pass(patch)
- return True
- except ScriptError, e:
- if e.exit_code != QueueEngine.handled_error_code:
- self._did_fail(patch)
- else:
- # The subprocess handled the error, but won't have released the patch, so we do.
- # FIXME: We need to simplify the rules by which _release_work_item is called.
- self._release_work_item(patch)
- raise e
-
- def handle_unexpected_error(self, patch, message):
- log(message)
-
- # StepSequenceErrorHandler methods
-
- @classmethod
- def handle_script_error(cls, tool, state, script_error):
- log(script_error.message_with_output())
-
-
-class StyleQueue(AbstractReviewQueue):
- name = "style-queue"
- def __init__(self):
- AbstractReviewQueue.__init__(self)
-
- def should_proceed_with_work_item(self, patch):
- self._update_status("Checking style", patch)
- return True
-
- def review_patch(self, patch):
- self.run_webkit_patch(["check-style", "--force-clean", "--non-interactive", "--parent-command=style-queue", patch.id()])
- return True
-
- @classmethod
- def handle_script_error(cls, tool, state, script_error):
- is_svn_apply = script_error.command_name() == "svn-apply"
- status_id = cls._update_status_for_script_error(tool, state, script_error, is_error=is_svn_apply)
- if is_svn_apply:
- QueueEngine.exit_after_handled_error(script_error)
- message = "Attachment %s did not pass %s:\n\n%s\n\nIf any of these errors are false positives, please file a bug against check-webkit-style." % (state["patch"].id(), cls.name, script_error.message_with_output(output_limit=3*1024))
- tool.bugs.post_comment_to_bug(state["patch"].bug_id(), message, cc=cls.watchers)
- exit(1)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py
deleted file mode 100644
index b45db7b..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py
+++ /dev/null
@@ -1,378 +0,0 @@
-# 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
-# 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 os
-
-from webkitpy.common.checkout.scm import CheckoutNeedsUpdate
-from webkitpy.common.config.committers import Committer
-from webkitpy.common.net.bugzilla import Attachment
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.tool.commands.commandtest import CommandsTest
-from webkitpy.tool.commands.queues import *
-from webkitpy.tool.commands.queuestest import QueuesTest
-from webkitpy.tool.commands.stepsequence import StepSequence
-from webkitpy.tool.mocktool import MockTool, MockSCM, MockStatusServer
-
-
-class TestQueue(AbstractPatchQueue):
- name = "test-queue"
-
-
-class TestReviewQueue(AbstractReviewQueue):
- name = "test-review-queue"
-
-
-class TestFeederQueue(FeederQueue):
- _sleep_duration = 0
-
-
-class AbstractQueueTest(CommandsTest):
- def test_log_directory(self):
- self.assertEquals(TestQueue()._log_directory(), "test-queue-logs")
-
- def _assert_run_webkit_patch(self, run_args, port=None):
- queue = TestQueue()
- tool = MockTool()
- tool.status_server.bot_id = "gort"
- tool.executive = Mock()
- queue.bind_to_tool(tool)
- queue._options = Mock()
- queue._options.port = port
-
- queue.run_webkit_patch(run_args)
- expected_run_args = ["echo", "--status-host=example.com", "--bot-id=gort"]
- if port:
- expected_run_args.append("--port=%s" % port)
- expected_run_args.extend(run_args)
- tool.executive.run_and_throw_if_fail.assert_called_with(expected_run_args)
-
- def test_run_webkit_patch(self):
- self._assert_run_webkit_patch([1])
- self._assert_run_webkit_patch(["one", 2])
- self._assert_run_webkit_patch([1], port="mockport")
-
- def test_iteration_count(self):
- queue = TestQueue()
- queue._options = Mock()
- queue._options.iterations = 3
- self.assertTrue(queue.should_continue_work_queue())
- self.assertTrue(queue.should_continue_work_queue())
- self.assertTrue(queue.should_continue_work_queue())
- self.assertFalse(queue.should_continue_work_queue())
-
- def test_no_iteration_count(self):
- queue = TestQueue()
- queue._options = Mock()
- self.assertTrue(queue.should_continue_work_queue())
- self.assertTrue(queue.should_continue_work_queue())
- self.assertTrue(queue.should_continue_work_queue())
- self.assertTrue(queue.should_continue_work_queue())
-
- def _assert_log_message(self, script_error, log_message):
- failure_log = AbstractQueue._log_from_script_error_for_upload(script_error, output_limit=10)
- self.assertTrue(failure_log.read(), log_message)
-
- def test_log_from_script_error_for_upload(self):
- self._assert_log_message(ScriptError("test"), "test")
- # In python 2.5 unicode(Exception) is busted. See:
- # http://bugs.python.org/issue2517
- # With no good workaround, we just ignore these tests.
- if not hasattr(Exception, "__unicode__"):
- return
-
- unicode_tor = u"WebKit \u2661 Tor Arne Vestb\u00F8!"
- utf8_tor = unicode_tor.encode("utf-8")
- self._assert_log_message(ScriptError(unicode_tor), utf8_tor)
- script_error = ScriptError(unicode_tor, output=unicode_tor)
- expected_output = "%s\nLast %s characters of output:\n%s" % (utf8_tor, 10, utf8_tor[-10:])
- self._assert_log_message(script_error, expected_output)
-
-
-class FeederQueueTest(QueuesTest):
- def test_feeder_queue(self):
- queue = TestFeederQueue()
- tool = MockTool(log_executive=True)
- expected_stderr = {
- "begin_work_queue": self._default_begin_work_queue_stderr("feeder-queue", MockSCM.fake_checkout_root),
- "should_proceed_with_work_item": "",
- "next_work_item": "",
- "process_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 setting flag 'commit-queue' to '-' on attachment '128' with comment 'Rejecting patch 128 from commit-queue.' and additional comment 'non-committer@example.com does not have committer permissions according to http://trac.webkit.org/browser/trunk/WebKitTools/Scripts/webkitpy/common/config/committers.py.
-
-- If you do not have committer rights please read http://webkit.org/coding/contributing.html for instructions on how to use bugzilla flags.
-
-- If you have committer rights please correct the error in WebKitTools/Scripts/webkitpy/common/config/committers.py by adding yourself to the file (no review needed). The commit-queue restarts itself every 2 hours. After restart the commit-queue will correctly respect your committer rights.'
-MOCK: update_work_items: commit-queue [106, 197]
-Feeding commit-queue items [106, 197]
-Feeding EWS (1 r? patch, 1 new)
-MOCK: submit_to_ews: 103
-""",
- "handle_unexpected_error": "Mock error message\n",
- }
- self.assert_queue_outputs(queue, tool=tool, expected_stderr=expected_stderr)
-
-
-class AbstractPatchQueueTest(CommandsTest):
- def test_next_patch(self):
- queue = AbstractPatchQueue()
- tool = MockTool()
- queue.bind_to_tool(tool)
- queue._options = Mock()
- queue._options.port = None
- self.assertEquals(queue._next_patch(), None)
- tool.status_server = MockStatusServer(work_items=[2, 197])
- expected_stdout = "MOCK: fetch_attachment: 2 is not a known attachment id\n" # A mock-only message to prevent us from making mistakes.
- expected_stderr = "MOCK: release_work_item: None 2\n"
- patch_id = OutputCapture().assert_outputs(self, queue._next_patch, [], expected_stdout=expected_stdout, expected_stderr=expected_stderr)
- self.assertEquals(patch_id, None) # 2 is an invalid patch id
- self.assertEquals(queue._next_patch().id(), 197)
-
-
-class NeedsUpdateSequence(StepSequence):
- def _run(self, tool, options, state):
- raise CheckoutNeedsUpdate([], 1, "", None)
-
-
-class AlwaysCommitQueueTool(object):
- def __init__(self):
- self.status_server = MockStatusServer()
-
- def command_by_name(self, name):
- return CommitQueue
-
-
-class SecondThoughtsCommitQueue(CommitQueue):
- def __init__(self):
- self._reject_patch = False
- CommitQueue.__init__(self)
-
- def run_command(self, command):
- # We want to reject the patch after the first validation,
- # so wait to reject it until after some other command has run.
- self._reject_patch = True
- return CommitQueue.run_command(self, command)
-
- def refetch_patch(self, patch):
- if not self._reject_patch:
- return self._tool.bugs.fetch_attachment(patch.id())
-
- attachment_dictionary = {
- "id": patch.id(),
- "bug_id": patch.bug_id(),
- "name": "Rejected",
- "is_obsolete": True,
- "is_patch": False,
- "review": "-",
- "reviewer_email": "foo@bar.com",
- "commit-queue": "-",
- "committer_email": "foo@bar.com",
- "attacher_email": "Contributer1",
- }
- return Attachment(attachment_dictionary, None)
-
-
-# Creating fake CommitInfos is a pain, so we use a mock one here.
-class MockCommitInfo(object):
- def __init__(self, author_email):
- self._author_email = author_email
-
- def author(self):
- # It's definitely possible to have commits with authors who
- # are not in our committers.py list.
- if not self._author_email:
- return None
- return Committer("Mock Committer", self._author_email)
-
-
-class CommitQueueTest(QueuesTest):
- def test_commit_queue(self):
- expected_stderr = {
- "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root),
- "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing patch\n",
- "next_work_item": "",
- "process_work_item": """MOCK: update_status: commit-queue Applied patch
-MOCK: update_status: commit-queue Built patch
-MOCK: update_status: commit-queue Passed tests
-MOCK: update_status: commit-queue Landed patch
-MOCK: update_status: commit-queue Pass
-MOCK: release_work_item: commit-queue 197
-""",
- "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'Mock error message'\n",
- "handle_script_error": "ScriptError error message\n",
- }
- self.assert_queue_outputs(CommitQueue(), expected_stderr=expected_stderr)
-
- def test_commit_queue_failure(self):
- expected_stderr = {
- "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root),
- "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing patch\n",
- "next_work_item": "",
- "process_work_item": """MOCK: update_status: commit-queue Patch does not apply
-MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'MOCK script error'
-MOCK: update_status: commit-queue Fail
-MOCK: release_work_item: commit-queue 197
-""",
- "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'Mock error message'\n",
- "handle_script_error": "ScriptError error message\n",
- }
- queue = CommitQueue()
-
- def mock_run_webkit_patch(command):
- raise ScriptError('MOCK script error')
-
- queue.run_webkit_patch = mock_run_webkit_patch
- self.assert_queue_outputs(queue, expected_stderr=expected_stderr)
-
- def test_rollout(self):
- tool = MockTool(log_executive=True)
- tool.buildbot.light_tree_on_fire()
- expected_stderr = {
- "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root),
- "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing patch\n",
- "next_work_item": "",
- "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-attachment', '--force-clean', '--non-interactive', 197]
-MOCK: update_status: commit-queue Applied patch
-MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build', '--no-clean', '--no-update', '--build-style=both']
-MOCK: update_status: commit-queue Built patch
-MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
-MOCK: update_status: commit-queue Passed tests
-MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 197]
-MOCK: update_status: commit-queue Landed patch
-MOCK: update_status: commit-queue Pass
-MOCK: release_work_item: commit-queue 197
-""",
- "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'Mock error message'\n",
- "handle_script_error": "ScriptError error message\n",
- }
- self.assert_queue_outputs(CommitQueue(), tool=tool, expected_stderr=expected_stderr)
-
- def test_rollout_lands(self):
- tool = MockTool(log_executive=True)
- tool.buildbot.light_tree_on_fire()
- rollout_patch = tool.bugs.fetch_attachment(106) # _patch6, a rollout patch.
- assert(rollout_patch.is_rollout())
- expected_stderr = {
- "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root),
- "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing rollout patch\n",
- "next_work_item": "",
- "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-attachment', '--force-clean', '--non-interactive', 106]
-MOCK: update_status: commit-queue Applied patch
-MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build', '--no-clean', '--no-update', '--build-style=both']
-MOCK: update_status: commit-queue Built patch
-MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 106]
-MOCK: update_status: commit-queue Landed patch
-MOCK: update_status: commit-queue Pass
-MOCK: release_work_item: commit-queue 106
-""",
- "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '106' with comment 'Rejecting patch 106 from commit-queue.' and additional comment 'Mock error message'\n",
- "handle_script_error": "ScriptError error message\n",
- }
- self.assert_queue_outputs(CommitQueue(), tool=tool, work_item=rollout_patch, expected_stderr=expected_stderr)
-
- def test_auto_retry(self):
- queue = CommitQueue()
- options = Mock()
- options.parent_command = "commit-queue"
- tool = AlwaysCommitQueueTool()
- sequence = NeedsUpdateSequence(None)
-
- expected_stderr = "Commit failed because the checkout is out of date. Please update and try again.\nMOCK: update_status: commit-queue Tests passed, but commit failed (checkout out of date). Updating, then landing without building or re-running tests.\n"
- state = {'patch': None}
- OutputCapture().assert_outputs(self, sequence.run_and_handle_errors, [tool, options, state], expected_exception=TryAgain, expected_stderr=expected_stderr)
-
- self.assertEquals(options.update, True)
- self.assertEquals(options.build, False)
- self.assertEquals(options.test, False)
-
- def test_manual_reject_during_processing(self):
- queue = SecondThoughtsCommitQueue()
- queue.bind_to_tool(MockTool())
- queue._options = Mock()
- queue._options.port = None
- expected_stderr = """MOCK: update_status: commit-queue Applied patch
-MOCK: update_status: commit-queue Built patch
-MOCK: update_status: commit-queue Passed tests
-MOCK: update_status: commit-queue Retry
-MOCK: release_work_item: commit-queue 197
-"""
- OutputCapture().assert_outputs(self, queue.process_work_item, [QueuesTest.mock_work_item], expected_stderr=expected_stderr)
-
- def _assert_emails_for_tests(self, emails):
- queue = CommitQueue()
- tool = MockTool()
- queue.bind_to_tool(tool)
- commit_infos = [MockCommitInfo(email) for email in emails]
- tool.checkout().recent_commit_infos_for_files = lambda paths: set(commit_infos)
- self.assertEqual(queue._author_emails_for_tests([]), set(emails))
-
- def test_author_emails_for_tests(self):
- self._assert_emails_for_tests([])
- self._assert_emails_for_tests(["test1@test.com", "test1@test.com"])
- self._assert_emails_for_tests(["test1@test.com", "test2@test.com"])
-
- def test_report_flaky_tests(self):
- queue = CommitQueue()
- queue.bind_to_tool(MockTool())
- expected_stderr = """MOCK bug comment: bug_id=42, cc=None
---- Begin comment ---
-The commit-queue encountered the following flaky tests while processing attachment 197:
-
-foo/bar.html
-bar/baz.html
-
-Please file bugs against the tests. These tests were authored by abarth@webkit.org. The commit-queue is continuing to process your patch.
---- End comment ---
-
-"""
- OutputCapture().assert_outputs(self, queue.report_flaky_tests, [QueuesTest.mock_work_item, ["foo/bar.html", "bar/baz.html"]], expected_stderr=expected_stderr)
-
- def test_layout_test_results(self):
- queue = CommitQueue()
- queue.bind_to_tool(MockTool())
- queue._read_file_contents = lambda path: None
- self.assertEquals(queue.layout_test_results(), None)
- queue._read_file_contents = lambda path: ""
- self.assertEquals(queue.layout_test_results(), None)
-
-
-class StyleQueueTest(QueuesTest):
- def test_style_queue(self):
- expected_stderr = {
- "begin_work_queue": self._default_begin_work_queue_stderr("style-queue", MockSCM.fake_checkout_root),
- "next_work_item": "",
- "should_proceed_with_work_item": "MOCK: update_status: style-queue Checking style\n",
- "process_work_item": "MOCK: update_status: style-queue Pass\nMOCK: release_work_item: style-queue 197\n",
- "handle_unexpected_error": "Mock error message\n",
- "handle_script_error": "MOCK: update_status: style-queue ScriptError error message\nMOCK bug comment: bug_id=42, cc=[]\n--- Begin comment ---\nAttachment 197 did not pass style-queue:\n\nScriptError error message\n\nIf any of these errors are false positives, please file a bug against check-webkit-style.\n--- End comment ---\n\n",
- }
- expected_exceptions = {
- "handle_script_error": SystemExit,
- }
- self.assert_queue_outputs(StyleQueue(), expected_stderr=expected_stderr, expected_exceptions=expected_exceptions)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py
deleted file mode 100644
index 6455617..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# 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
-# 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 unittest
-
-from webkitpy.common.net.bugzilla import Attachment
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.tool.commands.stepsequence import StepSequenceErrorHandler
-from webkitpy.tool.mocktool import MockTool
-
-
-class MockQueueEngine(object):
- def __init__(self, name, queue, wakeup_event):
- pass
-
- def run(self):
- pass
-
-
-class QueuesTest(unittest.TestCase):
- # This is _patch1 in mocktool.py
- mock_work_item = MockTool().bugs.fetch_attachment(197)
-
- def assert_outputs(self, func, func_name, args, expected_stdout, expected_stderr, expected_exceptions):
- exception = None
- if expected_exceptions and func_name in expected_exceptions:
- exception = expected_exceptions[func_name]
-
- OutputCapture().assert_outputs(self,
- func,
- args=args,
- expected_stdout=expected_stdout.get(func_name, ""),
- expected_stderr=expected_stderr.get(func_name, ""),
- expected_exception=exception)
-
- def _default_begin_work_queue_stderr(self, name, checkout_dir):
- string_replacements = {"name": name, 'checkout_dir': checkout_dir}
- return "CAUTION: %(name)s will discard all local changes in \"%(checkout_dir)s\"\nRunning WebKit %(name)s.\nMOCK: update_status: %(name)s Starting Queue\n" % string_replacements
-
- def assert_queue_outputs(self, queue, args=None, work_item=None, expected_stdout=None, expected_stderr=None, expected_exceptions=None, options=None, tool=None):
- if not tool:
- tool = MockTool()
- if not expected_stdout:
- expected_stdout = {}
- if not expected_stderr:
- expected_stderr = {}
- if not args:
- args = []
- if not options:
- options = Mock()
- options.port = None
- if not work_item:
- work_item = self.mock_work_item
- tool.user.prompt = lambda message: "yes"
-
- queue.execute(options, args, tool, engine=MockQueueEngine)
-
- self.assert_outputs(queue.queue_log_path, "queue_log_path", [], expected_stdout, expected_stderr, expected_exceptions)
- self.assert_outputs(queue.work_item_log_path, "work_item_log_path", [work_item], expected_stdout, expected_stderr, expected_exceptions)
- self.assert_outputs(queue.begin_work_queue, "begin_work_queue", [], expected_stdout, expected_stderr, expected_exceptions)
- self.assert_outputs(queue.should_continue_work_queue, "should_continue_work_queue", [], expected_stdout, expected_stderr, expected_exceptions)
- self.assert_outputs(queue.next_work_item, "next_work_item", [], expected_stdout, expected_stderr, expected_exceptions)
- self.assert_outputs(queue.should_proceed_with_work_item, "should_proceed_with_work_item", [work_item], expected_stdout, expected_stderr, expected_exceptions)
- self.assert_outputs(queue.process_work_item, "process_work_item", [work_item], expected_stdout, expected_stderr, expected_exceptions)
- self.assert_outputs(queue.handle_unexpected_error, "handle_unexpected_error", [work_item, "Mock error message"], expected_stdout, expected_stderr, expected_exceptions)
- # Should we have a different function for testing StepSequenceErrorHandlers?
- if isinstance(queue, StepSequenceErrorHandler):
- self.assert_outputs(queue.handle_script_error, "handle_script_error", [tool, {"patch": self.mock_work_item}, ScriptError(message="ScriptError error message", script_args="MockErrorCommand")], expected_stdout, expected_stderr, expected_exceptions)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/rebaseline.py b/WebKitTools/Scripts/webkitpy/tool/commands/rebaseline.py
deleted file mode 100644
index abfa850..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/rebaseline.py
+++ /dev/null
@@ -1,114 +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.
-
-import os.path
-import re
-import shutil
-import urllib
-
-from webkitpy.common.net.buildbot import BuildBot, LayoutTestResults
-from webkitpy.common.system.user import User
-from webkitpy.layout_tests.port import factory
-from webkitpy.tool.grammar import pluralize
-from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand
-
-
-# FIXME: I'm not sure where this logic should go in the end.
-# For now it's here, until we have a second need for it.
-class BuilderToPort(object):
- _builder_name_to_port_name = {
- r"SnowLeopard": "mac-snowleopard",
- r"Leopard": "mac-leopard",
- r"Tiger": "mac-tiger",
- r"Windows": "win",
- r"GTK": "gtk",
- r"Qt": "qt",
- r"Chromium Mac": "chromium-mac",
- r"Chromium Linux": "chromium-linux",
- r"Chromium Win": "chromium-win",
- }
-
- def _port_name_for_builder_name(self, builder_name):
- for regexp, port_name in self._builder_name_to_port_name.items():
- if re.match(regexp, builder_name):
- return port_name
-
- def port_for_builder(self, builder_name):
- port_name = self._port_name_for_builder_name(builder_name)
- assert(port_name) # Need to update _builder_name_to_port_name
- port = factory.get(port_name)
- assert(port) # Need to update _builder_name_to_port_name
- return port
-
-
-class Rebaseline(AbstractDeclarativeCommand):
- name = "rebaseline"
- help_text = "Replaces local expected.txt files with new results from build bots"
-
- # FIXME: This should share more code with FailureReason._builder_to_explain
- def _builder_to_pull_from(self):
- builder_statuses = self._tool.buildbot.builder_statuses()
- red_statuses = [status for status in builder_statuses if not status["is_green"]]
- print "%s failing" % (pluralize("builder", len(red_statuses)))
- builder_choices = [status["name"] for status in red_statuses]
- chosen_name = self._tool.user.prompt_with_list("Which builder to pull results from:", builder_choices)
- # FIXME: prompt_with_list should really take a set of objects and a set of names and then return the object.
- for status in red_statuses:
- if status["name"] == chosen_name:
- return (self._tool.buildbot.builder_with_name(chosen_name), status["build_number"])
-
- def _replace_expectation_with_remote_result(self, local_file, remote_file):
- (downloaded_file, headers) = urllib.urlretrieve(remote_file)
- shutil.move(downloaded_file, local_file)
-
- def _tests_to_update(self, build):
- parsed_results = build.layout_test_results().parsed_results()
- # FIXME: This probably belongs as API on LayoutTestResults
- # but .failing_tests() already means something else.
- failing_tests = parsed_results[LayoutTestResults.fail_key]
- return self._tool.user.prompt_with_list("Which test(s) to rebaseline:", failing_tests, can_choose_multiple=True)
-
- def _results_url_for_test(self, build, test):
- test_base = os.path.splitext(test)[0]
- actual_path = test_base + "-actual.txt"
- return build.results_url() + "/" + actual_path
-
- def execute(self, options, args, tool):
- builder, build_number = self._builder_to_pull_from()
- build = builder.build(build_number)
- port = BuilderToPort().port_for_builder(builder.name())
-
- for test in self._tests_to_update(build):
- results_url = self._results_url_for_test(build, test)
- # Port operates with absolute paths.
- absolute_path = os.path.join(port.layout_tests_dir(), test)
- expected_file = port.expected_filename(absolute_path, ".txt")
- print test
- self._replace_expectation_with_remote_result(expected_file, results_url)
-
- # FIXME: We should handle new results too.
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
deleted file mode 100644
index d6582a7..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
+++ /dev/null
@@ -1,38 +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.
-
-import unittest
-
-from webkitpy.tool.commands.rebaseline import BuilderToPort
-
-
-class BuilderToPortTest(unittest.TestCase):
- def test_port_for_builder(self):
- converter = BuilderToPort()
- port = converter.port_for_builder("Leopard Intel Debug (Tests)")
- self.assertEqual(port.name(), "mac-leopard")
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver.py b/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver.py
deleted file mode 100644
index 2924a6f..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver.py
+++ /dev/null
@@ -1,268 +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.
-
-"""Starts a local HTTP server which displays layout test failures (given a test
-results directory), provides comparisons of expected and actual results (both
-images and text) and allows one-click rebaselining of tests."""
-from __future__ import with_statement
-
-import codecs
-import datetime
-import mimetypes
-import os
-import os.path
-import shutil
-import threading
-import time
-import urlparse
-import BaseHTTPServer
-
-from optparse import make_option
-from wsgiref.handlers import format_date_time
-
-from webkitpy.common import system
-from webkitpy.layout_tests.port import factory
-from webkitpy.layout_tests.port.webkit import WebKitPort
-from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand
-from webkitpy.thirdparty import simplejson
-
-STATE_NEEDS_REBASELINE = 'needs_rebaseline'
-STATE_REBASELINE_FAILED = 'rebaseline_failed'
-STATE_REBASELINE_SUCCEEDED = 'rebaseline_succeeded'
-
-class RebaselineHTTPServer(BaseHTTPServer.HTTPServer):
- def __init__(self, httpd_port, results_directory, results_json, platforms_json):
- BaseHTTPServer.HTTPServer.__init__(self, ("", httpd_port), RebaselineHTTPRequestHandler)
- self.results_directory = results_directory
- self.results_json = results_json
- self.platforms_json = platforms_json
-
-
-class RebaselineHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
- STATIC_FILE_NAMES = frozenset([
- "index.html",
- "loupe.js",
- "main.js",
- "main.css",
- "queue.js",
- "util.js",
- ])
-
- STATIC_FILE_DIRECTORY = os.path.join(
- os.path.dirname(__file__), "data", "rebaselineserver")
-
- def do_GET(self):
- self._handle_request()
-
- def do_POST(self):
- self._handle_request()
-
- def _handle_request(self):
- # Parse input.
- if "?" in self.path:
- path, query_string = self.path.split("?", 1)
- self.query = urlparse.parse_qs(query_string)
- else:
- path = self.path
- self.query = {}
- function_or_file_name = path[1:] or "index.html"
-
- # See if a static file matches.
- if function_or_file_name in RebaselineHTTPRequestHandler.STATIC_FILE_NAMES:
- self._serve_static_file(function_or_file_name)
- return
-
- # See if a class method matches.
- function_name = function_or_file_name.replace(".", "_")
- if not hasattr(self, function_name):
- self.send_error(404, "Unknown function %s" % function_name)
- return
- if function_name[0] == "_":
- self.send_error(
- 401, "Not allowed to invoke private or protected methods")
- return
- function = getattr(self, function_name)
- function()
-
- def _serve_static_file(self, static_path):
- self._serve_file(os.path.join(
- RebaselineHTTPRequestHandler.STATIC_FILE_DIRECTORY, static_path))
-
- def quitquitquit(self):
- self.send_response(200)
- self.send_header("Content-type", "text/plain")
- self.end_headers()
- self.wfile.write("Quit.\n")
-
- # Shutdown has to happen on another thread from the server's thread,
- # otherwise there's a deadlock
- threading.Thread(target=lambda: self.server.shutdown()).start()
-
- def test_result(self):
- test_name, _ = os.path.splitext(self.query['test'][0])
- mode = self.query['mode'][0]
- if mode == 'expected-image':
- file_name = test_name + '-expected.png'
- elif mode == 'actual-image':
- file_name = test_name + '-actual.png'
- if mode == 'expected-checksum':
- file_name = test_name + '-expected.checksum'
- elif mode == 'actual-checksum':
- file_name = test_name + '-actual.checksum'
- elif mode == 'diff-image':
- file_name = test_name + '-diff.png'
- if mode == 'expected-text':
- file_name = test_name + '-expected.txt'
- elif mode == 'actual-text':
- file_name = test_name + '-actual.txt'
- elif mode == 'diff-text':
- file_name = test_name + '-diff.txt'
-
- file_path = os.path.join(self.server.results_directory, file_name)
-
- # Let results be cached for 60 seconds, so that they can be pre-fetched
- # by the UI
- self._serve_file(file_path, cacheable_seconds=60)
-
- def results_json(self):
- self._serve_json(self.server.results_json)
-
- def platforms_json(self):
- self._serve_json(self.server.platforms_json)
-
- def _serve_json(self, json):
- self.send_response(200)
- self.send_header('Content-type', 'application/json')
- self.end_headers()
- simplejson.dump(json, self.wfile)
-
- def _serve_file(self, file_path, cacheable_seconds=0):
- if not os.path.exists(file_path):
- self.send_error(404, "File not found")
- return
- with codecs.open(file_path, "rb") as static_file:
- self.send_response(200)
- self.send_header("Content-Length", os.path.getsize(file_path))
- mime_type, encoding = mimetypes.guess_type(file_path)
- if mime_type:
- self.send_header("Content-type", mime_type)
-
- if cacheable_seconds:
- expires_time = (datetime.datetime.now() +
- datetime.timedelta(0, cacheable_seconds))
- expires_formatted = format_date_time(
- time.mktime(expires_time.timetuple()))
- self.send_header("Expires", expires_formatted)
- self.end_headers()
-
- shutil.copyfileobj(static_file, self.wfile)
-
-
-def _get_test_baselines(test_file, layout_tests_directory, platforms, filesystem):
- class AllPlatformsPort(WebKitPort):
- def __init__(self):
- WebKitPort.__init__(self, filesystem=filesystem)
- self._platforms_by_directory = dict(
- [(self._webkit_baseline_path(p), p) for p in platforms])
-
- def baseline_search_path(self):
- return self._platforms_by_directory.keys()
-
- def platform_from_directory(self, directory):
- return self._platforms_by_directory[directory]
-
- all_platforms_port = AllPlatformsPort()
-
- test_baselines = {}
- for baseline_extension in ('.txt', '.checksum', '.png'):
- test_path = filesystem.join(layout_tests_directory, test_file)
- baselines = all_platforms_port.expected_baselines(
- test_path, baseline_extension, all_baselines=True)
- for platform_directory, expected_filename in baselines:
- if not platform_directory:
- continue
- if platform_directory == layout_tests_directory:
- platform = 'base'
- else:
- platform = all_platforms_port.platform_from_directory(
- platform_directory)
- if platform not in test_baselines:
- test_baselines[platform] = []
- test_baselines[platform].append(baseline_extension)
-
- for platform, extensions in test_baselines.items():
- test_baselines[platform] = tuple(extensions)
-
- return test_baselines
-
-class RebaselineServer(AbstractDeclarativeCommand):
- name = "rebaseline-server"
- help_text = __doc__
- argument_names = "/path/to/results/directory"
-
- def __init__(self):
- options = [
- make_option("--httpd-port", action="store", type="int", default=8127, help="Port to use for the the rebaseline HTTP server"),
- ]
- AbstractDeclarativeCommand.__init__(self, options=options)
-
- def execute(self, options, args, tool):
- results_directory = args[0]
- filesystem = system.filesystem.FileSystem()
-
- print 'Parsing unexpected_results.json...'
- results_json_path = filesystem.join(
- results_directory, 'unexpected_results.json')
- with codecs.open(results_json_path, "r") as results_json_file:
- results_json_file = file(results_json_path)
- results_json = simplejson.load(results_json_file)
-
- port = factory.get()
- layout_tests_directory = port.layout_tests_dir()
- platforms = filesystem.listdir(
- filesystem.join(layout_tests_directory, 'platform'))
-
- print 'Gathering current baselines...'
- for test_file, test_json in results_json['tests'].items():
- test_json['state'] = STATE_NEEDS_REBASELINE
- test_json['baselines'] = _get_test_baselines(
- test_file, layout_tests_directory, platforms, filesystem)
-
- print "Starting server at http://localhost:%d/" % options.httpd_port
- print ("Use the 'Exit' link in the UI, http://localhost:%d/"
- "quitquitquit or Ctrl-C to stop") % options.httpd_port
-
- httpd = RebaselineHTTPServer(
- httpd_port=options.httpd_port,
- results_directory=results_directory,
- results_json=results_json,
- platforms_json={
- 'platforms': platforms,
- 'defaultPlatform': port.name(),
- })
- httpd.serve_forever()
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver_unittest.py
deleted file mode 100644
index ea52902..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/rebaselineserver_unittest.py
+++ /dev/null
@@ -1,88 +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.
-
-import unittest
-
-from webkitpy.common.system import filesystem_mock
-from webkitpy.layout_tests.port import base
-from webkitpy.tool.commands import rebaselineserver
-
-
-class GetBaselinesTest(unittest.TestCase):
- def test_no_baselines(self):
- self._assertBaselines(
- test_files=(),
- test='fast/missing.html',
- expected_baselines={})
-
- def test_text_baselines(self):
- self._assertBaselines(
- test_files=(
- 'fast/text-expected.txt',
- 'platform/mac/fast/text-expected.txt',
- ),
- test='fast/text.html',
- expected_baselines={'mac': ('.txt',), 'base': ('.txt',)})
-
- def test_image_and_text_baselines(self):
- self._assertBaselines(
- test_files=(
- 'fast/image-expected.txt',
- 'platform/mac/fast/image-expected.png',
- 'platform/mac/fast/image-expected.checksum',
- 'platform/win/fast/image-expected.png',
- 'platform/win/fast/image-expected.checksum',
- ),
- test='fast/image.html',
- expected_baselines={
- 'base': ('.txt',),
- 'mac': ('.checksum', '.png'),
- 'win': ('.checksum', '.png'),
- })
-
- def test_extra_baselines(self):
- self._assertBaselines(
- test_files=(
- 'fast/text-expected.txt',
- 'platform/nosuchplatform/fast/text-expected.txt',
- ),
- test='fast/text.html',
- expected_baselines={'base': ('.txt',)})
-
- def _assertBaselines(self, test_files, test, expected_baselines):
- layout_tests_directory = base.Port().layout_tests_dir()
- mock_filesystem = filesystem_mock.MockFileSystem()
- for file in test_files + (test,):
- file_path = mock_filesystem.join(layout_tests_directory, file)
- mock_filesystem.files[file_path] = ''
- actual_baselines = rebaselineserver._get_test_baselines(
- test,
- layout_tests_directory,
- ('mac', 'win', 'linux'),
- mock_filesystem)
- self.assertEqual(actual_baselines, expected_baselines)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py
deleted file mode 100644
index 145f485..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# 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
-# 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 os
-
-from webkitpy.common.system.deprecated_logging import log
-from webkitpy.common.config.ports import WebKitPort
-from webkitpy.tool.bot.sheriff import Sheriff
-from webkitpy.tool.bot.sheriffircbot import SheriffIRCBot
-from webkitpy.tool.commands.queues import AbstractQueue
-from webkitpy.tool.commands.stepsequence import StepSequenceErrorHandler
-
-
-class SheriffBot(AbstractQueue, StepSequenceErrorHandler):
- name = "sheriff-bot"
- watchers = AbstractQueue.watchers + [
- "abarth@webkit.org",
- "eric@webkit.org",
- ]
-
- def _update(self):
- self.run_webkit_patch(["update", "--force-clean", "--quiet"])
-
- # AbstractQueue methods
-
- def begin_work_queue(self):
- AbstractQueue.begin_work_queue(self)
- self._sheriff = Sheriff(self._tool, self)
- self._irc_bot = SheriffIRCBot(self._tool, self._sheriff)
- self._tool.ensure_irc_connected(self._irc_bot.irc_delegate())
-
- def work_item_log_path(self, failure_map):
- return None
-
- def _is_old_failure(self, revision):
- return self._tool.status_server.svn_revision(revision)
-
- def next_work_item(self):
- self._irc_bot.process_pending_messages()
- self._update()
-
- # FIXME: We need to figure out how to provoke_flaky_builders.
-
- failure_map = self._tool.buildbot.failure_map()
- failure_map.filter_out_old_failures(self._is_old_failure)
- if failure_map.is_empty():
- return None
- return failure_map
-
- def should_proceed_with_work_item(self, failure_map):
- # Currently, we don't have any reasons not to proceed with work items.
- return True
-
- def process_work_item(self, failure_map):
- failing_revisions = failure_map.failing_revisions()
- for revision in failing_revisions:
- builders = failure_map.builders_failing_for(revision)
- tests = failure_map.tests_failing_for(revision)
- try:
- commit_info = self._tool.checkout().commit_info_for_revision(revision)
- if not commit_info:
- print "FAILED to fetch CommitInfo for r%s, likely missing ChangeLog" % revision
- continue
- self._sheriff.post_irc_warning(commit_info, builders)
- self._sheriff.post_blame_comment_on_bug(commit_info, builders, tests)
-
- finally:
- for builder in builders:
- self._tool.status_server.update_svn_revision(revision, builder.name())
- return True
-
- def handle_unexpected_error(self, failure_map, message):
- log(message)
-
- # StepSequenceErrorHandler methods
-
- @classmethod
- def handle_script_error(cls, tool, state, script_error):
- # Ideally we would post some information to IRC about what went wrong
- # here, but we don't have the IRC password in the child process.
- pass
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py
deleted file mode 100644
index 4db463e..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py
+++ /dev/null
@@ -1,57 +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.
-
-import os
-
-from webkitpy.tool.commands.queuestest import QueuesTest
-from webkitpy.tool.commands.sheriffbot import SheriffBot
-from webkitpy.tool.mocktool import *
-
-
-class SheriffBotTest(QueuesTest):
- builder1 = MockBuilder("Builder1")
- builder2 = MockBuilder("Builder2")
-
- def test_sheriff_bot(self):
- tool = MockTool()
- mock_work_item = MockFailureMap(tool.buildbot)
- expected_stderr = {
- "begin_work_queue": self._default_begin_work_queue_stderr("sheriff-bot", tool.scm().checkout_root),
- "next_work_item": "",
- "process_work_item": """MOCK: irc.post: abarth, darin, eseidel: http://trac.webkit.org/changeset/29837 might have broken Builder1
-MOCK bug comment: bug_id=42, cc=['abarth@webkit.org', 'eric@webkit.org']
---- Begin comment ---
-http://trac.webkit.org/changeset/29837 might have broken Builder1
-The following tests are not passing:
-mock-test-1
---- End comment ---
-
-""",
- "handle_unexpected_error": "Mock error message\n"
- }
- self.assert_queue_outputs(SheriffBot(), work_item=mock_work_item, expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/stepsequence.py b/WebKitTools/Scripts/webkitpy/tool/commands/stepsequence.py
deleted file mode 100644
index be2ed4c..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/stepsequence.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# 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
-# 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 webkitpy.tool.steps as steps
-
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.common.checkout.scm import CheckoutNeedsUpdate
-from webkitpy.tool.bot.queueengine import QueueEngine
-from webkitpy.common.system.deprecated_logging import log
-
-
-class StepSequenceErrorHandler():
- @classmethod
- def handle_script_error(cls, tool, patch, script_error):
- raise NotImplementedError, "subclasses must implement"
-
- @classmethod
- def handle_checkout_needs_update(cls, tool, state, options, error):
- raise NotImplementedError, "subclasses must implement"
-
-
-class StepSequence(object):
- def __init__(self, steps):
- self._steps = steps or []
-
- def options(self):
- collected_options = [
- steps.Options.parent_command,
- steps.Options.quiet,
- ]
- for step in self._steps:
- collected_options = collected_options + step.options()
- # Remove duplicates.
- collected_options = sorted(set(collected_options))
- return collected_options
-
- def _run(self, tool, options, state):
- for step in self._steps:
- step(tool, options).run(state)
-
- def run_and_handle_errors(self, tool, options, state=None):
- if not state:
- state = {}
- try:
- self._run(tool, options, state)
- except CheckoutNeedsUpdate, e:
- log("Commit failed because the checkout is out of date. Please update and try again.")
- if options.parent_command:
- command = tool.command_by_name(options.parent_command)
- command.handle_checkout_needs_update(tool, state, options, e)
- QueueEngine.exit_after_handled_error(e)
- except ScriptError, e:
- if not options.quiet:
- log(e.message_with_output())
- if options.parent_command:
- command = tool.command_by_name(options.parent_command)
- command.handle_script_error(tool, state, e)
- QueueEngine.exit_after_handled_error(e)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/upload.py b/WebKitTools/Scripts/webkitpy/tool/commands/upload.py
deleted file mode 100644
index e12c8e2..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/upload.py
+++ /dev/null
@@ -1,483 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2009, 2010 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple 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 os
-import re
-import sys
-
-from optparse import make_option
-
-import webkitpy.tool.steps as steps
-
-from webkitpy.common.config.committers import CommitterList
-from webkitpy.common.net.bugzilla import parse_bug_id
-from webkitpy.common.system.user import User
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.tool.commands.abstractsequencedcommand import AbstractSequencedCommand
-from webkitpy.tool.grammar import pluralize, join_with_separators
-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.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(options.git_commit).message()
-
-
-class CleanPendingCommit(AbstractDeclarativeCommand):
- name = "clean-pending-commit"
- help_text = "Clear r+ on obsolete patches so they do not appear in the pending-commit list."
-
- # NOTE: This was designed to be generic, but right now we're only processing patches from the pending-commit list, so only r+ matters.
- def _flags_to_clear_on_patch(self, patch):
- if not patch.is_obsolete():
- return None
- what_was_cleared = []
- if patch.review() == "+":
- if patch.reviewer():
- what_was_cleared.append("%s's review+" % patch.reviewer().full_name)
- else:
- what_was_cleared.append("review+")
- return join_with_separators(what_was_cleared)
-
- def execute(self, options, args, tool):
- committers = CommitterList()
- for bug_id in tool.bugs.queries.fetch_bug_ids_from_pending_commit_list():
- bug = self._tool.bugs.fetch_bug(bug_id)
- patches = bug.patches(include_obsolete=True)
- for patch in patches:
- flags_to_clear = self._flags_to_clear_on_patch(patch)
- if not flags_to_clear:
- continue
- message = "Cleared %s from obsolete attachment %s so that this bug does not appear in http://webkit.org/pending-commit." % (flags_to_clear, patch.id())
- self._tool.bugs.obsolete_attachment(patch.id(), message)
-
-
-# FIXME: This should be share more logic with AssignToCommitter and CleanPendingCommit
-class CleanReviewQueue(AbstractDeclarativeCommand):
- name = "clean-review-queue"
- help_text = "Clear r? on obsolete patches so they do not appear in the pending-commit list."
-
- def execute(self, options, args, tool):
- queue_url = "http://webkit.org/pending-review"
- # We do this inefficient dance to be more like webkit.org/pending-review
- # bugs.queries.fetch_bug_ids_from_review_queue() doesn't return
- # closed bugs, but folks using /pending-review will see them. :(
- for patch_id in tool.bugs.queries.fetch_attachment_ids_from_review_queue():
- patch = self._tool.bugs.fetch_attachment(patch_id)
- if not patch.review() == "?":
- continue
- attachment_obsolete_modifier = ""
- if patch.is_obsolete():
- attachment_obsolete_modifier = "obsolete "
- elif patch.bug().is_closed():
- bug_closed_explanation = " If you would like this patch reviewed, please attach it to a new bug (or re-open this bug before marking it for review again)."
- else:
- # Neither the patch was obsolete or the bug was closed, next patch...
- continue
- message = "Cleared review? from %sattachment %s so that this bug does not appear in %s.%s" % (attachment_obsolete_modifier, patch.id(), queue_url, bug_closed_explanation)
- self._tool.bugs.obsolete_attachment(patch.id(), message)
-
-
-class AssignToCommitter(AbstractDeclarativeCommand):
- name = "assign-to-committer"
- help_text = "Assign bug to whoever attached the most recent r+'d patch"
-
- def _patches_have_commiters(self, reviewed_patches):
- for patch in reviewed_patches:
- if not patch.committer():
- return False
- return True
-
- def _assign_bug_to_last_patch_attacher(self, bug_id):
- committers = CommitterList()
- bug = self._tool.bugs.fetch_bug(bug_id)
- if not bug.is_unassigned():
- assigned_to_email = bug.assigned_to_email()
- log("Bug %s is already assigned to %s (%s)." % (bug_id, assigned_to_email, committers.committer_by_email(assigned_to_email)))
- return
-
- reviewed_patches = bug.reviewed_patches()
- if not reviewed_patches:
- log("Bug %s has no non-obsolete patches, ignoring." % bug_id)
- return
-
- # We only need to do anything with this bug if one of the r+'d patches does not have a valid committer (cq+ set).
- if self._patches_have_commiters(reviewed_patches):
- log("All reviewed patches on bug %s already have commit-queue+, ignoring." % bug_id)
- return
-
- latest_patch = reviewed_patches[-1]
- attacher_email = latest_patch.attacher_email()
- committer = committers.committer_by_email(attacher_email)
- if not committer:
- log("Attacher %s is not a committer. Bug %s likely needs commit-queue+." % (attacher_email, bug_id))
- return
-
- reassign_message = "Attachment %s was posted by a committer and has review+, assigning to %s for commit." % (latest_patch.id(), committer.full_name)
- self._tool.bugs.reassign_bug(bug_id, committer.bugzilla_email(), reassign_message)
-
- def execute(self, options, args, tool):
- for bug_id in tool.bugs.queries.fetch_bug_ids_from_pending_commit_list():
- self._assign_bug_to_last_patch_attacher(bug_id)
-
-
-class ObsoleteAttachments(AbstractSequencedCommand):
- name = "obsolete-attachments"
- help_text = "Mark all attachments on a bug as obsolete"
- argument_names = "BUGID"
- steps = [
- steps.ObsoletePatches,
- ]
-
- def _prepare_state(self, options, args, tool):
- return { "bug_id" : args[0] }
-
-
-class AbstractPatchUploadingCommand(AbstractSequencedCommand):
- def _bug_id(self, options, args, tool, state):
- # Perfer a bug id passed as an argument over a bug url in the diff (i.e. ChangeLogs).
- bug_id = args and args[0]
- if not bug_id:
- changed_files = self._tool.scm().changed_files(options.git_commit)
- state["changed_files"] = changed_files
- bug_id = tool.checkout().bug_id_for_this_commit(options.git_commit, changed_files)
- return bug_id
-
- def _prepare_state(self, options, args, tool):
- state = {}
- state["bug_id"] = self._bug_id(options, args, tool, state)
- if not state["bug_id"]:
- error("No bug id passed and no bug url found in ChangeLogs.")
- return state
-
-
-class Post(AbstractPatchUploadingCommand):
- name = "post"
- help_text = "Attach the current working directory diff to a bug as a patch file"
- argument_names = "[BUGID]"
- steps = [
- steps.CheckStyle,
- steps.ConfirmDiff,
- steps.ObsoletePatches,
- steps.SuggestReviewers,
- steps.PostDiff,
- ]
-
-
-class LandSafely(AbstractPatchUploadingCommand):
- name = "land-safely"
- help_text = "Land the current diff via the commit-queue"
- argument_names = "[BUGID]"
- long_help = """land-safely updates the ChangeLog with the reviewer listed
- in bugs.webkit.org for BUGID (or the bug ID detected from the ChangeLog).
- The command then uploads the current diff to the bug and marks it for
- commit by the commit-queue."""
- show_in_main_help = True
- steps = [
- steps.UpdateChangeLogsWithReviewer,
- steps.ObsoletePatches,
- steps.PostDiffForCommit,
- ]
-
-
-class Prepare(AbstractSequencedCommand):
- name = "prepare"
- help_text = "Creates a bug (or prompts for an existing bug) and prepares the ChangeLogs"
- argument_names = "[BUGID]"
- steps = [
- steps.PromptForBugOrTitle,
- steps.CreateBug,
- steps.PrepareChangeLog,
- ]
-
- def _prepare_state(self, options, args, tool):
- bug_id = args and args[0]
- return { "bug_id" : bug_id }
-
-
-class Upload(AbstractPatchUploadingCommand):
- name = "upload"
- help_text = "Automates the process of uploading a patch for review"
- argument_names = "[BUGID]"
- show_in_main_help = True
- steps = [
- steps.CheckStyle,
- steps.PromptForBugOrTitle,
- steps.CreateBug,
- steps.PrepareChangeLog,
- steps.EditChangeLog,
- steps.ConfirmDiff,
- steps.ObsoletePatches,
- steps.SuggestReviewers,
- steps.PostDiff,
- ]
- long_help = """upload uploads the current diff to bugs.webkit.org.
- If no bug id is provided, upload will create a bug.
- If the current diff does not have a ChangeLog, upload
- will prepare a ChangeLog. Once a patch is read, upload
- will open the ChangeLogs for editing using the command in the
- EDITOR environment variable and will display the diff using the
- command in the PAGER environment variable."""
-
- def _prepare_state(self, options, args, tool):
- state = {}
- state["bug_id"] = self._bug_id(options, args, tool, state)
- return state
-
-
-class EditChangeLogs(AbstractSequencedCommand):
- name = "edit-changelogs"
- help_text = "Opens modified ChangeLogs in $EDITOR"
- show_in_main_help = True
- steps = [
- steps.EditChangeLog,
- ]
-
-
-class PostCommits(AbstractDeclarativeCommand):
- name = "post-commits"
- help_text = "Attach a range of local commits to bugs as patch files"
- argument_names = "COMMITISH"
-
- def __init__(self):
- options = [
- make_option("-b", "--bug-id", action="store", type="string", dest="bug_id", help="Specify bug id if no URL is provided in the commit log."),
- make_option("--add-log-as-comment", action="store_true", dest="add_log_as_comment", default=False, help="Add commit log message as a comment when uploading the patch."),
- make_option("-m", "--description", action="store", type="string", dest="description", help="Description string for the attachment (default: description from commit message)"),
- steps.Options.obsolete_patches,
- steps.Options.review,
- steps.Options.request_commit,
- ]
- AbstractDeclarativeCommand.__init__(self, options=options, requires_local_commits=True)
-
- def _comment_text_for_commit(self, options, commit_message, tool, commit_id):
- comment_text = None
- if (options.add_log_as_comment):
- comment_text = commit_message.body(lstrip=True)
- comment_text += "---\n"
- comment_text += tool.scm().files_changed_summary_for_commit(commit_id)
- return comment_text
-
- def execute(self, options, args, tool):
- commit_ids = tool.scm().commit_ids_from_commitish_arguments(args)
- if len(commit_ids) > 10: # We could lower this limit, 10 is too many for one bug as-is.
- error("webkit-patch does not support attaching %s at once. Are you sure you passed the right commit range?" % (pluralize("patch", len(commit_ids))))
-
- have_obsoleted_patches = set()
- for commit_id in commit_ids:
- commit_message = tool.scm().commit_message_for_local_commit(commit_id)
-
- # Prefer --bug-id=, then a bug url in the commit message, then a bug url in the entire commit diff (i.e. ChangeLogs).
- bug_id = options.bug_id or parse_bug_id(commit_message.message()) or parse_bug_id(tool.scm().create_patch(git_commit=commit_id))
- if not bug_id:
- log("Skipping %s: No bug id found in commit or specified with --bug-id." % commit_id)
- continue
-
- if options.obsolete_patches and bug_id not in have_obsoleted_patches:
- state = { "bug_id": bug_id }
- steps.ObsoletePatches(tool, options).run(state)
- have_obsoleted_patches.add(bug_id)
-
- diff = tool.scm().create_patch(git_commit=commit_id)
- description = options.description or commit_message.description(lstrip=True, strip_url=True)
- comment_text = self._comment_text_for_commit(options, commit_message, tool, commit_id)
- tool.bugs.add_patch_to_bug(bug_id, diff, description, comment_text, mark_for_review=options.review, mark_for_commit_queue=options.request_commit)
-
-
-# FIXME: This command needs to be brought into the modern age with steps and CommitInfo.
-class MarkBugFixed(AbstractDeclarativeCommand):
- name = "mark-bug-fixed"
- help_text = "Mark the specified bug as fixed"
- argument_names = "[SVN_REVISION]"
- def __init__(self):
- options = [
- make_option("--bug-id", action="store", type="string", dest="bug_id", help="Specify bug id if no URL is provided in the commit log."),
- make_option("--comment", action="store", type="string", dest="comment", help="Text to include in bug comment."),
- make_option("--open", action="store_true", default=False, dest="open_bug", help="Open bug in default web browser (Mac only)."),
- make_option("--update-only", action="store_true", default=False, dest="update_only", help="Add comment to the bug, but do not close it."),
- ]
- AbstractDeclarativeCommand.__init__(self, options=options)
-
- # FIXME: We should be using checkout().changelog_entries_for_revision(...) instead here.
- def _fetch_commit_log(self, tool, svn_revision):
- if not svn_revision:
- return tool.scm().last_svn_commit_log()
- return tool.scm().svn_commit_log(svn_revision)
-
- def _determine_bug_id_and_svn_revision(self, tool, bug_id, svn_revision):
- commit_log = self._fetch_commit_log(tool, svn_revision)
-
- if not bug_id:
- bug_id = parse_bug_id(commit_log)
-
- if not svn_revision:
- match = re.search("^r(?P<svn_revision>\d+) \|", commit_log, re.MULTILINE)
- if match:
- svn_revision = match.group('svn_revision')
-
- if not bug_id or not svn_revision:
- not_found = []
- if not bug_id:
- not_found.append("bug id")
- if not svn_revision:
- not_found.append("svn revision")
- error("Could not find %s on command-line or in %s."
- % (" or ".join(not_found), "r%s" % svn_revision if svn_revision else "last commit"))
-
- return (bug_id, svn_revision)
-
- def execute(self, options, args, tool):
- bug_id = options.bug_id
-
- svn_revision = args and args[0]
- if svn_revision:
- if re.match("^r[0-9]+$", svn_revision, re.IGNORECASE):
- svn_revision = svn_revision[1:]
- if not re.match("^[0-9]+$", svn_revision):
- error("Invalid svn revision: '%s'" % svn_revision)
-
- needs_prompt = False
- if not bug_id or not svn_revision:
- needs_prompt = True
- (bug_id, svn_revision) = self._determine_bug_id_and_svn_revision(tool, bug_id, svn_revision)
-
- log("Bug: <%s> %s" % (tool.bugs.bug_url_for_bug_id(bug_id), tool.bugs.fetch_bug_dictionary(bug_id)["title"]))
- log("Revision: %s" % svn_revision)
-
- if options.open_bug:
- tool.user.open_url(tool.bugs.bug_url_for_bug_id(bug_id))
-
- if needs_prompt:
- if not tool.user.confirm("Is this correct?"):
- exit(1)
-
- bug_comment = bug_comment_from_svn_revision(svn_revision)
- if options.comment:
- bug_comment = "%s\n\n%s" % (options.comment, bug_comment)
-
- if options.update_only:
- log("Adding comment to Bug %s." % bug_id)
- tool.bugs.post_comment_to_bug(bug_id, bug_comment)
- else:
- log("Adding comment to Bug %s and marking as Resolved/Fixed." % bug_id)
- tool.bugs.close_bug_as_fixed(bug_id, bug_comment)
-
-
-# FIXME: Requires unit test. Blocking issue: too complex for now.
-class CreateBug(AbstractDeclarativeCommand):
- name = "create-bug"
- help_text = "Create a bug from local changes or local commits"
- argument_names = "[COMMITISH]"
-
- def __init__(self):
- options = [
- steps.Options.cc,
- steps.Options.component,
- make_option("--no-prompt", action="store_false", dest="prompt", default=True, help="Do not prompt for bug title and comment; use commit log instead."),
- make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review."),
- make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review."),
- ]
- AbstractDeclarativeCommand.__init__(self, options=options)
-
- def create_bug_from_commit(self, options, args, tool):
- commit_ids = tool.scm().commit_ids_from_commitish_arguments(args)
- if len(commit_ids) > 3:
- error("Are you sure you want to create one bug with %s patches?" % len(commit_ids))
-
- commit_id = commit_ids[0]
-
- bug_title = ""
- comment_text = ""
- if options.prompt:
- (bug_title, comment_text) = self.prompt_for_bug_title_and_comment()
- else:
- commit_message = tool.scm().commit_message_for_local_commit(commit_id)
- bug_title = commit_message.description(lstrip=True, strip_url=True)
- comment_text = commit_message.body(lstrip=True)
- comment_text += "---\n"
- comment_text += tool.scm().files_changed_summary_for_commit(commit_id)
-
- diff = tool.scm().create_patch(git_commit=commit_id)
- bug_id = tool.bugs.create_bug(bug_title, comment_text, options.component, diff, "Patch", cc=options.cc, mark_for_review=options.review, mark_for_commit_queue=options.request_commit)
-
- if bug_id and len(commit_ids) > 1:
- options.bug_id = bug_id
- options.obsolete_patches = False
- # FIXME: We should pass through --no-comment switch as well.
- PostCommits.execute(self, options, commit_ids[1:], tool)
-
- def create_bug_from_patch(self, options, args, tool):
- bug_title = ""
- comment_text = ""
- if options.prompt:
- (bug_title, comment_text) = self.prompt_for_bug_title_and_comment()
- else:
- commit_message = tool.checkout().commit_message_for_this_commit(options.git_commit)
- bug_title = commit_message.description(lstrip=True, strip_url=True)
- comment_text = commit_message.body(lstrip=True)
-
- diff = tool.scm().create_patch(options.git_commit)
- bug_id = tool.bugs.create_bug(bug_title, comment_text, options.component, diff, "Patch", cc=options.cc, mark_for_review=options.review, mark_for_commit_queue=options.request_commit)
-
- def prompt_for_bug_title_and_comment(self):
- bug_title = User.prompt("Bug title: ")
- print "Bug comment (hit ^D on blank line to end):"
- lines = sys.stdin.readlines()
- try:
- sys.stdin.seek(0, os.SEEK_END)
- except IOError:
- # Cygwin raises an Illegal Seek (errno 29) exception when the above
- # seek() call is made. Ignoring it seems to cause no harm.
- # FIXME: Figure out a way to get avoid the exception in the first
- # place.
- pass
- comment_text = "".join(lines)
- return (bug_title, comment_text)
-
- def execute(self, options, args, tool):
- if len(args):
- if (not tool.scm().supports_local_commits()):
- error("Extra arguments not supported; patch is taken from working directory.")
- self.create_bug_from_commit(options, args, tool)
- else:
- self.create_bug_from_patch(options, args, tool)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py
deleted file mode 100644
index bd1fbd6..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# 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
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.tool.commands.commandtest import CommandsTest
-from webkitpy.tool.commands.upload import *
-from webkitpy.tool.mocktool import MockOptions, MockTool
-
-class UploadCommandsTest(CommandsTest):
- def test_commit_message_for_current_diff(self):
- tool = MockTool()
- 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):
- self.assert_execute_outputs(CleanPendingCommit(), [])
-
- def test_assign_to_committer(self):
- tool = MockTool()
- expected_stderr = "Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)\nBug 77 is already assigned to foo@foo.com (None).\nBug 76 has no non-obsolete patches, ignoring.\n"
- self.assert_execute_outputs(AssignToCommitter(), [], expected_stderr=expected_stderr, tool=tool)
- tool.bugs.reassign_bug.assert_called_with(42, "eric@webkit.org", "Attachment 128 was posted by a committer and has review+, assigning to Eric Seidel for commit.")
-
- def test_obsolete_attachments(self):
- expected_stderr = "Obsoleting 2 old patches on bug 42\n"
- self.assert_execute_outputs(ObsoleteAttachments(), [42], expected_stderr=expected_stderr)
-
- def test_post(self):
- options = MockOptions()
- options.cc = None
- options.check_style = True
- options.comment = None
- options.description = "MOCK description"
- options.request_commit = False
- options.review = True
- options.suggest_reviewers = False
- expected_stderr = """Running check-webkit-style
-MOCK: user.open_url: file://...
-Obsoleting 2 old patches on bug 42
-MOCK add_patch_to_bug: bug_id=42, description=MOCK description, mark_for_review=True, mark_for_commit_queue=False, mark_for_landing=False
--- Begin comment --
-None
--- End comment --
-MOCK: user.open_url: http://example.com/42
-"""
- expected_stdout = "Was that diff correct?\n"
- self.assert_execute_outputs(Post(), [42], options=options, expected_stdout=expected_stdout, expected_stderr=expected_stderr)
-
- def test_land_safely(self):
- expected_stderr = "Obsoleting 2 old patches on bug 42\nMOCK add_patch_to_bug: bug_id=42, description=Patch for landing, mark_for_review=False, mark_for_commit_queue=False, mark_for_landing=True\n-- Begin comment --\nNone\n-- End comment --\n"
- self.assert_execute_outputs(LandSafely(), [42], expected_stderr=expected_stderr)
-
- def test_prepare_diff_with_arg(self):
- self.assert_execute_outputs(Prepare(), [42])
-
- def test_prepare(self):
- expected_stderr = "MOCK create_bug\nbug_title: Mock user response\nbug_description: Mock user response\n"
- self.assert_execute_outputs(Prepare(), [], expected_stderr=expected_stderr)
-
- def test_upload(self):
- options = MockOptions()
- options.cc = None
- options.check_style = True
- options.comment = None
- options.description = "MOCK description"
- options.request_commit = False
- options.review = True
- options.suggest_reviewers = False
- expected_stderr = """Running check-webkit-style
-MOCK: user.open_url: file://...
-Obsoleting 2 old patches on bug 42
-MOCK add_patch_to_bug: bug_id=42, description=MOCK description, mark_for_review=True, mark_for_commit_queue=False, mark_for_landing=False
--- Begin comment --
-None
--- End comment --
-MOCK: user.open_url: http://example.com/42
-"""
- expected_stdout = "Was that diff correct?\n"
- self.assert_execute_outputs(Upload(), [42], options=options, expected_stdout=expected_stdout, expected_stderr=expected_stderr)
-
- def test_mark_bug_fixed(self):
- tool = MockTool()
- tool._scm.last_svn_commit_log = lambda: "r9876 |"
- options = Mock()
- options.bug_id = 42
- options.comment = "MOCK comment"
- expected_stderr = "Bug: <http://example.com/42> Bug with two r+'d and cq+'d patches, one of which has an invalid commit-queue setter.\nRevision: 9876\nMOCK: user.open_url: http://example.com/42\nAdding comment to Bug 42.\nMOCK bug comment: bug_id=42, cc=None\n--- Begin comment ---\nMOCK comment\n\nCommitted r9876: <http://trac.webkit.org/changeset/9876>\n--- End comment ---\n\n"
- expected_stdout = "Is this correct?\n"
- self.assert_execute_outputs(MarkBugFixed(), [], expected_stdout=expected_stdout, expected_stderr=expected_stderr, tool=tool, options=options)
-
- def test_edit_changelog(self):
- self.assert_execute_outputs(EditChangeLogs(), [])
diff --git a/WebKitTools/Scripts/webkitpy/tool/comments.py b/WebKitTools/Scripts/webkitpy/tool/comments.py
deleted file mode 100755
index 83f2be8..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/comments.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple 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.
-#
-# A tool for automating dealing with bugzilla, posting patches, committing
-# patches, etc.
-
-from webkitpy.common.checkout.changelog import view_source_url
-
-
-def bug_comment_from_svn_revision(svn_revision):
- return "Committed r%s: <%s>" % (svn_revision,
- view_source_url(svn_revision))
-
-
-def bug_comment_from_commit_text(scm, commit_text):
- svn_revision = scm.svn_revision_from_commit_text(commit_text)
- return bug_comment_from_svn_revision(svn_revision)
diff --git a/WebKitTools/Scripts/webkitpy/tool/grammar.py b/WebKitTools/Scripts/webkitpy/tool/grammar.py
deleted file mode 100644
index 8db9826..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/grammar.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple 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 re
-
-
-def plural(noun):
- # This is a dumb plural() implementation that is just enough for our uses.
- if re.search("h$", noun):
- return noun + "es"
- else:
- return noun + "s"
-
-
-def pluralize(noun, count):
- if count != 1:
- noun = plural(noun)
- return "%d %s" % (count, noun)
-
-
-def join_with_separators(list_of_strings, separator=', ', only_two_separator=" and ", last_separator=', and '):
- if not list_of_strings:
- return ""
- if len(list_of_strings) == 1:
- return list_of_strings[0]
- if len(list_of_strings) == 2:
- return only_two_separator.join(list_of_strings)
- return "%s%s%s" % (separator.join(list_of_strings[:-1]), last_separator, list_of_strings[-1])
diff --git a/WebKitTools/Scripts/webkitpy/tool/grammar_unittest.py b/WebKitTools/Scripts/webkitpy/tool/grammar_unittest.py
deleted file mode 100644
index cab71db..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/grammar_unittest.py
+++ /dev/null
@@ -1,41 +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.
-
-import unittest
-
-from webkitpy.tool.grammar import join_with_separators
-
-class GrammarTest(unittest.TestCase):
-
- def test_join_with_separators(self):
- self.assertEqual(join_with_separators(["one"]), "one")
- self.assertEqual(join_with_separators(["one", "two"]), "one and two")
- self.assertEqual(join_with_separators(["one", "two", "three"]), "one, two, and three")
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/tool/main.py b/WebKitTools/Scripts/webkitpy/tool/main.py
deleted file mode 100755
index 7b1d7f3..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/main.py
+++ /dev/null
@@ -1,133 +0,0 @@
-# Copyright (c) 2010 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple 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.
-#
-# A tool for automating dealing with bugzilla, posting patches, committing patches, etc.
-
-from optparse import make_option
-import os
-import threading
-
-from webkitpy.common.checkout.api import Checkout
-from webkitpy.common.checkout.scm import default_scm
-from webkitpy.common.config.ports import WebKitPort
-from webkitpy.common.net.bugzilla import Bugzilla
-from webkitpy.common.net.buildbot import BuildBot
-from webkitpy.common.net.irc.ircproxy import IRCProxy
-from webkitpy.common.net.statusserver import StatusServer
-from webkitpy.common.system.executive import Executive
-from webkitpy.common.system.user import User
-from webkitpy.layout_tests import port
-from webkitpy.tool.multicommandtool import MultiCommandTool
-import webkitpy.tool.commands as commands
-
-
-class WebKitPatch(MultiCommandTool):
- global_options = [
- make_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="enable all logging"),
- make_option("--dry-run", action="store_true", dest="dry_run", default=False, help="do not touch remote servers"),
- make_option("--status-host", action="store", dest="status_host", type="string", help="Hostname (e.g. localhost or commit.webkit.org) where status updates should be posted."),
- make_option("--bot-id", action="store", dest="bot_id", type="string", help="Identifier for this bot (if multiple bots are running for a queue)"),
- make_option("--irc-password", action="store", dest="irc_password", type="string", help="Password to use when communicating via IRC."),
- make_option("--port", action="store", dest="port", default=None, help="Specify a port (e.g., mac, qt, gtk, ...)."),
- ]
-
- def __init__(self, path):
- MultiCommandTool.__init__(self)
-
- self._path = path
- self.wakeup_event = threading.Event()
- self.bugs = Bugzilla()
- self.buildbot = BuildBot()
- self.executive = Executive()
- self._irc = None
- self._port = None
- self.user = User()
- self._scm = None
- self._checkout = None
- self.status_server = StatusServer()
- self.port_factory = port.factory
-
- def scm(self):
- # Lazily initialize SCM to not error-out before command line parsing (or when running non-scm commands).
- if not self._scm:
- self._scm = default_scm()
- return self._scm
-
- def checkout(self):
- if not self._checkout:
- self._checkout = Checkout(self.scm())
- return self._checkout
-
- def port(self):
- return self._port
-
- def ensure_irc_connected(self, irc_delegate):
- if not self._irc:
- self._irc = IRCProxy(irc_delegate)
-
- def irc(self):
- # We don't automatically construct IRCProxy here because constructing
- # IRCProxy actually connects to IRC. We want clients to explicitly
- # connect to IRC.
- return self._irc
-
- def path(self):
- return self._path
-
- def command_completed(self):
- if self._irc:
- self._irc.disconnect()
-
- def should_show_in_main_help(self, command):
- if not command.show_in_main_help:
- return False
- if command.requires_local_commits:
- return self.scm().supports_local_commits()
- return True
-
- # FIXME: This may be unnecessary since we pass global options to all commands during execute() as well.
- def handle_global_options(self, options):
- self._options = options
- if options.dry_run:
- self.scm().dryrun = True
- self.bugs.dryrun = True
- if options.status_host:
- self.status_server.set_host(options.status_host)
- if options.bot_id:
- self.status_server.set_bot_id(options.bot_id)
- if options.irc_password:
- self.irc_password = options.irc_password
- # If options.port is None, we'll get the default port for this platform.
- self._port = WebKitPort.port(options.port)
-
- def should_execute_command(self, command):
- if command.requires_local_commits and not self.scm().supports_local_commits():
- failure_reason = "%s requires local commits using %s in %s." % (command.name, self.scm().display_name(), self.scm().checkout_root)
- return (False, failure_reason)
- return (True, None)
diff --git a/WebKitTools/Scripts/webkitpy/tool/mocktool.py b/WebKitTools/Scripts/webkitpy/tool/mocktool.py
deleted file mode 100644
index 719f9b1..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/mocktool.py
+++ /dev/null
@@ -1,676 +0,0 @@
-# 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
-# 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 os
-import threading
-
-from webkitpy.common.config.committers import CommitterList, Reviewer
-from webkitpy.common.checkout.commitinfo import CommitInfo
-from webkitpy.common.checkout.scm import CommitMessage
-from webkitpy.common.net.bugzilla import Bug, Attachment
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.common.system.deprecated_logging import log
-
-
-def _id_to_object_dictionary(*objects):
- dictionary = {}
- for thing in objects:
- dictionary[thing["id"]] = thing
- return dictionary
-
-# Testing
-
-# FIXME: The ids should be 1, 2, 3 instead of crazy numbers.
-
-
-_patch1 = {
- "id": 197,
- "bug_id": 42,
- "url": "http://example.com/197",
- "name": "Patch1",
- "is_obsolete": False,
- "is_patch": True,
- "review": "+",
- "reviewer_email": "foo@bar.com",
- "commit-queue": "+",
- "committer_email": "foo@bar.com",
- "attacher_email": "Contributer1",
-}
-
-
-_patch2 = {
- "id": 128,
- "bug_id": 42,
- "url": "http://example.com/128",
- "name": "Patch2",
- "is_obsolete": False,
- "is_patch": True,
- "review": "+",
- "reviewer_email": "foo@bar.com",
- "commit-queue": "+",
- "committer_email": "non-committer@example.com",
- "attacher_email": "eric@webkit.org",
-}
-
-
-_patch3 = {
- "id": 103,
- "bug_id": 75,
- "url": "http://example.com/103",
- "name": "Patch3",
- "is_obsolete": False,
- "is_patch": True,
- "review": "?",
- "attacher_email": "eric@webkit.org",
-}
-
-
-_patch4 = {
- "id": 104,
- "bug_id": 77,
- "url": "http://example.com/103",
- "name": "Patch3",
- "is_obsolete": False,
- "is_patch": True,
- "review": "+",
- "commit-queue": "?",
- "reviewer_email": "foo@bar.com",
- "attacher_email": "Contributer2",
-}
-
-
-_patch5 = {
- "id": 105,
- "bug_id": 77,
- "url": "http://example.com/103",
- "name": "Patch5",
- "is_obsolete": False,
- "is_patch": True,
- "review": "+",
- "reviewer_email": "foo@bar.com",
- "attacher_email": "eric@webkit.org",
-}
-
-
-_patch6 = { # Valid committer, but no reviewer.
- "id": 106,
- "bug_id": 77,
- "url": "http://example.com/103",
- "name": "ROLLOUT of r3489",
- "is_obsolete": False,
- "is_patch": True,
- "commit-queue": "+",
- "committer_email": "foo@bar.com",
- "attacher_email": "eric@webkit.org",
-}
-
-
-_patch7 = { # Valid review, patch is marked obsolete.
- "id": 107,
- "bug_id": 76,
- "url": "http://example.com/103",
- "name": "Patch7",
- "is_obsolete": True,
- "is_patch": True,
- "review": "+",
- "reviewer_email": "foo@bar.com",
- "attacher_email": "eric@webkit.org",
-}
-
-
-# This matches one of Bug.unassigned_emails
-_unassigned_email = "webkit-unassigned@lists.webkit.org"
-
-
-# FIXME: The ids should be 1, 2, 3 instead of crazy numbers.
-
-
-_bug1 = {
- "id": 42,
- "title": "Bug with two r+'d and cq+'d patches, one of which has an "
- "invalid commit-queue setter.",
- "assigned_to_email": _unassigned_email,
- "attachments": [_patch1, _patch2],
- "bug_status": "UNCONFIRMED",
-}
-
-
-_bug2 = {
- "id": 75,
- "title": "Bug with a patch needing review.",
- "assigned_to_email": "foo@foo.com",
- "attachments": [_patch3],
- "bug_status": "ASSIGNED",
-}
-
-
-_bug3 = {
- "id": 76,
- "title": "The third bug",
- "assigned_to_email": _unassigned_email,
- "attachments": [_patch7],
- "bug_status": "NEW",
-}
-
-
-_bug4 = {
- "id": 77,
- "title": "The fourth bug",
- "assigned_to_email": "foo@foo.com",
- "attachments": [_patch4, _patch5, _patch6],
- "bug_status": "REOPENED",
-}
-
-
-# FIXME: This should not inherit from Mock
-class MockBugzillaQueries(Mock):
-
- def __init__(self, bugzilla):
- Mock.__init__(self)
- self._bugzilla = bugzilla
-
- def _all_bugs(self):
- return map(lambda bug_dictionary: Bug(bug_dictionary, self._bugzilla),
- self._bugzilla.bug_cache.values())
-
- def fetch_bug_ids_from_commit_queue(self):
- bugs_with_commit_queued_patches = filter(
- lambda bug: bug.commit_queued_patches(),
- self._all_bugs())
- return map(lambda bug: bug.id(), bugs_with_commit_queued_patches)
-
- def fetch_attachment_ids_from_review_queue(self):
- unreviewed_patches = sum([bug.unreviewed_patches()
- for bug in self._all_bugs()], [])
- return map(lambda patch: patch.id(), unreviewed_patches)
-
- def fetch_patches_from_commit_queue(self):
- return sum([bug.commit_queued_patches()
- for bug in self._all_bugs()], [])
-
- def fetch_bug_ids_from_pending_commit_list(self):
- bugs_with_reviewed_patches = filter(lambda bug: bug.reviewed_patches(),
- self._all_bugs())
- bug_ids = map(lambda bug: bug.id(), bugs_with_reviewed_patches)
- # NOTE: This manual hack here is to allow testing logging in
- # test_assign_to_committer the real pending-commit query on bugzilla
- # will return bugs with patches which have r+, but are also obsolete.
- return bug_ids + [76]
-
- def fetch_patches_from_pending_commit_list(self):
- return sum([bug.reviewed_patches() for bug in self._all_bugs()], [])
-
-
-_mock_reviewer = Reviewer("Foo Bar", "foo@bar.com")
-
-
-# FIXME: Bugzilla is the wrong Mock-point. Once we have a BugzillaNetwork
-# class we should mock that instead.
-# Most of this class is just copy/paste from Bugzilla.
-# FIXME: This should not inherit from Mock
-class MockBugzilla(Mock):
-
- bug_server_url = "http://example.com"
-
- bug_cache = _id_to_object_dictionary(_bug1, _bug2, _bug3, _bug4)
-
- attachment_cache = _id_to_object_dictionary(_patch1,
- _patch2,
- _patch3,
- _patch4,
- _patch5,
- _patch6,
- _patch7)
-
- def __init__(self):
- Mock.__init__(self)
- self.queries = MockBugzillaQueries(self)
- self.committers = CommitterList(reviewers=[_mock_reviewer])
- self._override_patch = None
-
- def create_bug(self,
- bug_title,
- bug_description,
- component=None,
- diff=None,
- patch_description=None,
- cc=None,
- blocked=None,
- mark_for_review=False,
- mark_for_commit_queue=False):
- log("MOCK create_bug")
- log("bug_title: %s" % bug_title)
- log("bug_description: %s" % bug_description)
-
- def quips(self):
- return ["Good artists copy. Great artists steal. - Pablo Picasso"]
-
- def fetch_bug(self, bug_id):
- return Bug(self.bug_cache.get(bug_id), self)
-
- def set_override_patch(self, patch):
- self._override_patch = patch
-
- def fetch_attachment(self, attachment_id):
- if self._override_patch:
- return self._override_patch
-
- attachment_dictionary = self.attachment_cache.get(attachment_id)
- if not attachment_dictionary:
- print "MOCK: fetch_attachment: %s is not a known attachment id" % attachment_id
- return None
- bug = self.fetch_bug(attachment_dictionary["bug_id"])
- for attachment in bug.attachments(include_obsolete=True):
- if attachment.id() == int(attachment_id):
- return attachment
-
- def bug_url_for_bug_id(self, bug_id):
- return "%s/%s" % (self.bug_server_url, bug_id)
-
- def fetch_bug_dictionary(self, bug_id):
- return self.bug_cache.get(bug_id)
-
- def attachment_url_for_id(self, attachment_id, action="view"):
- action_param = ""
- if action and action != "view":
- action_param = "&action=%s" % action
- return "%s/%s%s" % (self.bug_server_url, attachment_id, action_param)
-
- def set_flag_on_attachment(self,
- attachment_id,
- flag_name,
- flag_value,
- comment_text=None,
- additional_comment_text=None):
- log("MOCK setting flag '%s' to '%s' on attachment '%s' with comment '%s' and additional comment '%s'" % (
- flag_name, flag_value, attachment_id, comment_text, additional_comment_text))
-
- def post_comment_to_bug(self, bug_id, comment_text, cc=None):
- log("MOCK bug comment: bug_id=%s, cc=%s\n--- Begin comment ---\n%s\n--- End comment ---\n" % (
- bug_id, cc, comment_text))
-
- def add_patch_to_bug(self,
- bug_id,
- diff,
- description,
- comment_text=None,
- mark_for_review=False,
- mark_for_commit_queue=False,
- mark_for_landing=False):
- log("MOCK add_patch_to_bug: bug_id=%s, description=%s, mark_for_review=%s, mark_for_commit_queue=%s, mark_for_landing=%s" %
- (bug_id, description, mark_for_review, mark_for_commit_queue, mark_for_landing))
- log("-- Begin comment --")
- log(comment_text)
- log("-- End comment --")
-
-
-class MockBuilder(object):
- def __init__(self, name):
- self._name = name
-
- def name(self):
- return self._name
-
- def results_url(self):
- return "http://example.com/builders/%s/results/" % self.name()
-
- def force_build(self, username, comments):
- log("MOCK: force_build: name=%s, username=%s, comments=%s" % (
- self._name, username, comments))
-
-
-class MockFailureMap(object):
- def __init__(self, buildbot):
- self._buildbot = buildbot
-
- def is_empty(self):
- return False
-
- def filter_out_old_failures(self, is_old_revision):
- pass
-
- def failing_revisions(self):
- return [29837]
-
- def builders_failing_for(self, revision):
- return [self._buildbot.builder_with_name("Builder1")]
-
- def tests_failing_for(self, revision):
- return ["mock-test-1"]
-
-
-class MockBuildBot(object):
- buildbot_host = "dummy_buildbot_host"
- def __init__(self):
- self._mock_builder1_status = {
- "name": "Builder1",
- "is_green": True,
- "activity": "building",
- }
- self._mock_builder2_status = {
- "name": "Builder2",
- "is_green": True,
- "activity": "idle",
- }
-
- def builder_with_name(self, name):
- return MockBuilder(name)
-
- def builder_statuses(self):
- return [
- self._mock_builder1_status,
- self._mock_builder2_status,
- ]
-
- def red_core_builders_names(self):
- if not self._mock_builder2_status["is_green"]:
- return [self._mock_builder2_status["name"]]
- return []
-
- def red_core_builders(self):
- if not self._mock_builder2_status["is_green"]:
- return [self._mock_builder2_status]
- return []
-
- def idle_red_core_builders(self):
- if not self._mock_builder2_status["is_green"]:
- return [self._mock_builder2_status]
- return []
-
- def last_green_revision(self):
- return 9479
-
- def light_tree_on_fire(self):
- self._mock_builder2_status["is_green"] = False
-
- def failure_map(self):
- return MockFailureMap(self)
-
-
-# FIXME: This should not inherit from Mock
-class MockSCM(Mock):
-
- fake_checkout_root = os.path.realpath("/tmp") # realpath is needed to allow for Mac OS X's /private/tmp
-
- def __init__(self):
- Mock.__init__(self)
- # FIXME: We should probably use real checkout-root detection logic here.
- # os.getcwd() can't work here because other parts of the code assume that "checkout_root"
- # will actually be the root. Since getcwd() is wrong, use a globally fake root for now.
- self.checkout_root = self.fake_checkout_root
-
- def changed_files(self, git_commit=None):
- return ["MockFile1"]
-
- def create_patch(self, git_commit, changed_files=None):
- return "Patch1"
-
- def commit_ids_from_commitish_arguments(self, args):
- return ["Commitish1", "Commitish2"]
-
- def commit_message_for_local_commit(self, commit_id):
- if commit_id == "Commitish1":
- return CommitMessage("CommitMessage1\n" \
- "https://bugs.example.org/show_bug.cgi?id=42\n")
- if commit_id == "Commitish2":
- return CommitMessage("CommitMessage2\n" \
- "https://bugs.example.org/show_bug.cgi?id=75\n")
- raise Exception("Bogus commit_id in commit_message_for_local_commit.")
-
- def diff_for_revision(self, revision):
- return "DiffForRevision%s\n" \
- "http://bugs.webkit.org/show_bug.cgi?id=12345" % revision
-
- def svn_revision_from_commit_text(self, commit_text):
- return "49824"
-
-
-class MockCheckout(object):
-
- _committer_list = CommitterList()
-
- def commit_info_for_revision(self, svn_revision):
- # The real Checkout would probably throw an exception, but this is the only way tests have to get None back at the moment.
- if not svn_revision:
- return None
- return CommitInfo(svn_revision, "eric@webkit.org", {
- "bug_id": 42,
- "author_name": "Adam Barth",
- "author_email": "abarth@webkit.org",
- "author": self._committer_list.committer_by_email("abarth@webkit.org"),
- "reviewer_text": "Darin Adler",
- "reviewer": self._committer_list.committer_by_name("Darin Adler"),
- })
-
- def bug_id_for_revision(self, svn_revision):
- return 12345
-
- def recent_commit_infos_for_files(self, paths):
- return [self.commit_info_for_revision(32)]
-
- def modified_changelogs(self, git_commit, changed_files=None):
- # Ideally we'd return something more interesting here. The problem is
- # that LandDiff will try to actually read the patch from disk!
- return []
-
- def commit_message_for_this_commit(self, git_commit, changed_files=None):
- commit_message = Mock()
- commit_message.message = lambda:"This is a fake commit message that is at least 50 characters."
- return commit_message
-
- def apply_patch(self, patch, force=False):
- pass
-
- def apply_reverse_diff(self, revision):
- pass
-
- def suggested_reviewers(self, git_commit, changed_files=None):
- return [_mock_reviewer]
-
-
-class MockUser(object):
-
- @staticmethod
- def prompt(message, repeat=1, raw_input=raw_input):
- return "Mock user response"
-
- def edit(self, files):
- pass
-
- def edit_changelog(self, files):
- pass
-
- def page(self, message):
- pass
-
- def confirm(self, message=None, default='y'):
- print message
- return default == 'y'
-
- def can_open_url(self):
- return True
-
- def open_url(self, url):
- if url.startswith("file://"):
- log("MOCK: user.open_url: file://...")
- return
- log("MOCK: user.open_url: %s" % url)
-
-
-class MockIRC(object):
-
- def post(self, message):
- log("MOCK: irc.post: %s" % message)
-
- def disconnect(self):
- log("MOCK: irc.disconnect")
-
-
-class MockStatusServer(object):
-
- def __init__(self, bot_id=None, work_items=None):
- self.host = "example.com"
- self.bot_id = bot_id
- self._work_items = work_items or []
-
- def patch_status(self, queue_name, patch_id):
- return None
-
- def svn_revision(self, svn_revision):
- return None
-
- def next_work_item(self, queue_name):
- if not self._work_items:
- return None
- return self._work_items.pop(0)
-
- def release_work_item(self, queue_name, patch):
- log("MOCK: release_work_item: %s %s" % (queue_name, patch.id()))
-
- def update_work_items(self, queue_name, work_items):
- self._work_items = work_items
- log("MOCK: update_work_items: %s %s" % (queue_name, work_items))
-
- def submit_to_ews(self, patch_id):
- log("MOCK: submit_to_ews: %s" % (patch_id))
-
- 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):
- return 191
-
- def results_url_for_status(self, status_id):
- return "http://dummy_url"
-
-
-# FIXME: This should not inherit from Mock
-# FIXME: Unify with common.system.executive_mock.MockExecutive.
-class MockExecutive(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,
- args,
- cwd=None,
- input=None,
- error_handler=None,
- 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"
-
-
-class MockOptions(object):
- """Mock implementation of optparse.Values."""
-
- def __init__(self, **kwargs):
- # The caller can set option values using keyword arguments. We don't
- # set any values by default because we don't know how this
- # object will be used. Generally speaking unit tests should
- # subclass this or provider wrapper functions that set a common
- # set of options.
- for key, value in kwargs.items():
- self.__dict__[key] = value
-
-
-class MockTestPort1(object):
-
- def skips_layout_test(self, test_name):
- return test_name in ["media/foo/bar.html", "foo"]
-
-
-class MockTestPort2(object):
-
- def skips_layout_test(self, test_name):
- return test_name == "media/foo/bar.html"
-
-
-class MockPortFactory(object):
-
- def get_all(self, options=None):
- return {"test_port1": MockTestPort1(), "test_port2": MockTestPort2()}
-
-
-class MockTool(object):
-
- def __init__(self, log_executive=False):
- self.wakeup_event = threading.Event()
- self.bugs = MockBugzilla()
- self.buildbot = MockBuildBot()
- self.executive = MockExecutive(should_log=log_executive)
- self._irc = None
- self.user = MockUser()
- self._scm = MockSCM()
- self._checkout = MockCheckout()
- self.status_server = MockStatusServer()
- self.irc_password = "MOCK irc password"
- self.port_factory = MockPortFactory()
-
- def scm(self):
- return self._scm
-
- def checkout(self):
- return self._checkout
-
- def ensure_irc_connected(self, delegate):
- if not self._irc:
- self._irc = MockIRC()
-
- def irc(self):
- return self._irc
-
- def path(self):
- return "echo"
-
- def port(self):
- return Mock()
-
-
-class MockBrowser(object):
- params = {}
-
- def open(self, url):
- pass
-
- def select_form(self, name):
- pass
-
- def __setitem__(self, key, value):
- self.params[key] = value
-
- def submit(self):
- return Mock(file)
diff --git a/WebKitTools/Scripts/webkitpy/tool/mocktool_unittest.py b/WebKitTools/Scripts/webkitpy/tool/mocktool_unittest.py
deleted file mode 100644
index cceaa2e..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/mocktool_unittest.py
+++ /dev/null
@@ -1,59 +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.
-
-import unittest
-
-from mocktool import MockOptions
-
-
-class MockOptionsTest(unittest.TestCase):
- # MockOptions() should implement the same semantics that
- # optparse.Values does.
-
- def test_get__set(self):
- # Test that we can still set options after we construct the
- # object.
- options = MockOptions()
- options.foo = 'bar'
- self.assertEqual(options.foo, 'bar')
-
- def test_get__unset(self):
- # Test that unset options raise an exception (regular Mock
- # objects return an object and hence are different from
- # optparse.Values()).
- options = MockOptions()
- self.assertRaises(AttributeError, lambda: options.foo)
-
- def test_kwarg__set(self):
- # Test that keyword arguments work in the constructor.
- options = MockOptions(foo='bar')
- self.assertEqual(options.foo, 'bar')
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/tool/multicommandtool.py b/WebKitTools/Scripts/webkitpy/tool/multicommandtool.py
deleted file mode 100644
index 4848ae5..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/multicommandtool.py
+++ /dev/null
@@ -1,314 +0,0 @@
-# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple 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.
-#
-# MultiCommandTool provides a framework for writing svn-like/git-like tools
-# which are called with the following format:
-# tool-name [global options] command-name [command options]
-
-import sys
-
-from optparse import OptionParser, IndentedHelpFormatter, SUPPRESS_USAGE, make_option
-
-from webkitpy.tool.grammar import pluralize
-from webkitpy.common.system.deprecated_logging import log
-
-
-class TryAgain(Exception):
- pass
-
-
-class Command(object):
- name = None
- show_in_main_help = False
- def __init__(self, help_text, argument_names=None, options=None, long_help=None, requires_local_commits=False):
- self.help_text = help_text
- self.long_help = long_help
- self.argument_names = argument_names
- self.required_arguments = self._parse_required_arguments(argument_names)
- self.options = options
- self.requires_local_commits = requires_local_commits
- self._tool = None
- # option_parser can be overriden by the tool using set_option_parser
- # This default parser will be used for standalone_help printing.
- self.option_parser = HelpPrintingOptionParser(usage=SUPPRESS_USAGE, add_help_option=False, option_list=self.options)
-
- # This design is slightly awkward, but we need the
- # the tool to be able to create and modify the option_parser
- # before it knows what Command to run.
- def set_option_parser(self, option_parser):
- self.option_parser = option_parser
- self._add_options_to_parser()
-
- def _add_options_to_parser(self):
- options = self.options or []
- for option in options:
- self.option_parser.add_option(option)
-
- # The tool calls bind_to_tool on each Command after adding it to its list.
- def bind_to_tool(self, tool):
- # Command instances can only be bound to one tool at a time.
- if self._tool and tool != self._tool:
- raise Exception("Command already bound to tool!")
- self._tool = tool
-
- @staticmethod
- def _parse_required_arguments(argument_names):
- required_args = []
- if not argument_names:
- return required_args
- split_args = argument_names.split(" ")
- for argument in split_args:
- if argument[0] == '[':
- # For now our parser is rather dumb. Do some minimal validation that
- # we haven't confused it.
- if argument[-1] != ']':
- raise Exception("Failure to parse argument string %s. Argument %s is missing ending ]" % (argument_names, argument))
- else:
- required_args.append(argument)
- return required_args
-
- def name_with_arguments(self):
- usage_string = self.name
- if self.options:
- usage_string += " [options]"
- if self.argument_names:
- usage_string += " " + self.argument_names
- return usage_string
-
- def parse_args(self, args):
- return self.option_parser.parse_args(args)
-
- def check_arguments_and_execute(self, options, args, tool=None):
- if len(args) < len(self.required_arguments):
- log("%s required, %s provided. Provided: %s Required: %s\nSee '%s help %s' for usage." % (
- pluralize("argument", len(self.required_arguments)),
- pluralize("argument", len(args)),
- "'%s'" % " ".join(args),
- " ".join(self.required_arguments),
- tool.name(),
- self.name))
- return 1
- return self.execute(options, args, tool) or 0
-
- def standalone_help(self):
- help_text = self.name_with_arguments().ljust(len(self.name_with_arguments()) + 3) + self.help_text + "\n\n"
- if self.long_help:
- help_text += "%s\n\n" % self.long_help
- help_text += self.option_parser.format_option_help(IndentedHelpFormatter())
- return help_text
-
- def execute(self, options, args, tool):
- raise NotImplementedError, "subclasses must implement"
-
- # main() exists so that Commands can be turned into stand-alone scripts.
- # Other parts of the code will likely require modification to work stand-alone.
- def main(self, args=sys.argv):
- (options, args) = self.parse_args(args)
- # Some commands might require a dummy tool
- return self.check_arguments_and_execute(options, args)
-
-
-# FIXME: This should just be rolled into Command. help_text and argument_names do not need to be instance variables.
-class AbstractDeclarativeCommand(Command):
- help_text = None
- argument_names = None
- long_help = None
- def __init__(self, options=None, **kwargs):
- Command.__init__(self, self.help_text, self.argument_names, options=options, long_help=self.long_help, **kwargs)
-
-
-class HelpPrintingOptionParser(OptionParser):
- def __init__(self, epilog_method=None, *args, **kwargs):
- self.epilog_method = epilog_method
- OptionParser.__init__(self, *args, **kwargs)
-
- def error(self, msg):
- self.print_usage(sys.stderr)
- error_message = "%s: error: %s\n" % (self.get_prog_name(), msg)
- # This method is overriden to add this one line to the output:
- error_message += "\nType \"%s --help\" to see usage.\n" % self.get_prog_name()
- self.exit(1, error_message)
-
- # We override format_epilog to avoid the default formatting which would paragraph-wrap the epilog
- # and also to allow us to compute the epilog lazily instead of in the constructor (allowing it to be context sensitive).
- def format_epilog(self, epilog):
- if self.epilog_method:
- return "\n%s\n" % self.epilog_method()
- return ""
-
-
-class HelpCommand(AbstractDeclarativeCommand):
- name = "help"
- help_text = "Display information about this program or its subcommands"
- argument_names = "[COMMAND]"
-
- def __init__(self):
- options = [
- make_option("-a", "--all-commands", action="store_true", dest="show_all_commands", help="Print all available commands"),
- ]
- AbstractDeclarativeCommand.__init__(self, options)
- self.show_all_commands = False # A hack used to pass --all-commands to _help_epilog even though it's called by the OptionParser.
-
- def _help_epilog(self):
- # Only show commands which are relevant to this checkout's SCM system. Might this be confusing to some users?
- if self.show_all_commands:
- epilog = "All %prog commands:\n"
- relevant_commands = self._tool.commands[:]
- else:
- epilog = "Common %prog commands:\n"
- relevant_commands = filter(self._tool.should_show_in_main_help, self._tool.commands)
- longest_name_length = max(map(lambda command: len(command.name), relevant_commands))
- relevant_commands.sort(lambda a, b: cmp(a.name, b.name))
- command_help_texts = map(lambda command: " %s %s\n" % (command.name.ljust(longest_name_length), command.help_text), relevant_commands)
- epilog += "%s\n" % "".join(command_help_texts)
- epilog += "See '%prog help --all-commands' to list all commands.\n"
- epilog += "See '%prog help COMMAND' for more information on a specific command.\n"
- return epilog.replace("%prog", self._tool.name()) # Use of %prog here mimics OptionParser.expand_prog_name().
-
- # FIXME: This is a hack so that we don't show --all-commands as a global option:
- def _remove_help_options(self):
- for option in self.options:
- self.option_parser.remove_option(option.get_opt_string())
-
- def execute(self, options, args, tool):
- if args:
- command = self._tool.command_by_name(args[0])
- if command:
- print command.standalone_help()
- return 0
-
- self.show_all_commands = options.show_all_commands
- self._remove_help_options()
- self.option_parser.print_help()
- return 0
-
-
-class MultiCommandTool(object):
- global_options = None
-
- def __init__(self, name=None, commands=None):
- self._name = name or OptionParser(prog=name).get_prog_name() # OptionParser has nice logic for fetching the name.
- # Allow the unit tests to disable command auto-discovery.
- self.commands = commands or [cls() for cls in self._find_all_commands() if cls.name]
- self.help_command = self.command_by_name(HelpCommand.name)
- # Require a help command, even if the manual test list doesn't include one.
- if not self.help_command:
- self.help_command = HelpCommand()
- self.commands.append(self.help_command)
- for command in self.commands:
- command.bind_to_tool(self)
-
- @classmethod
- def _add_all_subclasses(cls, class_to_crawl, seen_classes):
- for subclass in class_to_crawl.__subclasses__():
- if subclass not in seen_classes:
- seen_classes.add(subclass)
- cls._add_all_subclasses(subclass, seen_classes)
-
- @classmethod
- def _find_all_commands(cls):
- commands = set()
- cls._add_all_subclasses(Command, commands)
- return sorted(commands)
-
- def name(self):
- return self._name
-
- def _create_option_parser(self):
- usage = "Usage: %prog [options] COMMAND [ARGS]"
- return HelpPrintingOptionParser(epilog_method=self.help_command._help_epilog, prog=self.name(), usage=usage)
-
- @staticmethod
- def _split_command_name_from_args(args):
- # Assume the first argument which doesn't start with "-" is the command name.
- command_index = 0
- for arg in args:
- if arg[0] != "-":
- break
- command_index += 1
- else:
- return (None, args[:])
-
- command = args[command_index]
- return (command, args[:command_index] + args[command_index + 1:])
-
- def command_by_name(self, command_name):
- for command in self.commands:
- if command_name == command.name:
- return command
- return None
-
- def path(self):
- raise NotImplementedError, "subclasses must implement"
-
- def command_completed(self):
- pass
-
- def should_show_in_main_help(self, command):
- return command.show_in_main_help
-
- def should_execute_command(self, command):
- return True
-
- def _add_global_options(self, option_parser):
- global_options = self.global_options or []
- for option in global_options:
- option_parser.add_option(option)
-
- def handle_global_options(self, options):
- pass
-
- def main(self, argv=sys.argv):
- (command_name, args) = self._split_command_name_from_args(argv[1:])
-
- option_parser = self._create_option_parser()
- self._add_global_options(option_parser)
-
- command = self.command_by_name(command_name) or self.help_command
- if not command:
- option_parser.error("%s is not a recognized command" % command_name)
-
- command.set_option_parser(option_parser)
- (options, args) = command.parse_args(args)
- self.handle_global_options(options)
-
- (should_execute, failure_reason) = self.should_execute_command(command)
- if not should_execute:
- log(failure_reason)
- return 0 # FIXME: Should this really be 0?
-
- while True:
- try:
- result = command.check_arguments_and_execute(options, args, self)
- break
- except TryAgain, e:
- pass
-
- self.command_completed()
- return result
diff --git a/WebKitTools/Scripts/webkitpy/tool/multicommandtool_unittest.py b/WebKitTools/Scripts/webkitpy/tool/multicommandtool_unittest.py
deleted file mode 100644
index c19095c..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/multicommandtool_unittest.py
+++ /dev/null
@@ -1,177 +0,0 @@
-# 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
-# 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
-
-from optparse import make_option
-
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.tool.multicommandtool import MultiCommandTool, Command, TryAgain
-
-
-class TrivialCommand(Command):
- name = "trivial"
- show_in_main_help = True
- def __init__(self, **kwargs):
- Command.__init__(self, "help text", **kwargs)
-
- def execute(self, options, args, tool):
- pass
-
-
-class UncommonCommand(TrivialCommand):
- name = "uncommon"
- show_in_main_help = False
-
-
-class LikesToRetry(Command):
- name = "likes-to-retry"
- show_in_main_help = True
-
- def __init__(self, **kwargs):
- Command.__init__(self, "help text", **kwargs)
- self.execute_count = 0
-
- def execute(self, options, args, tool):
- self.execute_count += 1
- if self.execute_count < 2:
- raise TryAgain()
-
-
-class CommandTest(unittest.TestCase):
- def test_name_with_arguments(self):
- command_with_args = TrivialCommand(argument_names="ARG1 ARG2")
- self.assertEqual(command_with_args.name_with_arguments(), "trivial ARG1 ARG2")
-
- command_with_args = TrivialCommand(options=[make_option("--my_option")])
- self.assertEqual(command_with_args.name_with_arguments(), "trivial [options]")
-
- def test_parse_required_arguments(self):
- self.assertEqual(Command._parse_required_arguments("ARG1 ARG2"), ["ARG1", "ARG2"])
- self.assertEqual(Command._parse_required_arguments("[ARG1] [ARG2]"), [])
- self.assertEqual(Command._parse_required_arguments("[ARG1] ARG2"), ["ARG2"])
- # Note: We might make our arg parsing smarter in the future and allow this type of arguments string.
- self.assertRaises(Exception, Command._parse_required_arguments, "[ARG1 ARG2]")
-
- def test_required_arguments(self):
- two_required_arguments = TrivialCommand(argument_names="ARG1 ARG2 [ARG3]")
- expected_missing_args_error = "2 arguments required, 1 argument provided. Provided: 'foo' Required: ARG1 ARG2\nSee 'trivial-tool help trivial' for usage.\n"
- exit_code = OutputCapture().assert_outputs(self, two_required_arguments.check_arguments_and_execute, [None, ["foo"], TrivialTool()], expected_stderr=expected_missing_args_error)
- self.assertEqual(exit_code, 1)
-
-
-class TrivialTool(MultiCommandTool):
- def __init__(self, commands=None):
- MultiCommandTool.__init__(self, name="trivial-tool", commands=commands)
-
- def path(self):
- return __file__
-
- def should_execute_command(self, command):
- return (True, None)
-
-
-class MultiCommandToolTest(unittest.TestCase):
- def _assert_split(self, args, expected_split):
- self.assertEqual(MultiCommandTool._split_command_name_from_args(args), expected_split)
-
- def test_split_args(self):
- # MultiCommandToolTest._split_command_name_from_args returns: (command, args)
- full_args = ["--global-option", "command", "--option", "arg"]
- full_args_expected = ("command", ["--global-option", "--option", "arg"])
- self._assert_split(full_args, full_args_expected)
-
- full_args = []
- full_args_expected = (None, [])
- self._assert_split(full_args, full_args_expected)
-
- full_args = ["command", "arg"]
- full_args_expected = ("command", ["arg"])
- self._assert_split(full_args, full_args_expected)
-
- def test_command_by_name(self):
- # This also tests Command auto-discovery.
- tool = TrivialTool()
- self.assertEqual(tool.command_by_name("trivial").name, "trivial")
- self.assertEqual(tool.command_by_name("bar"), None)
-
- def _assert_tool_main_outputs(self, tool, main_args, expected_stdout, expected_stderr = "", expected_exit_code=0):
- exit_code = OutputCapture().assert_outputs(self, tool.main, [main_args], expected_stdout=expected_stdout, expected_stderr=expected_stderr)
- self.assertEqual(exit_code, expected_exit_code)
-
- def test_retry(self):
- likes_to_retry = LikesToRetry()
- tool = TrivialTool(commands=[likes_to_retry])
- tool.main(["tool", "likes-to-retry"])
- self.assertEqual(likes_to_retry.execute_count, 2)
-
- def test_global_help(self):
- tool = TrivialTool(commands=[TrivialCommand(), UncommonCommand()])
- expected_common_commands_help = """Usage: trivial-tool [options] COMMAND [ARGS]
-
-Options:
- -h, --help show this help message and exit
-
-Common trivial-tool commands:
- trivial help text
-
-See 'trivial-tool help --all-commands' to list all commands.
-See 'trivial-tool help COMMAND' for more information on a specific command.
-
-"""
- self._assert_tool_main_outputs(tool, ["tool"], expected_common_commands_help)
- self._assert_tool_main_outputs(tool, ["tool", "help"], expected_common_commands_help)
- expected_all_commands_help = """Usage: trivial-tool [options] COMMAND [ARGS]
-
-Options:
- -h, --help show this help message and exit
-
-All trivial-tool commands:
- help Display information about this program or its subcommands
- trivial help text
- uncommon help text
-
-See 'trivial-tool help --all-commands' to list all commands.
-See 'trivial-tool help COMMAND' for more information on a specific command.
-
-"""
- self._assert_tool_main_outputs(tool, ["tool", "help", "--all-commands"], expected_all_commands_help)
- # Test that arguments can be passed before commands as well
- self._assert_tool_main_outputs(tool, ["tool", "--all-commands", "help"], expected_all_commands_help)
-
-
- def test_command_help(self):
- command_with_options = TrivialCommand(options=[make_option("--my_option")], long_help="LONG HELP")
- tool = TrivialTool(commands=[command_with_options])
- expected_subcommand_help = "trivial [options] help text\n\nLONG HELP\n\nOptions:\n --my_option=MY_OPTION\n\n"
- self._assert_tool_main_outputs(tool, ["tool", "help", "trivial"], expected_subcommand_help)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/__init__.py b/WebKitTools/Scripts/webkitpy/tool/steps/__init__.py
deleted file mode 100644
index 64d9d05..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/__init__.py
+++ /dev/null
@@ -1,59 +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.
-
-# FIXME: Is this the right way to do this?
-from webkitpy.tool.steps.applypatch import ApplyPatch
-from webkitpy.tool.steps.applypatchwithlocalcommit import ApplyPatchWithLocalCommit
-from webkitpy.tool.steps.build import Build
-from webkitpy.tool.steps.checkstyle import CheckStyle
-from webkitpy.tool.steps.cleanworkingdirectory import CleanWorkingDirectory
-from webkitpy.tool.steps.cleanworkingdirectorywithlocalcommits import CleanWorkingDirectoryWithLocalCommits
-from webkitpy.tool.steps.closebug import CloseBug
-from webkitpy.tool.steps.closebugforlanddiff import CloseBugForLandDiff
-from webkitpy.tool.steps.closepatch import ClosePatch
-from webkitpy.tool.steps.commit import Commit
-from webkitpy.tool.steps.confirmdiff import ConfirmDiff
-from webkitpy.tool.steps.createbug import CreateBug
-from webkitpy.tool.steps.editchangelog import EditChangeLog
-from webkitpy.tool.steps.ensurebuildersaregreen import EnsureBuildersAreGreen
-from webkitpy.tool.steps.ensurelocalcommitifneeded import EnsureLocalCommitIfNeeded
-from webkitpy.tool.steps.obsoletepatches import ObsoletePatches
-from webkitpy.tool.steps.options import Options
-from webkitpy.tool.steps.postdiff import PostDiff
-from webkitpy.tool.steps.postdiffforcommit import PostDiffForCommit
-from webkitpy.tool.steps.postdiffforrevert import PostDiffForRevert
-from webkitpy.tool.steps.preparechangelogforrevert import PrepareChangeLogForRevert
-from webkitpy.tool.steps.preparechangelog import PrepareChangeLog
-from webkitpy.tool.steps.promptforbugortitle import PromptForBugOrTitle
-from webkitpy.tool.steps.reopenbugafterrollout import ReopenBugAfterRollout
-from webkitpy.tool.steps.revertrevision import RevertRevision
-from webkitpy.tool.steps.runtests import RunTests
-from webkitpy.tool.steps.suggestreviewers import SuggestReviewers
-from webkitpy.tool.steps.updatechangelogswithreviewer import UpdateChangeLogsWithReviewer
-from webkitpy.tool.steps.update import Update
-from webkitpy.tool.steps.validatereviewer import ValidateReviewer
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/abstractstep.py b/WebKitTools/Scripts/webkitpy/tool/steps/abstractstep.py
deleted file mode 100644
index 5525ea0..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/abstractstep.py
+++ /dev/null
@@ -1,79 +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.
-
-from webkitpy.common.system.deprecated_logging import log
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.common.config.ports import WebKitPort
-from webkitpy.tool.steps.options import Options
-
-
-class AbstractStep(object):
- def __init__(self, tool, options):
- self._tool = tool
- self._options = options
-
- # FIXME: This should use tool.port()
- def _run_script(self, script_name, args=None, quiet=False, port=WebKitPort):
- log("Running %s" % script_name)
- command = [port.script_path(script_name)]
- if args:
- command.extend(args)
- self._tool.executive.run_and_throw_if_fail(command, quiet)
-
- def _changed_files(self, state):
- return self.cached_lookup(state, "changed_files")
-
- _well_known_keys = {
- "bug_title": lambda self, state: self._tool.bugs.fetch_bug(state["bug_id"]).title(),
- "changed_files": lambda self, state: self._tool.scm().changed_files(self._options.git_commit),
- "diff": lambda self, state: self._tool.scm().create_patch(self._options.git_commit, changed_files=self._changed_files(state)),
- "changelogs": lambda self, state: self._tool.checkout().modified_changelogs(self._options.git_commit, changed_files=self._changed_files(state)),
- }
-
- def cached_lookup(self, state, key, promise=None):
- if state.get(key):
- return state[key]
- if not promise:
- promise = self._well_known_keys.get(key)
- state[key] = promise(self, state)
- return state[key]
-
- def did_modify_checkout(self, state):
- state["diff"] = None
- state["changelogs"] = None
- state["changed_files"] = None
-
- @classmethod
- def options(cls):
- return [
- # We need this option here because cached_lookup uses it. :(
- Options.git_commit,
- ]
-
- def run(self, state):
- raise NotImplementedError, "subclasses must implement"
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/applypatch.py b/WebKitTools/Scripts/webkitpy/tool/steps/applypatch.py
deleted file mode 100644
index 327ac09..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/applypatch.py
+++ /dev/null
@@ -1,43 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.deprecated_logging import log
-
-class ApplyPatch(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.non_interactive,
- Options.force_patch,
- ]
-
- def run(self, state):
- log("Processing patch %s from bug %s." % (state["patch"].id(), state["patch"].bug_id()))
- self._tool.checkout().apply_patch(state["patch"], force=self._options.non_interactive or self._options.force_patch)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/applypatchwithlocalcommit.py b/WebKitTools/Scripts/webkitpy/tool/steps/applypatchwithlocalcommit.py
deleted file mode 100644
index 3dcd8d9..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/applypatchwithlocalcommit.py
+++ /dev/null
@@ -1,43 +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.
-
-from webkitpy.tool.steps.applypatch import ApplyPatch
-from webkitpy.tool.steps.options import Options
-
-class ApplyPatchWithLocalCommit(ApplyPatch):
- @classmethod
- def options(cls):
- return ApplyPatch.options() + [
- Options.local_commit,
- ]
-
- def run(self, state):
- ApplyPatch.run(self, state)
- if self._options.local_commit:
- commit_message = self._tool.checkout().commit_message_for_this_commit(git_commit=None)
- self._tool.scm().commit_locally_with_message(commit_message.message() or state["patch"].name())
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/build.py b/WebKitTools/Scripts/webkitpy/tool/steps/build.py
deleted file mode 100644
index 0990b8b..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/build.py
+++ /dev/null
@@ -1,54 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.deprecated_logging import log
-
-
-class Build(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.build,
- Options.quiet,
- Options.build_style,
- ]
-
- def build(self, build_style):
- self._tool.executive.run_and_throw_if_fail(self._tool.port().build_webkit_command(build_style=build_style), self._options.quiet)
-
- def run(self, state):
- if not self._options.build:
- return
- log("Building WebKit")
- if self._options.build_style == "both":
- self.build("debug")
- self.build("release")
- else:
- self.build(self._options.build_style)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/checkstyle.py b/WebKitTools/Scripts/webkitpy/tool/steps/checkstyle.py
deleted file mode 100644
index af66c50..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/checkstyle.py
+++ /dev/null
@@ -1,66 +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.
-
-import os
-
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.deprecated_logging import error
-
-class CheckStyle(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.non_interactive,
- Options.check_style,
- Options.git_commit,
- ]
-
- def run(self, state):
- if not self._options.check_style:
- return
- os.chdir(self._tool.scm().checkout_root)
-
- args = []
- if self._options.git_commit:
- args.append("--git-commit")
- args.append(self._options.git_commit)
-
- args.append("--diff-files")
- args.extend(self._changed_files(state))
-
- try:
- self._run_script("check-webkit-style", args)
- except ScriptError, e:
- if self._options.non_interactive:
- # We need to re-raise the exception here to have the
- # style-queue do the right thing.
- raise e
- if not self._tool.user.confirm("Are you sure you want to continue?"):
- exit(1)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py b/WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py
deleted file mode 100644
index e13fbc2..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py
+++ /dev/null
@@ -1,52 +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.
-
-import os
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-
-
-class CleanWorkingDirectory(AbstractStep):
- def __init__(self, tool, options, allow_local_commits=False):
- AbstractStep.__init__(self, tool, options)
- self._allow_local_commits = allow_local_commits
-
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.force_clean,
- Options.clean,
- ]
-
- def run(self, state):
- os.chdir(self._tool.scm().checkout_root)
- if not self._allow_local_commits:
- self._tool.scm().ensure_no_local_commits(self._options.force_clean)
- if self._options.clean:
- self._tool.scm().ensure_clean_working_directory(force_clean=self._options.force_clean)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectorywithlocalcommits.py b/WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectorywithlocalcommits.py
deleted file mode 100644
index f06f94e..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/cleanworkingdirectorywithlocalcommits.py
+++ /dev/null
@@ -1,34 +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.
-
-from webkitpy.tool.steps.cleanworkingdirectory import CleanWorkingDirectory
-
-class CleanWorkingDirectoryWithLocalCommits(CleanWorkingDirectory):
- def __init__(self, tool, options):
- # FIXME: This a bit of a hack. Consider doing this more cleanly.
- CleanWorkingDirectory.__init__(self, tool, options, allow_local_commits=True)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/closebug.py b/WebKitTools/Scripts/webkitpy/tool/steps/closebug.py
deleted file mode 100644
index e77bc24..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/closebug.py
+++ /dev/null
@@ -1,51 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.deprecated_logging import log
-
-
-class CloseBug(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.close_bug,
- ]
-
- def run(self, state):
- if not self._options.close_bug:
- return
- # Check to make sure there are no r? or r+ patches on the bug before closing.
- # Assume that r- patches are just previous patches someone forgot to obsolete.
- patches = self._tool.bugs.fetch_bug(state["patch"].bug_id()).patches()
- for patch in patches:
- if patch.review() == "?" or patch.review() == "+":
- log("Not closing bug %s as attachment %s has review=%s. Assuming there are more patches to land from this bug." % (patch.bug_id(), patch.id(), patch.review()))
- return
- self._tool.bugs.close_bug_as_fixed(state["patch"].bug_id(), "All reviewed patches have been landed. Closing bug.")
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff.py b/WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff.py
deleted file mode 100644
index e5a68db..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff.py
+++ /dev/null
@@ -1,58 +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.
-
-from webkitpy.tool.comments import bug_comment_from_commit_text
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.deprecated_logging import log
-
-
-class CloseBugForLandDiff(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.close_bug,
- ]
-
- def run(self, state):
- comment_text = bug_comment_from_commit_text(self._tool.scm(), state["commit_text"])
- bug_id = state.get("bug_id")
- if not bug_id and state.get("patch"):
- bug_id = state.get("patch").bug_id()
-
- if bug_id:
- log("Updating bug %s" % bug_id)
- if self._options.close_bug:
- self._tool.bugs.close_bug_as_fixed(bug_id, comment_text)
- else:
- # FIXME: We should a smart way to figure out if the patch is attached
- # to the bug, and if so obsolete it.
- self._tool.bugs.post_comment_to_bug(bug_id, comment_text)
- else:
- log(comment_text)
- log("No bug id provided.")
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py b/WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py
deleted file mode 100644
index 0a56564..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# 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
-# 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 unittest
-
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.tool.mocktool import MockOptions, MockTool
-from webkitpy.tool.steps.closebugforlanddiff import CloseBugForLandDiff
-
-class CloseBugForLandDiffTest(unittest.TestCase):
- def test_empty_state(self):
- capture = OutputCapture()
- step = CloseBugForLandDiff(MockTool(), MockOptions())
- expected_stderr = "Committed r49824: <http://trac.webkit.org/changeset/49824>\nNo bug id provided.\n"
- capture.assert_outputs(self, step.run, [{"commit_text" : "Mock commit text"}], expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/closepatch.py b/WebKitTools/Scripts/webkitpy/tool/steps/closepatch.py
deleted file mode 100644
index ff94df8..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/closepatch.py
+++ /dev/null
@@ -1,36 +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.
-
-from webkitpy.tool.comments import bug_comment_from_commit_text
-from webkitpy.tool.steps.abstractstep import AbstractStep
-
-
-class ClosePatch(AbstractStep):
- def run(self, state):
- comment_text = bug_comment_from_commit_text(self._tool.scm(), state["commit_text"])
- self._tool.bugs.clear_attachment_flags(state["patch"].id(), comment_text)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/commit.py b/WebKitTools/Scripts/webkitpy/tool/steps/commit.py
deleted file mode 100644
index 5c6bdb7..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/commit.py
+++ /dev/null
@@ -1,81 +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.
-
-from webkitpy.common.checkout.changelog import view_source_url
-from webkitpy.common.checkout.scm import AuthenticationError, AmbiguousCommitError
-from webkitpy.common.system.deprecated_logging import log
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.common.system.user import User
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-
-
-class Commit(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.git_commit,
- ]
-
- def _commit_warning(self, error):
- working_directory_message = "" if error.working_directory_is_clean else " and working copy changes"
- return ('There are %s local commits%s. Everything will be committed as a single commit. '
- 'To avoid this prompt, set "git config webkit-patch.commit-should-always-squash true".' % (
- error.num_local_commits, working_directory_message))
-
- def run(self, state):
- self._commit_message = self._tool.checkout().commit_message_for_this_commit(self._options.git_commit).message()
- if len(self._commit_message) < 50:
- raise Exception("Attempted to commit with a commit message shorter than 50 characters. Either your patch is missing a ChangeLog or webkit-patch may have a bug.")
-
- self._state = state
-
- username = None
- force_squash = False
-
- num_tries = 0
- while num_tries < 3:
- num_tries += 1
-
- try:
- scm = self._tool.scm()
- commit_text = scm.commit_with_message(self._commit_message, git_commit=self._options.git_commit, username=username, force_squash=force_squash)
- svn_revision = scm.svn_revision_from_commit_text(commit_text)
- log("Committed r%s: <%s>" % (svn_revision, view_source_url(svn_revision)))
- self._state["commit_text"] = commit_text
- break;
- except AmbiguousCommitError, e:
- if self._tool.user.confirm(self._commit_warning(e)):
- force_squash = True
- else:
- # This will correctly interrupt the rest of the commit process.
- raise ScriptError(message="Did not commit")
- except AuthenticationError, e:
- username = self._tool.user.prompt("%s login: " % e.server_host, repeat=5)
- if not username:
- raise ScriptError("You need to specify the username on %s to perform the commit as." % self.svn_server_host)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/confirmdiff.py b/WebKitTools/Scripts/webkitpy/tool/steps/confirmdiff.py
deleted file mode 100644
index 7e8e348..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/confirmdiff.py
+++ /dev/null
@@ -1,77 +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.
-
-import urllib
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.prettypatch import PrettyPatch
-from webkitpy.common.system import logutils
-from webkitpy.common.system.executive import ScriptError
-
-
-_log = logutils.get_logger(__file__)
-
-
-class ConfirmDiff(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.confirm,
- ]
-
- def _show_pretty_diff(self, diff):
- if not self._tool.user.can_open_url():
- return None
-
- try:
- pretty_patch = PrettyPatch(self._tool.executive,
- self._tool.scm().checkout_root)
- pretty_diff_file = pretty_patch.pretty_diff_file(diff)
- url = "file://%s" % urllib.quote(pretty_diff_file.name)
- self._tool.user.open_url(url)
- # We return the pretty_diff_file here because we need to keep the
- # file alive until the user has had a chance to confirm the diff.
- return pretty_diff_file
- except ScriptError, e:
- _log.warning("PrettyPatch failed. :(")
- except OSError, e:
- _log.warning("PrettyPatch unavailable.")
-
- def run(self, state):
- if not self._options.confirm:
- return
- diff = self.cached_lookup(state, "diff")
- pretty_diff_file = self._show_pretty_diff(diff)
- if not pretty_diff_file:
- self._tool.user.page(diff)
- diff_correct = self._tool.user.confirm("Was that diff correct?")
- if pretty_diff_file:
- pretty_diff_file.close()
- if not diff_correct:
- exit(1)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/createbug.py b/WebKitTools/Scripts/webkitpy/tool/steps/createbug.py
deleted file mode 100644
index 0ab6f68..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/createbug.py
+++ /dev/null
@@ -1,52 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-
-
-class CreateBug(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.cc,
- Options.component,
- Options.blocks,
- ]
-
- def run(self, state):
- # No need to create a bug if we already have one.
- if state.get("bug_id"):
- return
- cc = self._options.cc
- if not cc:
- cc = state.get("bug_cc")
- blocks = self._options.blocks
- if not blocks:
- blocks = state.get("bug_blocked")
- state["bug_id"] = self._tool.bugs.create_bug(state["bug_title"], state["bug_description"], blocked=blocks, component=self._options.component, cc=cc)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/editchangelog.py b/WebKitTools/Scripts/webkitpy/tool/steps/editchangelog.py
deleted file mode 100644
index 4d9646f..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/editchangelog.py
+++ /dev/null
@@ -1,38 +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.
-
-import os
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-
-
-class EditChangeLog(AbstractStep):
- def run(self, state):
- os.chdir(self._tool.scm().checkout_root)
- self._tool.user.edit_changelog(self.cached_lookup(state, "changelogs"))
- self.did_modify_checkout(state)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/ensurebuildersaregreen.py b/WebKitTools/Scripts/webkitpy/tool/steps/ensurebuildersaregreen.py
deleted file mode 100644
index a4fc174..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/ensurebuildersaregreen.py
+++ /dev/null
@@ -1,48 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.deprecated_logging import log, error
-
-
-class EnsureBuildersAreGreen(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.check_builders,
- ]
-
- def run(self, state):
- if not self._options.check_builders:
- return
- red_builders_names = self._tool.buildbot.red_core_builders_names()
- if not red_builders_names:
- return
- red_builders_names = map(lambda name: "\"%s\"" % name, red_builders_names) # Add quotes around the names.
- log("\nWARNING: Builders [%s] are red, please watch your commit carefully.\nSee http://%s/console?category=core\n" % (", ".join(red_builders_names), self._tool.buildbot.buildbot_host))
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/ensurelocalcommitifneeded.py b/WebKitTools/Scripts/webkitpy/tool/steps/ensurelocalcommitifneeded.py
deleted file mode 100644
index d0cda46..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/ensurelocalcommitifneeded.py
+++ /dev/null
@@ -1,43 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.deprecated_logging import error
-
-
-class EnsureLocalCommitIfNeeded(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.local_commit,
- ]
-
- def run(self, state):
- if self._options.local_commit and not self._tool.scm().supports_local_commits():
- error("--local-commit passed, but %s does not support local commits" % self._tool.scm.display_name())
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/metastep.py b/WebKitTools/Scripts/webkitpy/tool/steps/metastep.py
deleted file mode 100644
index 7cbd1c5..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/metastep.py
+++ /dev/null
@@ -1,54 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-
-
-# FIXME: Unify with StepSequence? I'm not sure yet which is the better design.
-class MetaStep(AbstractStep):
- substeps = [] # Override in subclasses
- def __init__(self, tool, options):
- AbstractStep.__init__(self, tool, options)
- self._step_instances = []
- for step_class in self.substeps:
- self._step_instances.append(step_class(tool, options))
-
- @staticmethod
- def _collect_options_from_steps(steps):
- collected_options = []
- for step in steps:
- collected_options = collected_options + step.options()
- return collected_options
-
- @classmethod
- def options(cls):
- return cls._collect_options_from_steps(cls.substeps)
-
- def run(self, state):
- for step in self._step_instances:
- step.run(state)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/obsoletepatches.py b/WebKitTools/Scripts/webkitpy/tool/steps/obsoletepatches.py
deleted file mode 100644
index de508c6..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/obsoletepatches.py
+++ /dev/null
@@ -1,51 +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.
-
-from webkitpy.tool.grammar import pluralize
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.deprecated_logging import log
-
-
-class ObsoletePatches(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.obsolete_patches,
- ]
-
- def run(self, state):
- if not self._options.obsolete_patches:
- return
- bug_id = state["bug_id"]
- patches = self._tool.bugs.fetch_bug(bug_id).patches()
- if not patches:
- return
- log("Obsoleting %s on bug %s" % (pluralize("old patch", len(patches)), bug_id))
- for patch in patches:
- self._tool.bugs.obsolete_attachment(patch.id())
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/options.py b/WebKitTools/Scripts/webkitpy/tool/steps/options.py
deleted file mode 100644
index 4f17dd3..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/options.py
+++ /dev/null
@@ -1,59 +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.
-
-from optparse import make_option
-
-class Options(object):
- blocks = make_option("--blocks", action="store", dest="blocks", default=None, help="Bug number which the created bug blocks.")
- build = make_option("--build", action="store_true", dest="build", default=False, help="Build and run run-webkit-tests before committing.")
- build_style = make_option("--build-style", action="store", dest="build_style", default=None, help="Whether to build debug, release, or both.")
- cc = make_option("--cc", action="store", type="string", dest="cc", help="Comma-separated list of email addresses to carbon-copy.")
- check_builders = make_option("--ignore-builders", action="store_false", dest="check_builders", default=True, help="Don't check to see if the build.webkit.org builders are green before landing.")
- check_style = make_option("--ignore-style", action="store_false", dest="check_style", default=True, help="Don't check to see if the patch has proper style before uploading.")
- clean = make_option("--no-clean", action="store_false", dest="clean", default=True, help="Don't check if the working directory is clean before applying patches")
- close_bug = make_option("--no-close", action="store_false", dest="close_bug", default=True, help="Leave bug open after landing.")
- comment = make_option("--comment", action="store", type="string", dest="comment", help="Comment to post to bug.")
- component = make_option("--component", action="store", type="string", dest="component", help="Component for the new bug.")
- confirm = make_option("--no-confirm", action="store_false", dest="confirm", default=True, help="Skip confirmation steps.")
- description = make_option("-m", "--description", action="store", type="string", dest="description", help="Description string for the attachment (default: \"patch\")")
- email = make_option("--email", action="store", type="string", dest="email", help="Email address to use in ChangeLogs.")
- force_clean = make_option("--force-clean", action="store_true", dest="force_clean", default=False, help="Clean working directory before applying patches (removes local changes and commits)")
- force_patch = make_option("--force-patch", action="store_true", dest="force_patch", default=False, help="Forcefully applies the patch, continuing past errors.")
- git_commit = make_option("-g", "--git-commit", action="store", dest="git_commit", help="Operate on a local commit. If a range, the commits are squashed into one. HEAD.. operates on working copy changes only.")
- local_commit = make_option("--local-commit", action="store_true", dest="local_commit", default=False, help="Make a local commit for each applied patch")
- non_interactive = make_option("--non-interactive", action="store_true", dest="non_interactive", default=False, help="Never prompt the user, fail as fast as possible.")
- obsolete_patches = make_option("--no-obsolete", action="store_false", dest="obsolete_patches", default=True, help="Do not obsolete old patches before posting this one.")
- open_bug = make_option("--open-bug", action="store_true", dest="open_bug", default=False, help="Opens the associated bug in a browser.")
- parent_command = make_option("--parent-command", action="store", dest="parent_command", default=None, help="(Internal) The command that spawned this instance.")
- quiet = make_option("--quiet", action="store_true", dest="quiet", default=False, help="Produce less console output.")
- request_commit = make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review.")
- review = make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review.")
- reviewer = make_option("-r", "--reviewer", action="store", type="string", dest="reviewer", help="Update ChangeLogs to say Reviewed by REVIEWER.")
- suggest_reviewers = make_option("--suggest-reviewers", action="store_true", default=False, help="Offer to CC appropriate reviewers.")
- test = make_option("--test", action="store_true", dest="test", default=False, help="Run run-webkit-tests before committing.")
- update = make_option("--no-update", action="store_false", dest="update", default=True, help="Don't update the working directory.")
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/postdiff.py b/WebKitTools/Scripts/webkitpy/tool/steps/postdiff.py
deleted file mode 100644
index c40b6ff..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/postdiff.py
+++ /dev/null
@@ -1,50 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-
-
-class PostDiff(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.description,
- Options.comment,
- Options.review,
- Options.request_commit,
- Options.open_bug,
- ]
-
- def run(self, state):
- diff = self.cached_lookup(state, "diff")
- description = self._options.description or "Patch"
- comment_text = self._options.comment
- 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/postdiffforcommit.py b/WebKitTools/Scripts/webkitpy/tool/steps/postdiffforcommit.py
deleted file mode 100644
index 13bc00c..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/postdiffforcommit.py
+++ /dev/null
@@ -1,39 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-
-
-class PostDiffForCommit(AbstractStep):
- def run(self, state):
- self._tool.bugs.add_patch_to_bug(
- state["bug_id"],
- self.cached_lookup(state, "diff"),
- "Patch for landing",
- mark_for_review=False,
- mark_for_landing=True)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/postdiffforrevert.py b/WebKitTools/Scripts/webkitpy/tool/steps/postdiffforrevert.py
deleted file mode 100644
index bfa631f..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/postdiffforrevert.py
+++ /dev/null
@@ -1,49 +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.
-
-from webkitpy.common.net.bugzilla import Attachment
-from webkitpy.tool.steps.abstractstep import AbstractStep
-
-
-class PostDiffForRevert(AbstractStep):
- def run(self, state):
- comment_text = "Any committer can land this patch automatically by \
-marking it commit-queue+. The commit-queue will build and test \
-the patch before landing to ensure that the rollout will be \
-successful. This process takes approximately 15 minutes.\n\n\
-If you would like to land the rollout faster, you can use the \
-following command:\n\n\
- webkit-patch land-attachment ATTACHMENT_ID --ignore-builders\n\n\
-where ATTACHMENT_ID is the ID of this attachment."
- self._tool.bugs.add_patch_to_bug(
- state["bug_id"],
- self.cached_lookup(state, "diff"),
- "%s%s" % (Attachment.rollout_preamble, state["revision"]),
- comment_text=comment_text,
- mark_for_review=False,
- mark_for_commit_queue=True)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelog.py b/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelog.py
deleted file mode 100644
index 099dfe3..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelog.py
+++ /dev/null
@@ -1,77 +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.
-
-import os
-
-from webkitpy.common.checkout.changelog import ChangeLog
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.deprecated_logging import error
-
-
-class PrepareChangeLog(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.quiet,
- Options.email,
- Options.git_commit,
- ]
-
- def _ensure_bug_url(self, state):
- if not state.get("bug_id"):
- return
- bug_id = state.get("bug_id")
- changelogs = self.cached_lookup(state, "changelogs")
- for changelog_path in changelogs:
- changelog = ChangeLog(changelog_path)
- if not changelog.latest_entry().bug_id():
- changelog.set_short_description_and_bug_url(
- self.cached_lookup(state, "bug_title"),
- self._tool.bugs.bug_url_for_bug_id(bug_id))
-
- def run(self, state):
- if self.cached_lookup(state, "changelogs"):
- self._ensure_bug_url(state)
- return
- os.chdir(self._tool.scm().checkout_root)
- args = [self._tool.port().script_path("prepare-ChangeLog")]
- if state.get("bug_id"):
- args.append("--bug=%s" % state["bug_id"])
- if self._options.email:
- args.append("--email=%s" % self._options.email)
-
- if self._tool.scm().supports_local_commits():
- args.append("--merge-base=%s" % self._tool.scm().merge_base(self._options.git_commit))
-
- try:
- self._tool.executive.run_and_throw_if_fail(args, self._options.quiet)
- except ScriptError, e:
- error("Unable to prepare ChangeLogs.")
- self.did_modify_checkout(state)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelog_unittest.py b/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelog_unittest.py
deleted file mode 100644
index eceffdf..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelog_unittest.py
+++ /dev/null
@@ -1,54 +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.
-
-import os
-import unittest
-
-from webkitpy.common.checkout.changelog_unittest import ChangeLogTest
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.tool.mocktool import MockOptions, MockTool
-from webkitpy.tool.steps.preparechangelog import PrepareChangeLog
-
-
-class PrepareChangeLogTest(ChangeLogTest):
- def test_ensure_bug_url(self):
- capture = OutputCapture()
- step = PrepareChangeLog(MockTool(), MockOptions())
- changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate, self._example_changelog)
- changelog_path = self._write_tmp_file_with_contents(changelog_contents.encode("utf-8"))
- state = {
- "bug_title": "Example title",
- "bug_id": 1234,
- "changelogs": [changelog_path],
- }
- capture.assert_outputs(self, step.run, [state])
- actual_contents = self._read_file_contents(changelog_path, "utf-8")
- expected_message = "Example title\n http://example.com/1234"
- expected_contents = changelog_contents.replace("Need a short description and bug URL (OOPS!)", expected_message)
- os.remove(changelog_path)
- self.assertEquals(actual_contents, expected_contents)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py b/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py
deleted file mode 100644
index 0e78bc2..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py
+++ /dev/null
@@ -1,44 +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.
-
-import os
-
-from webkitpy.common.checkout.changelog import ChangeLog
-from webkitpy.tool.steps.abstractstep import AbstractStep
-
-
-class PrepareChangeLogForRevert(AbstractStep):
- def run(self, state):
- # This could move to prepare-ChangeLog by adding a --revert= option.
- self._run_script("prepare-ChangeLog")
- changelog_paths = self._tool.checkout().modified_changelogs(git_commit=None)
- bug_url = self._tool.bugs.bug_url_for_bug_id(state["bug_id"]) if state["bug_id"] else None
- for changelog_path in changelog_paths:
- # FIXME: Seems we should prepare the message outside of changelogs.py and then just pass in
- # text that we want to use to replace the reviewed by line.
- ChangeLog(changelog_path).update_for_revert(state["revision"], state["reason"], bug_url)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/promptforbugortitle.py b/WebKitTools/Scripts/webkitpy/tool/steps/promptforbugortitle.py
deleted file mode 100644
index 31c913c..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/promptforbugortitle.py
+++ /dev/null
@@ -1,45 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-
-
-class PromptForBugOrTitle(AbstractStep):
- def run(self, state):
- # No need to prompt if we alrady have the bug_id.
- if state.get("bug_id"):
- return
- user_response = self._tool.user.prompt("Please enter a bug number or a title for a new bug:\n")
- # If the user responds with a number, we assume it's bug number.
- # Otherwise we assume it's a bug subject.
- try:
- state["bug_id"] = int(user_response)
- except ValueError, TypeError:
- state["bug_title"] = user_response
- # FIXME: This is kind of a lame description.
- state["bug_description"] = user_response
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/reopenbugafterrollout.py b/WebKitTools/Scripts/webkitpy/tool/steps/reopenbugafterrollout.py
deleted file mode 100644
index f369ca9..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/reopenbugafterrollout.py
+++ /dev/null
@@ -1,44 +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.
-
-from webkitpy.tool.comments import bug_comment_from_commit_text
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.common.system.deprecated_logging import log
-
-
-class ReopenBugAfterRollout(AbstractStep):
- def run(self, state):
- commit_comment = bug_comment_from_commit_text(self._tool.scm(), state["commit_text"])
- comment_text = "Reverted r%s for reason:\n\n%s\n\n%s" % (state["revision"], state["reason"], commit_comment)
-
- bug_id = state["bug_id"]
- if not bug_id:
- log(comment_text)
- log("No bugs were updated.")
- return
- self._tool.bugs.reopen_bug(bug_id, comment_text)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/revertrevision.py b/WebKitTools/Scripts/webkitpy/tool/steps/revertrevision.py
deleted file mode 100644
index bbb794c..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/revertrevision.py
+++ /dev/null
@@ -1,35 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-
-
-class RevertRevision(AbstractStep):
- def run(self, state):
- self._tool.checkout().apply_reverse_diff(state["revision"])
- self.did_modify_checkout(state)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/runtests.py b/WebKitTools/Scripts/webkitpy/tool/steps/runtests.py
deleted file mode 100644
index 282e381..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/runtests.py
+++ /dev/null
@@ -1,81 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.deprecated_logging import log
-
-class RunTests(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.test,
- Options.non_interactive,
- Options.quiet,
- ]
-
- def run(self, state):
- if not self._options.test:
- return
-
- # Run the scripting unit tests first because they're quickest.
- log("Running Python unit tests")
- self._tool.executive.run_and_throw_if_fail(self._tool.port().run_python_unittests_command())
- log("Running Perl unit tests")
- self._tool.executive.run_and_throw_if_fail(self._tool.port().run_perl_unittests_command())
-
- javascriptcore_tests_command = self._tool.port().run_javascriptcore_tests_command()
- if javascriptcore_tests_command:
- log("Running JavaScriptCore tests")
- self._tool.executive.run_and_throw_if_fail(javascriptcore_tests_command, quiet=True)
-
- log("Running run-webkit-tests")
- args = self._tool.port().run_webkit_tests_command()
- if self._options.non_interactive:
- args.append("--no-new-test-results")
- args.append("--no-launch-safari")
- args.append("--exit-after-n-failures=1")
- args.append("--wait-for-httpd")
- # FIXME: Hack to work around https://bugs.webkit.org/show_bug.cgi?id=38912
- # when running the commit-queue on a mac leopard machine since compositing
- # does not work reliably on Leopard due to various graphics driver/system bugs.
- if self._tool.port().name() == "Mac" and self._tool.port().is_leopard():
- tests_to_ignore = []
- tests_to_ignore.append("compositing")
-
- # media tests are also broken on mac leopard due to
- # a separate CoreVideo bug which causes random crashes/hangs
- # https://bugs.webkit.org/show_bug.cgi?id=38912
- tests_to_ignore.append("media")
-
- args.extend(["--ignore-tests", ",".join(tests_to_ignore)])
-
- 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
deleted file mode 100644
index eabb656..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py
+++ /dev/null
@@ -1,92 +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.
-
-import unittest
-
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.common.config.ports import WebKitPort
-from webkitpy.tool.mocktool import MockOptions, MockTool
-from webkitpy.tool.steps.update import Update
-from webkitpy.tool.steps.runtests import RunTests
-from webkitpy.tool.steps.promptforbugortitle import PromptForBugOrTitle
-
-
-class StepsTest(unittest.TestCase):
- def _step_options(self):
- options = MockOptions()
- options.non_interactive = True
- options.port = 'MOCK port'
- options.quiet = True
- options.test = True
- return options
-
- def _run_step(self, step, tool=None, options=None, state=None):
- if not tool:
- tool = MockTool()
- if not options:
- options = self._step_options()
- if not state:
- state = {}
- step(tool, options).run(state)
-
- def test_update_step(self):
- tool = MockTool()
- options = self._step_options()
- options.update = True
- expected_stderr = "Updating working directory\n"
- OutputCapture().assert_outputs(self, self._run_step, [Update, tool, options], expected_stderr=expected_stderr)
-
- def test_prompt_for_bug_or_title_step(self):
- tool = MockTool()
- tool.user.prompt = lambda message: 42
- self._run_step(PromptForBugOrTitle, tool=tool)
-
- def test_runtests_leopard_commit_queue_hack_step(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_command(self):
- mock_options = self._step_options()
- 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
- tool = MockTool(log_executive=True)
- tool.port = lambda: mock_port
- step = RunTests(tool, mock_options)
- 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-new-test-results', '--no-launch-safari', '--exit-after-n-failures=1', '--wait-for-httpd', '--ignore-tests', 'compositing,media', '--quiet']
-"""
- OutputCapture().assert_outputs(self, step.run, [{}], expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers.py b/WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers.py
deleted file mode 100644
index 76bef35..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers.py
+++ /dev/null
@@ -1,51 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-
-
-class SuggestReviewers(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.git_commit,
- Options.suggest_reviewers,
- ]
-
- def run(self, state):
- if not self._options.suggest_reviewers:
- return
-
- reviewers = self._tool.checkout().suggested_reviewers(self._options.git_commit, self._changed_files(state))
- print "The following reviewers have recently modified files in your patch:"
- print "\n".join([reviewer.full_name for reviewer in reviewers])
- if not self._tool.user.confirm("Would you like to CC them?"):
- return
- reviewer_emails = [reviewer.bugzilla_email() for reviewer in reviewers]
- self._tool.bugs.add_cc_to_bug(state['bug_id'], reviewer_emails)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers_unittest.py b/WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers_unittest.py
deleted file mode 100644
index 0c86535..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers_unittest.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# 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
-# 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 unittest
-
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.tool.mocktool import MockOptions, MockTool
-from webkitpy.tool.steps.suggestreviewers import SuggestReviewers
-
-
-class SuggestReviewersTest(unittest.TestCase):
- def test_disabled(self):
- step = SuggestReviewers(MockTool(), MockOptions(suggest_reviewers=False))
- OutputCapture().assert_outputs(self, step.run, [{}])
-
- def test_basic(self):
- capture = OutputCapture()
- step = SuggestReviewers(MockTool(), MockOptions(suggest_reviewers=True, git_commit=None))
- expected_stdout = "The following reviewers have recently modified files in your patch:\nFoo Bar\nWould you like to CC them?\n"
- capture.assert_outputs(self, step.run, [{"bug_id": "123"}], expected_stdout=expected_stdout)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/update.py b/WebKitTools/Scripts/webkitpy/tool/steps/update.py
deleted file mode 100644
index cd1d4d8..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/update.py
+++ /dev/null
@@ -1,45 +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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.deprecated_logging import log
-
-
-class Update(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.update,
- ]
-
- def run(self, state):
- if not self._options.update:
- return
- log("Updating working directory")
- self._tool.executive.run_and_throw_if_fail(self._tool.port().update_webkit_command(), quiet=True)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittest.py b/WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittest.py
deleted file mode 100644
index b475378..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittest.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# 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
-# 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 unittest
-
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.tool.mocktool import MockOptions, MockTool
-from webkitpy.tool.steps.updatechangelogswithreviewer import UpdateChangeLogsWithReviewer
-
-class UpdateChangeLogsWithReviewerTest(unittest.TestCase):
- def test_guess_reviewer_from_bug(self):
- capture = OutputCapture()
- step = UpdateChangeLogsWithReviewer(MockTool(), MockOptions())
- expected_stderr = "0 reviewed patches on bug 75, cannot infer reviewer.\n"
- capture.assert_outputs(self, step._guess_reviewer_from_bug, [75], expected_stderr=expected_stderr)
-
- def test_empty_state(self):
- capture = OutputCapture()
- options = MockOptions()
- options.reviewer = 'MOCK reviewer'
- options.git_commit = 'MOCK git commit'
- step = UpdateChangeLogsWithReviewer(MockTool(), options)
- capture.assert_outputs(self, step.run, [{}])
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py b/WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py
deleted file mode 100644
index e46b790..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py
+++ /dev/null
@@ -1,72 +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.
-
-import os
-
-from webkitpy.common.checkout.changelog import ChangeLog
-from webkitpy.tool.grammar import pluralize
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.deprecated_logging import log, error
-
-class UpdateChangeLogsWithReviewer(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.git_commit,
- Options.reviewer,
- ]
-
- def _guess_reviewer_from_bug(self, bug_id):
- patches = self._tool.bugs.fetch_bug(bug_id).reviewed_patches()
- if len(patches) != 1:
- log("%s on bug %s, cannot infer reviewer." % (pluralize("reviewed patch", len(patches)), bug_id))
- return None
- patch = patches[0]
- log("Guessing \"%s\" as reviewer from attachment %s on bug %s." % (patch.reviewer().full_name, patch.id(), bug_id))
- return patch.reviewer().full_name
-
- def run(self, state):
- bug_id = state.get("bug_id")
- if not bug_id and state.get("patch"):
- bug_id = state.get("patch").bug_id()
-
- reviewer = self._options.reviewer
- if not reviewer:
- if not bug_id:
- log("No bug id provided and --reviewer= not provided. Not updating ChangeLogs with reviewer.")
- return
- reviewer = self._guess_reviewer_from_bug(bug_id)
-
- if not reviewer:
- log("Failed to guess reviewer from bug %s and --reviewer= not provided. Not updating ChangeLogs with reviewer." % bug_id)
- return
-
- os.chdir(self._tool.scm().checkout_root)
- for changelog_path in self.cached_lookup(state, "changelogs"):
- ChangeLog(changelog_path).set_reviewer(reviewer)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer.py b/WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer.py
deleted file mode 100644
index bdf729e..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer.py
+++ /dev/null
@@ -1,71 +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.
-
-import os
-import re
-
-from webkitpy.common.checkout.changelog import ChangeLog
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.deprecated_logging import error, log
-
-
-# FIXME: Some of this logic should probably be unified with CommitterValidator?
-class ValidateReviewer(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.git_commit,
- ]
-
- # FIXME: This should probably move onto ChangeLogEntry
- def _has_valid_reviewer(self, changelog_entry):
- if changelog_entry.reviewer():
- return True
- if re.search("unreviewed", changelog_entry.contents(), re.IGNORECASE):
- return True
- if re.search("rubber[ -]stamp", changelog_entry.contents(), re.IGNORECASE):
- return True
- return False
-
- def run(self, state):
- # FIXME: For now we disable this check when a user is driving the script
- # this check is too draconian (and too poorly tested) to foist upon users.
- if not self._options.non_interactive:
- return
- # FIXME: We should figure out how to handle the current working
- # directory issue more globally.
- os.chdir(self._tool.scm().checkout_root)
- for changelog_path in self.cached_lookup(state, "changelogs"):
- changelog_entry = ChangeLog(changelog_path).latest_entry()
- if self._has_valid_reviewer(changelog_entry):
- continue
- reviewer_text = changelog_entry.reviewer_text()
- if reviewer_text:
- log("%s found in %s does not appear to be a valid reviewer according to committers.py." % (reviewer_text, changelog_path))
- error('%s neither lists a valid reviewer nor contains the string "Unreviewed" or "Rubber stamp" (case insensitive).' % changelog_path)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer_unittest.py b/WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer_unittest.py
deleted file mode 100644
index d9b856a..0000000
--- a/WebKitTools/Scripts/webkitpy/tool/steps/validatereviewer_unittest.py
+++ /dev/null
@@ -1,57 +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.
-
-import unittest
-
-from webkitpy.common.checkout.changelog import ChangeLogEntry
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.tool.mocktool import MockOptions, MockTool
-from webkitpy.tool.steps.validatereviewer import ValidateReviewer
-
-class ValidateReviewerTest(unittest.TestCase):
- _boilerplate_entry = '''2009-08-19 Eric Seidel <eric@webkit.org>
-
- REVIEW_LINE
-
- * Scripts/bugzilla-tool:
-'''
-
- def _test_review_text(self, step, text, expected):
- contents = self._boilerplate_entry.replace("REVIEW_LINE", text)
- entry = ChangeLogEntry(contents)
- self.assertEqual(step._has_valid_reviewer(entry), expected)
-
- def test_has_valid_reviewer(self):
- step = ValidateReviewer(MockTool(), MockOptions())
- self._test_review_text(step, "Reviewed by Eric Seidel.", True)
- self._test_review_text(step, "Reviewed by Eric Seidel", True) # Not picky about the '.'
- self._test_review_text(step, "Reviewed by Eric.", False)
- self._test_review_text(step, "Reviewed by Eric C Seidel.", False)
- self._test_review_text(step, "Rubber-stamped by Eric.", True)
- self._test_review_text(step, "Rubber stamped by Eric.", True)
- self._test_review_text(step, "Unreviewed build fix.", True)