summaryrefslogtreecommitdiff
path: root/chromium/third_party/WebKit/Tools/Scripts/webkitpy
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2014-03-18 13:16:26 +0100
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-03-20 15:55:39 +0100
commit3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch)
tree92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/third_party/WebKit/Tools/Scripts/webkitpy
parente90d7c4b152c56919d963987e2503f9909a666d2 (diff)
downloadqtwebengine-chromium-3f0f86b0caed75241fa71c95a5d73bc0164348c5.tar.gz
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies needed on Windows. Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42 Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu> Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/third_party/WebKit/Tools/Scripts/webkitpy')
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/bindings/main.py201
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/__init__.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baselineoptimizer_unittest.py250
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/checkout.py78
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/checkout_mock.py61
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/checkout_unittest.py59
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/__init__.py3
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/git.py317
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm.py123
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py72
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py1466
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/svn.py211
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/ports.py118
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/watchlist460
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/host.py7
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/host_mock.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/message_pool.py16
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/credentials.py163
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/credentials_unittest.py208
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem.py3
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_mock.py20
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py9
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo.py11
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo_unittest.py3
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/webkit_finder.py9
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/bisect_test_ordering.py170
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/__init__.py1
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader.py97
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_multipart.py172
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_multipart_unittest.py109
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_win.py119
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_win_unittest.py77
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py59
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner_unittest.py30
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py34
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py39
-rwxr-xr-xchromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/layout_tests_mover_unittest.py6
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_results.py3
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py35
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/__init__.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py257
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py100
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py438
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py35
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/builders.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium.py422
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium_port_testcase.py230
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py62
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver.py40
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver_unittest.py9
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux.py47
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py12
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py37
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py17
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py152
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py92
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py179
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py4
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py95
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py72
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py14
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_types.py (renamed from chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/comments.py)34
-rwxr-xr-x[-rw-r--r--]chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_types_unittest.py (renamed from chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/ports_unittest.py)35
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py19
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py64
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/crash_service.py87
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/crash_service_unittest.py81
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/http_server.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/http_server_unittest.py6
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/websocket_server.py9
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py11
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftest.py7
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker.py2
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp.py174
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py160
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/python.py4
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py154
-rwxr-xr-xchromium/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/standalone.py108
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/bot/commitannouncer.py5
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/bot/commitannouncer_unittest.py5
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/__init__.py1
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/commitannouncer.py1
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/download.py61
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/download_unittest.py56
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/flakytests.py1
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/gardenomatic.py1
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queries.py3
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queuestest.py107
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py16
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py17
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaselineserver.py1
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/stepsequence.py7
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/main.py9
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/servers/rebaselineserver_unittest.py8
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/__init__.py14
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/abstractstep.py27
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/addsvnmimetypeforpng.py80
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/addsvnmimetypeforpng_unittest.py58
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/applypatch.py46
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/checkstyle.py67
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py50
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory_unittest.py68
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/commit.py108
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/commit_unittest.py68
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/confirmdiff.py14
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/discardlocalchanges.py52
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/discardlocalchanges_unittest.py97
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/ensurebugisopenandassigned.py41
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/ensurelocalcommitifneeded.py48
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/haslanded.py120
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/haslanded_unittest.py299
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/metastep.py54
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/obsoletepatches.py54
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/postdiff.py52
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/promptforbugortitle.py45
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/reopenbugafterrollout.py47
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/revertrevision.py35
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/runtests.py108
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py61
-rw-r--r--chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py81
125 files changed, 3419 insertions, 6782 deletions
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/bindings/main.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/bindings/main.py
index b1409d27ada..663fd67b1f0 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/bindings/main.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/bindings/main.py
@@ -28,31 +28,36 @@ import tempfile
from webkitpy.common.checkout.scm.detection import detect_scm_system
from webkitpy.common.system.executive import ScriptError
+PASS_MESSAGE = 'All tests PASS!'
+FAIL_MESSAGE = """Some tests FAIL!
+To update the reference files, execute:
+ run-bindings-tests --reset-results
+
+If the failures are not due to your changes, test results may be out of sync;
+please rebaseline them in a separate CL, after checking that tests fail in ToT.
+In CL, please set:
+NOTRY=true
+TBR=(someone in Source/bindings/OWNERS or WATCHLISTS:bindings)
+"""
+
# Python compiler is incomplete; skip IDLs with unimplemented features
SKIP_PYTHON = set([
- 'TestActiveDOMObject.idl',
- 'TestCallback.idl',
'TestCustomAccessors.idl',
- 'TestEvent.idl',
- 'TestEventConstructor.idl',
'TestEventTarget.idl',
'TestException.idl',
- 'TestExtendedEvent.idl',
'TestImplements.idl',
'TestInterface.idl',
'TestInterfaceImplementedAs.idl',
- 'TestMediaQueryListListener.idl',
'TestNamedConstructor.idl',
'TestNode.idl',
'TestObject.idl',
'TestOverloadedConstructors.idl',
'TestPartialInterface.idl',
- 'TestSerializedScriptValueInterface.idl',
- 'TestTypedArray.idl',
'TestTypedefs.idl',
])
input_directory = os.path.join('bindings', 'tests', 'idls')
+support_input_directory = os.path.join('bindings', 'tests', 'idls', 'testing')
reference_directory = os.path.join('bindings', 'tests', 'results')
reference_event_names_filename = os.path.join(reference_directory, 'EventInterfaces.in')
@@ -82,178 +87,198 @@ provider = ScopedTempFileProvider()
class BindingsTests(object):
- def __init__(self, reset_results, test_python, executive):
+ def __init__(self, reset_results, test_python, verbose, executive):
self.reset_results = reset_results
self.test_python = test_python
+ self.verbose = verbose
self.executive = executive
_, self.interface_dependencies_filename = provider.newtempfile()
+ _, self.derived_sources_list_filename = provider.newtempfile()
+ # Generate output into the reference directory if resetting results, or
+ # a temp directory if not.
if reset_results:
- self.event_names_filename = os.path.join(reference_directory, 'EventInterfaces.in')
+ self.output_directory = reference_directory
else:
- _, self.event_names_filename = provider.newtempfile()
+ self.output_directory = provider.newtempdir()
+ self.output_directory_py = provider.newtempdir()
+ self.event_names_filename = os.path.join(self.output_directory, 'EventInterfaces.in')
def run_command(self, cmd):
- return self.executive.run_command(cmd)
+ output = self.executive.run_command(cmd)
+ if output:
+ print output
- def generate_from_idl_pl(self, idl_file, output_directory):
+ def generate_from_idl_pl(self, idl_file):
cmd = ['perl', '-w',
'-Ibindings/scripts',
+ '-Ibuild/scripts',
'-Icore/scripts',
'-I../../JSON/out/lib/perl5',
'bindings/scripts/generate_bindings.pl',
# idl include directories (path relative to generate-bindings.pl)
'--include', '.',
- '--outputDir', output_directory,
+ '--outputDir', self.output_directory,
'--interfaceDependenciesFile', self.interface_dependencies_filename,
- '--idlAttributesFile', 'bindings/scripts/IDLAttributes.txt',
+ '--idlAttributesFile', 'bindings/IDLExtendedAttributes.txt',
idl_file]
try:
- output = self.run_command(cmd)
+ self.run_command(cmd)
except ScriptError, e:
+ print 'ERROR: generate_bindings.pl: ' + os.path.basename(idl_file)
print e.output
return e.exit_code
- if output:
- print output
return 0
- def generate_from_idl_py(self, idl_file, output_directory):
+ def generate_from_idl_py(self, idl_file):
cmd = ['python',
'bindings/scripts/unstable/idl_compiler.py',
- '--output-dir', output_directory,
- '--idl-attributes-file', 'bindings/scripts/IDLAttributes.txt',
+ '--output-dir', self.output_directory_py,
+ '--idl-attributes-file', 'bindings/IDLExtendedAttributes.txt',
'--include', '.',
'--interface-dependencies-file',
self.interface_dependencies_filename,
idl_file]
try:
- output = self.run_command(cmd)
+ self.run_command(cmd)
except ScriptError, e:
+ print 'ERROR: idl_compiler.py: ' + os.path.basename(idl_file)
print e.output
return e.exit_code
- if output:
- print output
return 0
def generate_interface_dependencies(self):
- idl_files_list_file, idl_files_list_filename = provider.newtempfile()
+ idl_files_list_file, main_idl_files_list_filename = provider.newtempfile()
idl_paths = [os.path.join(input_directory, input_file)
for input_file in os.listdir(input_directory)
if input_file.endswith('.idl')]
idl_files_list_contents = ''.join(idl_path + '\n'
for idl_path in idl_paths)
os.write(idl_files_list_file, idl_files_list_contents)
+ support_idl_files_list_file, support_idl_files_list_filename = provider.newtempfile()
+ support_idl_paths = [os.path.join(support_input_directory, input_file)
+ for input_file in os.listdir(support_input_directory)
+ if input_file.endswith('.idl')]
+ support_idl_files_list_contents = ''.join(idl_path + '\n'
+ for idl_path in support_idl_paths)
+ os.write(support_idl_files_list_file, support_idl_files_list_contents)
# Dummy files, required by compute_dependencies but not checked
_, window_constructors_file = provider.newtempfile()
_, workerglobalscope_constructors_file = provider.newtempfile()
_, sharedworkerglobalscope_constructors_file = provider.newtempfile()
_, dedicatedworkerglobalscope_constructors_file = provider.newtempfile()
+ _, serviceworkersglobalscope_constructors_file = provider.newtempfile()
cmd = ['python',
'bindings/scripts/compute_dependencies.py',
- '--idl-files-list', idl_files_list_filename,
+ '--main-idl-files-list', main_idl_files_list_filename,
+ '--support-idl-files-list', support_idl_files_list_filename,
'--interface-dependencies-file', self.interface_dependencies_filename,
+ '--bindings-derived-sources-file', self.derived_sources_list_filename,
'--window-constructors-file', window_constructors_file,
'--workerglobalscope-constructors-file', workerglobalscope_constructors_file,
'--sharedworkerglobalscope-constructors-file', sharedworkerglobalscope_constructors_file,
'--dedicatedworkerglobalscope-constructors-file', dedicatedworkerglobalscope_constructors_file,
+ '--serviceworkerglobalscope-constructors-file', serviceworkersglobalscope_constructors_file,
'--event-names-file', self.event_names_filename,
'--write-file-only-if-changed', '0']
- if self.reset_results:
+ if self.reset_results and self.verbose:
print 'Reset results: EventInterfaces.in'
try:
- output = self.run_command(cmd)
+ self.run_command(cmd)
except ScriptError, e:
+ print 'ERROR: compute_dependencies.py'
print e.output
return e.exit_code
- if output:
- print output
return 0
- def identical_file(self, reference_filename, work_filename):
+ def identical_file(self, reference_filename, output_filename):
+ reference_basename = os.path.basename(reference_filename)
cmd = ['diff',
'-u',
'-N',
reference_filename,
- work_filename]
+ output_filename]
try:
- output = self.run_command(cmd)
+ self.run_command(cmd)
except ScriptError, e:
+ # run_command throws an exception on diff (b/c non-zero exit code)
+ print 'FAIL: %s' % reference_basename
print e.output
return False
- reference_basename = os.path.basename(reference_filename)
- if output:
- print 'FAIL: %s' % reference_basename
- print output
- return False
- print 'PASS: %s' % reference_basename
+ if self.verbose:
+ print 'PASS: %s' % reference_basename
return True
- def identical_output_directory(self, work_directory):
+ def identical_output_files(self, output_directory):
file_pairs = [(os.path.join(reference_directory, output_file),
- os.path.join(work_directory, output_file))
- for output_file in os.listdir(work_directory)
+ os.path.join(output_directory, output_file))
+ for output_file in os.listdir(output_directory)
# FIXME: add option to compiler to not generate tables
if output_file != 'parsetab.py']
- return all([self.identical_file(reference_filename, work_filename)
- for (reference_filename, work_filename) in file_pairs])
+ return all([self.identical_file(reference_filename, output_filename)
+ for (reference_filename, output_filename) in file_pairs])
+
+ def no_excess_files(self):
+ generated_files = set(os.listdir(self.output_directory))
+ generated_files.add('.svn') # Subversion working copy directory
+ excess_files = [output_file
+ for output_file in os.listdir(reference_directory)
+ if output_file not in generated_files]
+ if excess_files:
+ print ('Excess reference files! '
+ '(probably cruft from renaming or deleting):\n' +
+ '\n'.join(excess_files))
+ return False
+ return True
def run_tests(self):
- def generate_and_check_output_pl(idl_filename):
- # Generate output into the reference directory if resetting
- # results, or a temp directory if not.
- if self.reset_results:
- work_directory = reference_directory
- else:
- work_directory = provider.newtempdir()
- idl_path = os.path.join(input_directory, idl_filename)
- if self.generate_from_idl_pl(idl_path, work_directory):
- return False
- if self.reset_results:
- print 'Reset results: %s' % input_file
- return True
- return self.identical_output_directory(work_directory)
+ # Generate output, immediately dying on failure
+ if self.generate_interface_dependencies():
+ return False
- def generate_and_check_output_py(idl_filename):
- if idl_filename in SKIP_PYTHON:
- print 'SKIP: %s' % idl_filename
- return True
- work_directory = provider.newtempdir()
- idl_path = os.path.join(input_directory, idl_filename)
- if self.generate_from_idl_py(idl_path, work_directory):
- return False
- # Detect changes
- return self.identical_output_directory(work_directory)
+ for directory in [input_directory, support_input_directory]:
+ for input_filename in os.listdir(directory):
+ if not input_filename.endswith('.idl'):
+ continue
+ idl_path = os.path.join(directory, input_filename)
+ if self.generate_from_idl_pl(idl_path):
+ return False
+ if self.reset_results and self.verbose:
+ print 'Reset results: %s' % input_filename
+ if not self.test_python:
+ continue
+ if (input_filename in SKIP_PYTHON or
+ directory == support_input_directory):
+ if self.verbose:
+ print 'SKIP: %s' % input_filename
+ continue
+ if self.generate_from_idl_py(idl_path):
+ return False
- if self.reset_results:
- passed = True
- else:
- passed = self.identical_file(reference_event_names_filename,
- self.event_names_filename)
- passed &= all([generate_and_check_output_pl(input_file)
- for input_file in os.listdir(input_directory)
- if input_file.endswith('.idl')])
- print
+ # Detect all changes
+ passed = self.identical_file(reference_event_names_filename,
+ self.event_names_filename)
+ passed &= self.identical_output_files(self.output_directory)
if self.test_python:
- print 'Python:'
- passed &= all([generate_and_check_output_py(input_file)
- for input_file in os.listdir(input_directory)
- if input_file.endswith('.idl')])
+ if self.verbose:
+ print
+ print 'Python:'
+ passed &= self.identical_output_files(self.output_directory_py)
+ passed &= self.no_excess_files()
return passed
def main(self):
current_scm = detect_scm_system(os.curdir)
os.chdir(os.path.join(current_scm.checkout_root, 'Source'))
- if self.generate_interface_dependencies():
- print 'Failed to generate interface dependencies file.'
- return -1
-
all_tests_passed = self.run_tests()
- print
if all_tests_passed:
- print 'All tests PASS!'
+ if self.verbose:
+ print
+ print PASS_MESSAGE
return 0
- print 'Some tests FAIL! (To update the reference files, execute "run-bindings-tests --reset-results")'
+ print
+ print FAIL_MESSAGE
return -1
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/__init__.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/__init__.py
index f385ae4f150..ef65bee5bb7 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/__init__.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/__init__.py
@@ -1,3 +1 @@
# Required for Python to search this directory for module files
-
-from .checkout import Checkout
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baselineoptimizer_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baselineoptimizer_unittest.py
index b6a10eeb273..881d112706c 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baselineoptimizer_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/baselineoptimizer_unittest.py
@@ -26,218 +26,168 @@
# (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 hashlib
-import sys
import webkitpy.thirdparty.unittest2 as unittest
from webkitpy.common.checkout.baselineoptimizer import BaselineOptimizer
-from webkitpy.common.system.filesystem_mock import MockFileSystem
from webkitpy.common.host_mock import MockHost
-
-
-class TestBaselineOptimizer(BaselineOptimizer):
- def __init__(self, mock_results_by_directory, create_mock_files, baseline_name):
- host = MockHost()
- BaselineOptimizer.__init__(self, host, host.port_factory.all_port_names())
- self._mock_results_by_directory = mock_results_by_directory
- self._filesystem = host.filesystem
- self._port_factory = host.port_factory
- self._created_mock_files = create_mock_files
- self._baseline_name = baseline_name
-
- self._create_mock_files(mock_results_by_directory)
-
- # We override this method for testing so we don't have to construct an
- # elaborate mock file system.
- def read_results_by_directory(self, baseline_name):
- if self._created_mock_files:
- return super(TestBaselineOptimizer, self).read_results_by_directory(baseline_name)
- return self._mock_results_by_directory
-
- def _move_baselines(self, baseline_name, results_by_directory, new_results_by_directory):
- self.new_results_by_directory.append(new_results_by_directory)
-
- if self._created_mock_files:
- super(TestBaselineOptimizer, self)._move_baselines(baseline_name, results_by_directory, new_results_by_directory)
- return
- self._mock_results_by_directory = new_results_by_directory
-
- def _create_mock_files(self, results_by_directory):
- root = self._port_factory.get().webkit_base()
- for directory in results_by_directory:
- if 'virtual' in directory:
- virtual_suite = self._port_factory.get().lookup_virtual_suite(self._baseline_name)
- if virtual_suite:
- baseline_name = self._baseline_name[len(virtual_suite.name) + 1:]
- else:
- baseline_name = self._baseline_name
- else:
- baseline_name = self._port_factory.get().lookup_virtual_test_base(self._baseline_name)
- path = self._filesystem.join(root, directory, baseline_name)
- self._filesystem.write_text_file(path, results_by_directory[directory])
+from webkitpy.common.webkit_finder import WebKitFinder
class BaselineOptimizerTest(unittest.TestCase):
- VIRTUAL_DIRECTORY = 'virtual/softwarecompositing'
-
- def _appendVirtualSuffix(self, results_by_directory):
- new_results_by_directory = {}
- for directory in results_by_directory:
- new_results_by_directory[directory + '/' + self.VIRTUAL_DIRECTORY] = results_by_directory[directory]
- return new_results_by_directory
-
- def _assertOneLevelOptimization(self, results_by_directory, expected_new_results_by_directory, baseline_name, create_mock_files=False):
- baseline_optimizer = TestBaselineOptimizer(results_by_directory, create_mock_files, baseline_name)
- self.assertTrue(baseline_optimizer.optimize(baseline_name))
- if type(expected_new_results_by_directory) != list:
- expected_new_results_by_directory = [expected_new_results_by_directory]
- self.assertEqual(baseline_optimizer.new_results_by_directory, expected_new_results_by_directory)
-
- def _assertOptimization(self, results_by_directory, expected_new_results_by_directory):
- baseline_name = 'mock-baseline.png'
- self._assertOneLevelOptimization(results_by_directory, expected_new_results_by_directory, baseline_name)
-
- results_by_directory = self._appendVirtualSuffix(results_by_directory)
- expected_new_results_by_directory = self._appendVirtualSuffix(expected_new_results_by_directory)
- baseline_name = self.VIRTUAL_DIRECTORY + '/' + baseline_name
- self._assertOneLevelOptimization(results_by_directory, [expected_new_results_by_directory, expected_new_results_by_directory], baseline_name)
-
def test_move_baselines(self):
host = MockHost()
- host.filesystem.write_binary_file('/mock-checkout/LayoutTests/platform/win/another/test-expected.txt', 'result A')
- host.filesystem.write_binary_file('/mock-checkout/LayoutTests/platform/mac/another/test-expected.txt', 'result A')
- host.filesystem.write_binary_file('/mock-checkout/LayoutTests/another/test-expected.txt', 'result B')
+ host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/test-expected.txt', 'result A')
+ host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another/test-expected.txt', 'result A')
+ host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt', 'result B')
baseline_optimizer = BaselineOptimizer(host, host.port_factory.all_port_names())
baseline_optimizer._move_baselines('another/test-expected.txt', {
- 'LayoutTests/platform/win': 'aaa',
- 'LayoutTests/platform/mac': 'aaa',
- 'LayoutTests': 'bbb',
+ '/mock-checkout/third_party/WebKit/LayoutTests/platform/win': 'aaa',
+ '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac': 'aaa',
+ '/mock-checkout/third_party/WebKit/LayoutTests': 'bbb',
}, {
- 'LayoutTests': 'aaa',
+ '/mock-checkout/third_party/WebKit/LayoutTests': 'aaa',
})
- self.assertEqual(host.filesystem.read_binary_file('/mock-checkout/LayoutTests/another/test-expected.txt'), 'result A')
+ self.assertEqual(host.filesystem.read_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt'), 'result A')
+
+ def _assertOptimization(self, results_by_directory, expected_new_results_by_directory, baseline_dirname=''):
+ host = MockHost()
+ fs = host.filesystem
+ webkit_base = WebKitFinder(fs).webkit_base()
+ baseline_name = 'mock-baseline-expected.txt'
+
+ for dirname, contents in results_by_directory.items():
+ path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name)
+ fs.write_binary_file(path, contents)
+
+ baseline_optimizer = BaselineOptimizer(host, host.port_factory.all_port_names())
+ self.assertTrue(baseline_optimizer.optimize(fs.join(baseline_dirname, baseline_name)))
+
+ for dirname, contents in expected_new_results_by_directory.items():
+ path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name)
+ if contents is None:
+ self.assertFalse(fs.exists(path))
+ else:
+ self.assertEqual(fs.read_binary_file(path), contents)
+
+ # Check that the files that were in the original set have been deleted where necessary.
+ for dirname in results_by_directory:
+ path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name)
+ if not dirname in expected_new_results_by_directory:
+ self.assertFalse(fs.exists(path))
def test_linux_redundant_with_win(self):
self._assertOptimization({
- 'LayoutTests/platform/win': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- 'LayoutTests/platform/linux': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
+ 'platform/win': '1',
+ 'platform/linux': '1',
}, {
- 'LayoutTests/platform/win': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
+ 'platform/win': '1',
})
def test_covers_mac_win_linux(self):
self._assertOptimization({
- 'LayoutTests/platform/mac': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- 'LayoutTests/platform/win': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- 'LayoutTests/platform/linux': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
+ 'platform/mac': '1',
+ 'platform/win': '1',
+ 'platform/linux': '1',
+ '': None,
}, {
- 'LayoutTests': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
+ '': '1',
})
def test_overwrites_root(self):
self._assertOptimization({
- 'LayoutTests/platform/mac': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- 'LayoutTests/platform/win': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- 'LayoutTests/platform/linux': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- 'LayoutTests': '1',
+ 'platform/mac': '1',
+ 'platform/win': '1',
+ 'platform/linux': '1',
+ '': '2',
}, {
- 'LayoutTests': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
+ '': '1',
})
def test_no_new_common_directory(self):
self._assertOptimization({
- 'LayoutTests/platform/mac': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- 'LayoutTests/platform/linux': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- 'LayoutTests': '1',
+ 'platform/mac': '1',
+ 'platform/linux': '1',
+ '': '2',
}, {
- 'LayoutTests/platform/mac': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- 'LayoutTests/platform/linux': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- 'LayoutTests': '1',
+ 'platform/mac': '1',
+ 'platform/linux': '1',
+ '': '2',
})
- def test_no_common_directory(self):
- self._assertOptimization({
- 'LayoutTests/platform/mac': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- 'LayoutTests/platform/chromium': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- }, {
- 'LayoutTests/platform/mac': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- 'LayoutTests/platform/chromium': '462d03b9c025db1b0392d7453310dbee5f9a9e74',
- })
-
def test_local_optimization(self):
self._assertOptimization({
- 'LayoutTests/platform/mac': '1',
- 'LayoutTests/platform/linux': '1',
- 'LayoutTests/platform/linux-x86': '1',
+ 'platform/mac': '1',
+ 'platform/linux': '1',
+ 'platform/linux-x86': '1',
}, {
- 'LayoutTests/platform/mac': '1',
- 'LayoutTests/platform/linux': '1',
+ 'platform/mac': '1',
+ 'platform/linux': '1',
})
def test_local_optimization_skipping_a_port_in_the_middle(self):
self._assertOptimization({
- 'LayoutTests/platform/mac-snowleopard': '1',
- 'LayoutTests/platform/win': '1',
- 'LayoutTests/platform/linux-x86': '1',
+ 'platform/mac-snowleopard': '1',
+ 'platform/win': '1',
+ 'platform/linux-x86': '1',
}, {
- 'LayoutTests/platform/mac-snowleopard': '1',
- 'LayoutTests/platform/win': '1',
+ 'platform/mac-snowleopard': '1',
+ 'platform/win': '1',
})
def test_baseline_redundant_with_root(self):
self._assertOptimization({
- 'LayoutTests/platform/mac': '1',
- 'LayoutTests/platform/win': '2',
- 'LayoutTests': '2',
+ 'platform/mac': '1',
+ 'platform/win': '2',
+ '': '2',
}, {
- 'LayoutTests/platform/mac': '1',
- 'LayoutTests': '2',
+ 'platform/mac': '1',
+ '': '2',
})
def test_root_baseline_unused(self):
self._assertOptimization({
- 'LayoutTests/platform/mac': '1',
- 'LayoutTests/platform/win': '2',
- 'LayoutTests': '3',
+ 'platform/mac': '1',
+ 'platform/win': '2',
+ '': '3',
}, {
- 'LayoutTests/platform/mac': '1',
- 'LayoutTests/platform/win': '2',
+ 'platform/mac': '1',
+ 'platform/win': '2',
})
def test_root_baseline_unused_and_non_existant(self):
self._assertOptimization({
- 'LayoutTests/platform/mac': '1',
- 'LayoutTests/platform/win': '2',
+ 'platform/mac': '1',
+ 'platform/win': '2',
}, {
- 'LayoutTests/platform/mac': '1',
- 'LayoutTests/platform/win': '2',
+ 'platform/mac': '1',
+ 'platform/win': '2',
})
def test_virtual_root_redundant_with_actual_root(self):
- baseline_name = self.VIRTUAL_DIRECTORY + '/mock-baseline.png'
- hash_of_two = hashlib.sha1('2').hexdigest()
- expected_result = [{'LayoutTests/virtual/softwarecompositing': hash_of_two}, {'LayoutTests': hash_of_two}]
- self._assertOneLevelOptimization({
- 'LayoutTests/' + self.VIRTUAL_DIRECTORY: '2',
- 'LayoutTests': '2',
- }, expected_result, baseline_name, create_mock_files=True)
+ self._assertOptimization({
+ 'virtual/softwarecompositing': '2',
+ 'compositing': '2',
+ }, {
+ 'virtual/softwarecompositing': None,
+ 'compositing': '2',
+ }, baseline_dirname='virtual/softwarecompositing')
def test_virtual_root_redundant_with_ancestors(self):
- baseline_name = self.VIRTUAL_DIRECTORY + '/mock-baseline.png'
- hash_of_two = hashlib.sha1('2').hexdigest()
- expected_result = [{'LayoutTests/virtual/softwarecompositing': hash_of_two}, {'LayoutTests': hash_of_two}]
- self._assertOneLevelOptimization({
- 'LayoutTests/' + self.VIRTUAL_DIRECTORY: '2',
- 'LayoutTests/platform/mac': '2',
- 'LayoutTests/platform/win': '2',
- }, expected_result, baseline_name, create_mock_files=True)
+ self._assertOptimization({
+ 'virtual/softwarecompositing': '2',
+ 'platform/mac/compositing': '2',
+ 'platform/win/compositing': '2',
+ }, {
+ 'virtual/softwarecompositing': None,
+ 'compositing': '2',
+ }, baseline_dirname='virtual/softwarecompositing')
def test_virtual_root_not_redundant_with_ancestors(self):
- baseline_name = self.VIRTUAL_DIRECTORY + '/mock-baseline.png'
- hash_of_two = hashlib.sha1('2').hexdigest()
- expected_result = [{'LayoutTests/virtual/softwarecompositing': hash_of_two}, {'LayoutTests/platform/mac': hash_of_two}]
- self._assertOneLevelOptimization({
- 'LayoutTests/' + self.VIRTUAL_DIRECTORY: '2',
- 'LayoutTests/platform/mac': '2',
- }, expected_result, baseline_name, create_mock_files=True)
+ self._assertOptimization({
+ 'virtual/softwarecompositing': '2',
+ 'platform/mac/compositing': '1',
+ }, {
+ 'virtual/softwarecompositing': '2',
+ 'platform/mac/compositing': '1',
+ }, baseline_dirname='virtual/softwarecompositing')
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/checkout.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/checkout.py
deleted file mode 100644
index 6b640d0d3c4..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/checkout.py
+++ /dev/null
@@ -1,78 +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
-
-from webkitpy.common.config import urls
-from webkitpy.common.checkout.scm import CommitMessage
-from webkitpy.common.checkout.deps import DEPS
-from webkitpy.common.memoized import memoized
-from webkitpy.common.system.executive import ScriptError
-
-
-# This class represents the WebKit-specific parts of the checkout.
-# NOTE: All paths returned from this class should be absolute.
-class Checkout(object):
- def __init__(self, scm, executive=None, filesystem=None):
- self._scm = scm
- # FIXME: We shouldn't be grabbing at private members on scm.
- self._executive = executive or self._scm._executive
- self._filesystem = filesystem or self._scm._filesystem
-
- 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)
- return filter(predicate, map(self._scm.absolute_path, changed_files))
-
- 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 apply_patch(self, patch):
- # 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.
- # --force (continue after errors) is the common case, so we always use it.
- args = [self._scm.script_path('svn-apply'), "--force"]
- if patch.reviewer():
- args += ['--reviewer', patch.reviewer().full_name]
- self._executive.run_command(args, input=patch.contents(), cwd=self._scm.checkout_root)
-
- def apply_reverse_diff(self, revision):
- self._scm.apply_reverse_diff(revision)
-
- 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)))
-
- def apply_reverse_diffs(self, revision_list):
- for revision in sorted(revision_list, reverse=True):
- self.apply_reverse_diff(revision)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/checkout_mock.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/checkout_mock.py
deleted file mode 100644
index ca76d57393c..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/checkout_mock.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright (C) 2011 Google Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (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.filesystem_mock import MockFileSystem
-
-
-class MockCommitMessage(object):
- def message(self):
- return "This is a fake commit message that is at least 50 characters."
-
-
-class MockCheckout(object):
- def __init__(self):
- # FIXME: It's unclear if a MockCheckout is very useful. A normal Checkout
- # with a MockSCM/MockFileSystem/MockExecutive is probably better.
- self._filesystem = MockFileSystem()
-
- def is_path_to_changelog(self, path):
- return self._filesystem.basename(path) == "ChangeLog"
-
- 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):
- return MockCommitMessage()
-
- def apply_patch(self, patch):
- pass
-
- def apply_reverse_diffs(self, revision):
- pass
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/checkout_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/checkout_unittest.py
deleted file mode 100644
index fb8b108ba43..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/checkout_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 codecs
-import os
-import shutil
-import tempfile
-import webkitpy.thirdparty.unittest2 as unittest
-
-from .checkout import Checkout
-from .scm import SCMDetector
-from .scm.scm_mock import MockSCM
-from webkitpy.common.webkit_finder import WebKitFinder
-from webkitpy.common.system.executive import Executive, ScriptError
-from webkitpy.common.system.filesystem import FileSystem # FIXME: This should not be needed.
-from webkitpy.common.system.filesystem_mock import MockFileSystem
-from webkitpy.common.system.executive_mock import MockExecutive
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.thirdparty.mock import Mock
-
-
-class CheckoutTest(unittest.TestCase):
- def _make_checkout(self):
- return Checkout(scm=MockSCM(), filesystem=MockFileSystem(), executive=MockExecutive())
-
- def test_apply_patch(self):
- checkout = self._make_checkout()
- checkout._executive = MockExecutive(should_log=True)
- checkout._scm.script_path = lambda script: script
- mock_patch = Mock()
- mock_patch.contents = lambda: "foo"
- mock_patch.reviewer = lambda: None
- expected_logs = "MOCK run_command: ['svn-apply', '--force'], cwd=/mock-checkout, input=foo\n"
- OutputCapture().assert_outputs(self, checkout.apply_patch, [mock_patch], expected_logs=expected_logs)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/__init__.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/__init__.py
index f691f58e17b..9a2810c55e0 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/__init__.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/__init__.py
@@ -1,8 +1,7 @@
# Required for Python to search this directory for module files
# We only export public API here.
-from .commitmessage import CommitMessage
from .detection import SCMDetector
from .git import Git, AmbiguousCommitError
-from .scm import SCM, AuthenticationError, CheckoutNeedsUpdate
+from .scm import SCM
from .svn import SVN
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/git.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/git.py
index c69a09888d7..30f1fb23c92 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/git.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/git.py
@@ -32,13 +32,10 @@ import logging
import os
import re
+from webkitpy.common.checkout.scm.scm import SCM
from webkitpy.common.memoized import memoized
from webkitpy.common.system.executive import Executive, ScriptError
-from .commitmessage import CommitMessage
-from .scm import AuthenticationError, SCM, commit_error_handler
-from .svn import SVN, SVNRepository
-
_log = logging.getLogger(__name__)
@@ -50,7 +47,7 @@ class AmbiguousCommitError(Exception):
self.has_working_directory_changes = has_working_directory_changes
-class Git(SCM, SVNRepository):
+class Git(SCM):
# Git doesn't appear to document error codes, but seems to return
# 1 or 128, mostly.
@@ -60,45 +57,13 @@ class Git(SCM, SVNRepository):
def __init__(self, cwd, **kwargs):
SCM.__init__(self, cwd, **kwargs)
- self._check_git_architecture()
-
- def _machine_is_64bit(self):
- import platform
- # This only is tested on Mac.
- if not platform.mac_ver()[0]:
- return False
-
- # platform.architecture()[0] can be '64bit' even if the machine is 32bit:
- # http://mail.python.org/pipermail/pythonmac-sig/2009-September/021648.html
- # Use the sysctl command to find out what the processor actually supports.
- return self.run(['sysctl', '-n', 'hw.cpu64bit_capable']).rstrip() == '1'
-
- def _executable_is_64bit(self, path):
- # Again, platform.architecture() fails us. On my machine
- # git_bits = platform.architecture(executable=git_path, bits='default')[0]
- # git_bits is just 'default', meaning the call failed.
- file_output = self.run(['file', path])
- return re.search('x86_64', file_output)
-
- def _check_git_architecture(self):
- if not self._machine_is_64bit():
- return
-
- # We could path-search entirely in python or with
- # which.py (http://code.google.com/p/which), but this is easier:
- git_path = self.run(['which', self.executable_name]).rstrip()
- if self._executable_is_64bit(git_path):
- return
-
- webkit_dev_thread_url = "https://lists.webkit.org/pipermail/webkit-dev/2010-December/015287.html"
- _log.warning("This machine is 64-bit, but the git binary (%s) does not support 64-bit.\nInstall a 64-bit git for better performance, see:\n%s\n" % (git_path, webkit_dev_thread_url))
def _run_git(self, command_args, **kwargs):
full_command_args = [self.executable_name] + command_args
full_kwargs = kwargs
if not 'cwd' in full_kwargs:
full_kwargs['cwd'] = self.checkout_root
- return self.run(full_command_args, **full_kwargs)
+ return self._run(full_command_args, **full_kwargs)
@classmethod
def in_working_directory(cls, path, executive=None):
@@ -116,12 +81,6 @@ class Git(SCM, SVNRepository):
checkout_root = self._filesystem.join(path, checkout_root)
return checkout_root
- def to_object_name(self, filepath):
- # FIXME: This can't be the right way to append a slash.
- root_end_with_slash = self._filesystem.join(self.find_checkout_root(self._filesystem.dirname(filepath)), '')
- # FIXME: This seems to want some sort of rel_path instead?
- return filepath.replace(root_end_with_slash, '')
-
@classmethod
def read_git_config(cls, key, cwd=None, executive=None):
# FIXME: This should probably use cwd=self.checkout_root.
@@ -131,27 +90,23 @@ class Git(SCM, SVNRepository):
executive = executive or Executive()
return executive.run_command([cls.executable_name, "config", "--get-all", key], error_handler=Executive.ignore_error, cwd=cwd).rstrip('\n')
- @staticmethod
- def commit_success_regexp():
- return "^Committed r(?P<svn_revision>\d+)$"
-
- def discard_local_commits(self):
- self._run_git(['reset', '--hard', self.remote_branch_ref()])
+ def _discard_local_commits(self):
+ self._run_git(['reset', '--hard', self._remote_branch_ref()])
- def local_commits(self, ref='HEAD'):
- return self._run_git(['log', '--pretty=oneline', ref + '...' + self.remote_branch_ref()]).splitlines()
+ def _local_commits(self, ref='HEAD'):
+ return self._run_git(['log', '--pretty=oneline', ref + '...' + self._remote_branch_ref()]).splitlines()
- def rebase_in_progress(self):
+ def _rebase_in_progress(self):
return self._filesystem.exists(self.absolute_path(self._filesystem.join('.git', 'rebase-apply')))
def has_working_directory_changes(self):
return self._run_git(['diff', 'HEAD', '--no-renames', '--name-only']) != ""
- def discard_working_directory_changes(self):
+ def _discard_working_directory_changes(self):
# Could run git clean here too, but that wouldn't match subversion
self._run_git(['reset', 'HEAD', '--hard'])
# Aborting rebase even though this does not match subversion
- if self.rebase_in_progress():
+ if self._rebase_in_progress():
self._run_git(['rebase', '--abort'])
def status_command(self):
@@ -185,7 +140,7 @@ class Git(SCM, SVNRepository):
current_branch = self.current_branch()
return self._branch_from_ref(self.read_git_config('branch.%s.merge' % current_branch, cwd=self.checkout_root, executive=self._executive).strip())
- def merge_base(self, git_commit):
+ def _merge_base(self, git_commit=None):
if git_commit:
# Rewrite UPSTREAM to the upstream branch
if 'UPSTREAM' in git_commit:
@@ -202,45 +157,20 @@ class Git(SCM, SVNRepository):
git_commit = git_commit + "^.." + git_commit
return git_commit
- return self.remote_merge_base()
+ return self._remote_merge_base()
def changed_files(self, git_commit=None):
# FIXME: --diff-filter could be used to avoid the "extract_filenames" step.
- status_command = [self.executable_name, 'diff', '-r', '--name-status', "--no-renames", "--no-ext-diff", "--full-index", self.merge_base(git_commit)]
+ status_command = [self.executable_name, 'diff', '-r', '--name-status', "--no-renames", "--no-ext-diff", "--full-index", self._merge_base(git_commit)]
# FIXME: I'm not sure we're returning the same set of files that SVN.changed_files is.
# Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R)
- 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):
- # raise a script error if path does not exists to match the behavior of the svn implementation.
- if not self._filesystem.exists(path):
- raise ScriptError(message="Path %s does not exist." % path)
-
- # 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))
+ return self._run_status_and_extract_filenames(status_command, self._status_regexp("ADM"))
- 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 = [self.executable_name, 'diff', '--name-status', '--no-renames', '--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 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"))
+ def _deleted_files(self):
+ return self._run_status_and_extract_filenames(self.status_command(), self._status_regexp("D"))
@staticmethod
def supports_local_commits():
@@ -276,8 +206,8 @@ class Git(SCM, SVNRepository):
time_without_timezone = time_with_timezone - datetime.timedelta(hours=sign * int(match.group(8)), minutes=int(match.group(9)))
return time_without_timezone.strftime('%Y-%m-%dT%H:%M:%SZ')
- def prepend_svn_revision(self, diff):
- revision = self.head_svn_revision()
+ def _prepend_svn_revision(self, diff):
+ revision = self._head_svn_revision()
if not revision:
return diff
@@ -296,164 +226,28 @@ class Git(SCM, SVNRepository):
if self._filesystem.exists(order_file):
order = "-O%s" % order_file
- command = [self.executable_name, 'diff', '--binary', '--no-color', "--no-ext-diff", "--full-index", "--no-renames", order, self.merge_base(git_commit), "--"]
+ command = [self.executable_name, 'diff', '--binary', '--no-color', "--no-ext-diff", "--full-index", "--no-renames", order, self._merge_base(git_commit), "--"]
if changed_files:
command += changed_files
- return self.prepend_svn_revision(self.run(command, decode_output=False, cwd=self.checkout_root))
+ return self._prepend_svn_revision(self._run(command, decode_output=False, cwd=self.checkout_root))
- def _run_git_svn_find_rev(self, arg):
+ @memoized
+ def svn_revision_from_git_commit(self, git_commit):
# git svn find-rev always exits 0, even when the revision or commit is not found.
- return self._run_git(['svn', 'find-rev', arg]).rstrip()
-
- def _string_to_int_or_none(self, string):
try:
- return int(string)
+ return int(self._run_git(['svn', 'find-rev', git_commit]).rstrip())
except ValueError, e:
return None
- @memoized
- def git_commit_from_svn_revision(self, svn_revision):
- # FIXME: https://bugs.webkit.org/show_bug.cgi?id=111668
- # We should change this to run git log --grep 'git-svn-id' instead
- # so that we don't require git+svn to be set up.
- 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', '--no-renames', '--', 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, has_working_directory_changes):
- squash = self.read_git_config('webkit-patch.commit-should-always-squash', cwd=self.checkout_root, executive=self._executive)
- 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 has_working_directory_changes):
- raise AmbiguousCommitError(num_local_commits, has_working_directory_changes)
-
- def commit_with_message(self, message, username=None, password=None, git_commit=None, force_squash=False, changed_files=None):
- # Username is ignored during Git commits.
- has_working_directory_changes = self.has_working_directory_changes()
-
- if git_commit:
- # Special-case HEAD.. to mean working-copy changes only.
- if git_commit.upper() == 'HEAD..':
- if not has_working_directory_changes:
- 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', username=username, password=password)
-
- # Need working directory changes to be committed so we can checkout the merge branch.
- if has_working_directory_changes:
- # 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, username=username, password=password)
-
- if not force_squash:
- self._assert_can_squash(has_working_directory_changes)
- self._run_git(['reset', '--soft', self.remote_merge_base()])
- self.commit_locally_with_message(message)
- return self.push_local_commits_to_server(username=username, password=password)
-
def checkout_branch(self, name):
self._run_git(['checkout', '-q', name])
def create_clean_branch(self, name):
- self._run_git(['checkout', '-q', '-b', name, self.remote_branch_ref()])
-
- def _commit_on_branch(self, message, git_commit, username=None, password=None):
- branch_name = self.current_branch()
- 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.
- # FIXME: We should never be using chdir! We can instead pass cwd= to run_command/self.run!
- self._filesystem.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.create_clean_branch(MERGE_BRANCH_NAME)
-
- for commit in commit_ids:
- # We're on a different branch now, so convert "head" to the branch name.
- commit = re.sub(r'(?i)head', branch_name, commit)
- # FIXME: Once changed_files and create_patch are modified to separately handle each
- # commit in a commit range, commit each cherry pick so they'll get dcommitted separately.
- self._run_git(['cherry-pick', '--no-commit', commit])
-
- self._run_git(['commit', '-m', message])
- output = self.push_local_commits_to_server(username=username, password=password)
- except Exception, e:
- _log.warning("COMMIT FAILED: " + str(e))
- output = "Commit failed."
- commit_succeeded = False
- finally:
- # And then swap back to the original branch and clean up.
- self.discard_working_directory_changes()
- self.checkout_branch(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'])
+ self._run_git(['checkout', '-q', '-b', name, self._remote_branch_ref()])
def blame(self, path):
return self._run_git(['blame', path])
- def svn_blame(self, path):
- return self._run_git(['svn', 'blame', path])
-
# 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
@@ -462,10 +256,10 @@ class Git(SCM, SVNRepository):
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_merge_base(self):
+ return self._run_git(['merge-base', self._remote_branch_ref(), 'HEAD']).strip()
- def remote_branch_ref(self):
+ 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 = self.read_git_config('svn-remote.svn.fetch', cwd=self.checkout_root, executive=self._executive)
if not remote_branch_refs:
@@ -485,49 +279,6 @@ class Git(SCM, SVNRepository):
command.insert(1, '--all')
self._run_git(command, input=message)
- def push_local_commits_to_server(self, username=None, password=None):
- dcommit_command = ['svn', 'dcommit']
- if (not username or not password) and not self.has_authorization_for_realm(self.svn_server_realm):
- raise AuthenticationError(self.svn_server_host, prompt_for_password=True)
- if username:
- dcommit_command.extend(["--username", username])
- output = self._run_git(dcommit_command, error_handler=commit_error_handler, input=password)
- 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-renames', '--no-commit-id', commit_id])
-
# These methods are git specific and are meant to provide support for the Git oriented workflow
# that Blink is moving towards, hence there are no equivalent methods in the SVN class.
@@ -552,18 +303,18 @@ class Git(SCM, SVNRepository):
if not match:
raise ScriptError(message="Unable to find local branch tracking origin/master.")
branch = str(match.group("branch_name"))
- return self._run_git(['rev-parse', '--symbolic-full-name', branch]).strip()
+ return self._branch_from_ref(self._run_git(['rev-parse', '--symbolic-full-name', branch]).strip())
def is_cleanly_tracking_remote_master(self):
if self.has_working_directory_changes():
return False
if self.current_branch() != self._branch_tracking_remote_master():
return False
- if len(self.local_commits(self._branch_tracking_remote_master())) > 0:
+ if len(self._local_commits(self._branch_tracking_remote_master())) > 0:
return False
return True
def ensure_cleanly_tracking_remote_master(self):
- self.discard_working_directory_changes()
- self._run_git(['checkout', '-q', self._branch_tracking_remote_master().replace('refs/heads/', '', 1)])
- self.discard_local_commits()
+ self._discard_working_directory_changes()
+ self._run_git(['checkout', '-q', self._branch_tracking_remote_master()])
+ self._discard_local_commits()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm.py
index c57aec9fa69..daa477ea7af 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm.py
@@ -39,25 +39,6 @@ from webkitpy.common.system.filesystem import FileSystem
_log = logging.getLogger(__name__)
-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)
-
-
-# FIXME: Should be moved onto SCM
-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, prompt_for_password=False):
- self.server_host = server_host
- self.prompt_for_password = prompt_for_password
-
-
-
# SCM methods are expected to return paths relative to self.checkout_root.
class SCM:
def __init__(self, cwd, executive=None, filesystem=None):
@@ -67,7 +48,7 @@ class SCM:
self.checkout_root = self.find_checkout_root(self.cwd)
# 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):
+ 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.
return self._executive.run_command(args,
cwd=cwd,
@@ -82,18 +63,10 @@ class SCM:
def absolute_path(self, repository_relative_path):
return self._filesystem.join(self.checkout_root, repository_relative_path)
- # FIXME: This belongs in Checkout, not SCM.
- def scripts_directory(self):
- return self._filesystem.join(self.checkout_root, "Tools", "Scripts")
-
- # FIXME: This belongs in Checkout, not SCM.
- def script_path(self, script_name):
- return self._filesystem.join(self.scripts_directory(), script_name)
-
- def run_status_and_extract_filenames(self, status_command, status_regexp):
+ 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():
+ for line in self._run(status_command, cwd=self.checkout_root).splitlines():
match = re.search(status_regexp, line)
if not match:
continue
@@ -102,16 +75,6 @@ class SCM:
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")
@@ -123,13 +86,6 @@ class SCM:
def find_checkout_root(self, path):
SCM._subclass_must_implement()
- @staticmethod
- def commit_success_regexp():
- SCM._subclass_must_implement()
-
- def status_command(self):
- self._subclass_must_implement()
-
def add(self, path, return_exit_code=False):
self.add_list([path], return_exit_code)
@@ -151,22 +107,16 @@ class SCM:
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):
+ def _added_files(self):
self._subclass_must_implement()
- def added_files(self):
- self._subclass_must_implement()
-
- def conflicted_files(self):
+ def _deleted_files(self):
self._subclass_must_implement()
def display_name(self):
self._subclass_must_implement()
- def head_svn_revision(self):
+ def _head_svn_revision(self):
return self.svn_revision(self.checkout_root)
def svn_revision(self, path):
@@ -176,51 +126,12 @@ class SCM:
def timestamp_of_revision(self, path, revision):
self._subclass_must_implement()
- def create_patch(self, git_commit=None, changed_files=None):
- 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, password=None, git_commit=None, force_squash=False, changed_files=None):
- 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()
-
def blame(self, path):
self._subclass_must_implement()
- def svn_blame(self, path):
- self._subclass_must_implement()
-
def has_working_directory_changes(self):
self._subclass_must_implement()
- def discard_working_directory_changes(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.
@@ -228,28 +139,6 @@ class SCM:
def supports_local_commits():
SCM._subclass_must_implement()
- def local_commits(self):
- return []
-
- def has_local_commits(self):
- return len(self.local_commits()) > 0
-
- def discard_local_commits(self):
- return
-
- def remote_merge_base(self):
- SCM._subclass_must_implement()
-
def commit_locally_with_message(self, message, commit_all_working_directory_changes=True):
_log.error("Your source control manager does not support local commits.")
sys.exit(1)
-
- def local_changes_exist(self):
- return (self.supports_local_commits() and self.has_local_commits()) or self.has_working_directory_changes()
-
- def discard_local_changes(self):
- if self.has_working_directory_changes():
- self.discard_working_directory_changes()
-
- if self.has_local_commits():
- self.discard_local_commits()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py
index 192c03c905d..d07cd700e7b 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm_mock.py
@@ -26,7 +26,6 @@
# (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.scm import CommitMessage
from webkitpy.common.system.filesystem_mock import MockFileSystem
from webkitpy.common.system.executive_mock import MockExecutive
@@ -35,7 +34,7 @@ class MockSCM(object):
executable_name = "MockSCM"
def __init__(self, filesystem=None, executive=None):
- self.checkout_root = "/mock-checkout"
+ self.checkout_root = "/mock-checkout/third_party/WebKit"
self.added_paths = set()
self._filesystem = filesystem or MockFileSystem()
self._executive = executive or MockExecutive()
@@ -51,9 +50,6 @@ class MockSCM(object):
def has_working_directory_changes(self):
return False
- def discard_working_directory_changes(self):
- pass
-
def ensure_cleanly_tracking_remote_master(self):
pass
@@ -72,15 +68,6 @@ class MockSCM(object):
def supports_local_commits(self):
return True
- def has_local_commits(self):
- return False
-
- def discard_local_commits(self):
- pass
-
- def discard_local_changes(self):
- pass
-
def exists(self, path):
# TestRealMain.test_real_main (and several other rebaseline tests) are sensitive to this return value.
# We should make those tests more robust, but for now we just return True always (since no test needs otherwise).
@@ -89,60 +76,9 @@ class MockSCM(object):
def absolute_path(self, *comps):
return self._filesystem.join(self.checkout_root, *comps)
- def changed_files(self, git_commit=None):
- return ["MockFile1"]
-
- def changed_files_for_revision(self, revision):
- return ["MockFile1"]
-
- def head_svn_revision(self):
- return '1234'
-
def svn_revision(self, path):
return '5678'
- def timestamp_of_revision(self, path, revision):
- return '2013-02-01 08:48:05 +0000'
-
- def create_patch(self, git_commit, changed_files=None):
- return "Patch1"
-
- def commit_ids_from_commitish_arguments(self, args):
- return ["Commitish1", "Commitish2"]
-
- def committer_email_for_revision(self, revision):
- return "mock@webkit.org"
-
- def commit_locally_with_message(self, message, commit_all_working_directory_changes=True):
- pass
-
- def commit_with_message(self, message, username=None, password=None, git_commit=None, force_squash=False, changed_files=None):
- pass
-
- def merge_base(self, git_commit):
- return None
-
- 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=50000\n")
- if commit_id == "Commitish2":
- return CommitMessage("CommitMessage2\n" \
- "https://bugs.example.org/show_bug.cgi?id=50001\n")
- raise Exception("Bogus commit_id in commit_message_for_local_commit.")
-
- def diff_for_file(self, path, log=None):
- return path + '-diff'
-
- def diff_for_revision(self, revision):
- return "DiffForRevision%s\nhttp://bugs.webkit.org/show_bug.cgi?id=12345" % revision
-
- def show_head(self, path):
- return path
-
- def svn_revision_from_commit_text(self, commit_text):
- return "49824"
-
def svn_revision_from_git_commit(self, git_commit):
if git_commit == '6469e754a1':
return 1234
@@ -152,6 +88,12 @@ class MockSCM(object):
return 10000
return None
+ def timestamp_of_revision(self, path, revision):
+ return '2013-02-01 08:48:05 +0000'
+
+ def commit_locally_with_message(self, message, commit_all_working_directory_changes=True):
+ pass
+
def delete(self, path):
return self.delete_list([path])
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py
index a9dabcce412..2b172ecff8d 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py
@@ -29,464 +29,207 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import atexit
-import base64
-import codecs
-import getpass
import os
-import os.path
-import re
-import stat
-import sys
-import subprocess
-import tempfile
-import time
-import webkitpy.thirdparty.unittest2 as unittest
-import urllib
import shutil
-from datetime import date
-from webkitpy.common.checkout.checkout import Checkout
from webkitpy.common.system.executive import Executive, ScriptError
-from webkitpy.common.system.filesystem_mock import MockFileSystem
-from webkitpy.common.system.outputcapture import OutputCapture
from webkitpy.common.system.executive_mock import MockExecutive
-from .git import Git, AmbiguousCommitError
-from .detection import detect_scm_system
-from .scm import SCM, CheckoutNeedsUpdate, commit_error_handler, AuthenticationError
-from .svn import SVN
+from webkitpy.common.system.filesystem import FileSystem
+from webkitpy.common.system.filesystem_mock import MockFileSystem
+from webkitpy.common.checkout.scm.detection import detect_scm_system
+from webkitpy.common.checkout.scm.git import Git, AmbiguousCommitError
+from webkitpy.common.checkout.scm.scm import SCM
+from webkitpy.common.checkout.scm.svn import SVN
+import webkitpy.thirdparty.unittest2 as unittest
# We cache the mock SVN repo so that we don't create it again for each call to an SVNTest or GitTest test_ method.
# We store it in a global variable so that we can delete this cached repo on exit(3).
-# FIXME: Remove this once we migrate to Python 2.7. Unittest in Python 2.7 supports module-specific setup and teardown functions.
+original_cwd = None
cached_svn_repo_path = None
-
-def remove_dir(path):
- # Change directory to / to ensure that we aren't in the directory we want to delete.
- os.chdir('/')
- shutil.rmtree(path)
-
-
-# FIXME: Remove this once we migrate to Python 2.7. Unittest in Python 2.7 supports module-specific setup and teardown functions.
@atexit.register
-def delete_cached_mock_repo_at_exit():
+def delete_cached_svn_repo_at_exit():
if cached_svn_repo_path:
- remove_dir(cached_svn_repo_path)
-
-# 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.
-
-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)
-
+ os.chdir(original_cwd)
+ shutil.rmtree(cached_svn_repo_path)
-# 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))
+class SCMTestBase(unittest.TestCase):
+ def __init__(self, *args, **kwargs):
+ super(SCMTestBase, self).__init__(*args, **kwargs)
+ self.scm = None
+ self.executive = None
+ self.fs = None
+ self.original_cwd = None
-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 setUp(self):
+ self.executive = Executive()
+ self.fs = FileSystem()
+ self.original_cwd = self.fs.getcwd()
+ def tearDown(self):
+ self._chdir(self.original_cwd)
-def read_from_path(file_path, encoding="utf-8"):
- with codecs.open(file_path, "r", encoding) as file:
- return file.read()
+ def _join(self, *comps):
+ return self.fs.join(*comps)
+ def _chdir(self, path):
+ self.fs.chdir(path)
-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.
- # FIXME: This should use an Executive.
- return run_command([command, "diff"] + list(args), decode_output=False)
+ def _mkdir(self, path):
+ assert not self.fs.exists(path)
+ self.fs.maybe_make_directory(path)
+ def _mkdtemp(self, **kwargs):
+ return str(self.fs.mkdtemp(**kwargs))
-def _svn_diff(*args):
- return _make_diff("svn", *args)
+ def _remove(self, path):
+ self.fs.remove(path)
+ def _rmtree(self, path):
+ self.fs.rmtree(path)
-def _git_diff(*args):
- return _make_diff("git", *args)
+ def _run(self, *args, **kwargs):
+ return self.executive.run_command(*args, **kwargs)
+ def _run_silent(self, args, **kwargs):
+ self.executive.run_and_throw_if_fail(args, quiet=True, **kwargs)
-# Exists to share svn repository creation code between the git and svn tests
-class SVNTestRepository(object):
- @classmethod
- def _svn_add(cls, path):
- run_command(["svn", "add", path])
+ def _write_text_file(self, path, contents):
+ self.fs.write_text_file(path, contents)
- @classmethod
- def _svn_commit(cls, message):
- run_command(["svn", "commit", "--quiet", "--message", message])
+ def _write_binary_file(self, path, contents):
+ self.fs.write_binary_file(path, contents)
- @classmethod
- def _setup_test_commits(cls, svn_repo_url):
+ def _make_diff(self, 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 self._run([command, "diff"] + list(args), decode_output=False)
- svn_checkout_path = tempfile.mkdtemp(suffix="svn_test_checkout")
- run_command(['svn', 'checkout', '--quiet', svn_repo_url, svn_checkout_path])
+ def _svn_diff(self, *args):
+ return self._make_diff("svn", *args)
- # Add some test commits
- os.chdir(svn_checkout_path)
+ def _git_diff(self, *args):
+ return self._make_diff("git", *args)
- 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")
+ def _svn_add(self, path):
+ self._run(["svn", "add", path])
- 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'])
- remove_dir(svn_checkout_path)
+ def _svn_commit(self, message):
+ self._run(["svn", "commit", "--quiet", "--message", message])
# This is a hot function since it's invoked by unittest before calling each test_ method in SVNTest and
# GitTest. We create a mock SVN repo once and then perform an SVN checkout from a filesystem copy of
# it since it's expensive to create the mock repo.
- @classmethod
- def setup(cls, test_object):
+ def _set_up_svn_checkout(self):
global cached_svn_repo_path
+ global original_cwd
if not cached_svn_repo_path:
- cached_svn_repo_path = cls._setup_mock_repo()
-
- test_object.temp_directory = tempfile.mkdtemp(suffix="svn_test")
- test_object.svn_repo_path = os.path.join(test_object.temp_directory, "repo")
- test_object.svn_repo_url = "file://%s" % test_object.svn_repo_path
- test_object.svn_checkout_path = os.path.join(test_object.temp_directory, "checkout")
- shutil.copytree(cached_svn_repo_path, test_object.svn_repo_path)
- run_command(['svn', 'checkout', '--quiet', test_object.svn_repo_url + "/trunk", test_object.svn_checkout_path])
-
- @classmethod
- def _setup_mock_repo(cls):
- # Create an test SVN repository
- svn_repo_path = tempfile.mkdtemp(suffix="svn_test_repo")
+ cached_svn_repo_path = self._set_up_svn_repo()
+ original_cwd = self.original_cwd
+
+ self.temp_directory = self._mkdtemp(suffix="svn_test")
+ self.svn_repo_path = self._join(self.temp_directory, "repo")
+ self.svn_repo_url = "file://%s" % self.svn_repo_path
+ self.svn_checkout_path = self._join(self.temp_directory, "checkout")
+ shutil.copytree(cached_svn_repo_path, self.svn_repo_path)
+ self._run(['svn', 'checkout', '--quiet', self.svn_repo_url + "/trunk", self.svn_checkout_path])
+
+ def _set_up_svn_repo(self):
+ svn_repo_path = self._mkdtemp(suffix="svn_test_repo")
svn_repo_url = "file://%s" % 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', svn_repo_path])
+ self._run(['svnadmin', 'create', '--pre-1.5-compatible', svn_repo_path])
# Create a test svn checkout
- svn_checkout_path = tempfile.mkdtemp(suffix="svn_test_checkout")
- run_command(['svn', 'checkout', '--quiet', svn_repo_url, svn_checkout_path])
+ svn_checkout_path = self._mkdtemp(suffix="svn_test_checkout")
+ self._run(['svn', 'checkout', '--quiet', svn_repo_url, svn_checkout_path])
# Create and checkout a trunk dir to match the standard svn configuration to match git-svn's expectations
- os.chdir(svn_checkout_path)
- os.mkdir('trunk')
- cls._svn_add('trunk')
+ self._chdir(svn_checkout_path)
+ self._mkdir('trunk')
+ self._svn_add('trunk')
# We can add tags and branches as well if we ever need to test those.
- cls._svn_commit('add trunk')
+ self._svn_commit('add trunk')
- # Change directory out of the svn checkout so we can delete the checkout directory.
- remove_dir(svn_checkout_path)
+ self._rmtree(svn_checkout_path)
- cls._setup_test_commits(svn_repo_url + "/trunk")
+ self._set_up_svn_test_commits(svn_repo_url + "/trunk")
return svn_repo_path
- @classmethod
- def tear_down(cls, test_object):
- remove_dir(test_object.temp_directory)
+ def _set_up_svn_test_commits(self, svn_repo_url):
+ svn_checkout_path = self._mkdtemp(suffix="svn_test_checkout")
+ self._run(['svn', 'checkout', '--quiet', svn_repo_url, 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.
- if os.path.isabs(__file__):
- path = os.path.dirname(__file__)
- else:
- path = sys.path[0]
- os.chdir(detect_scm_system(path).checkout_root)
+ # Add some test commits
+ self._chdir(svn_checkout_path)
+ self._write_text_file("test_file", "test1")
+ self._svn_add("test_file")
+ self._svn_commit("initial commit")
-# 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.
+ self._write_text_file("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
+ self._mkdir("test_dir")
+ # Slash should always be the right path separator since we use cygwin on Windows.
+ test_file3_path = "test_dir/test_file3"
+ self._write_text_file(test_file3_path, "third file")
+ self._svn_add("test_dir")
+ self._svn_commit("second commit")
- 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.assertNotEqual(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("Joe Cool", "joe@cool.com")
- 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.assertItemsEqual(self.scm.changed_files(), ["test_file"])
- write_into_file_at_path("test_dir/test_file3", "new stuff")
- self.assertItemsEqual(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.assertItemsEqual(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.assertItemsEqual(self.scm.added_files(), [])
-
- write_into_file_at_path("added_file", "new stuff")
- self.scm.add("added_file")
-
- write_into_file_at_path("added_file3", "more new stuff")
- write_into_file_at_path("added_file4", "more new stuff")
- self.scm.add_list(["added_file3", "added_file4"])
-
- 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.assertItemsEqual(added_files, ["added_dir/added_file2", "added_file", "added_file3", "added_file4"])
-
- # Test also to make sure discard_working_directory_changes removes added files
- self.scm.discard_working_directory_changes()
- self.assertItemsEqual(self.scm.added_files(), [])
- self.assertFalse(os.path.exists("added_file"))
- self.assertFalse(os.path.exists("added_file3"))
- self.assertFalse(os.path.exists("added_file4"))
- 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.assertItemsEqual(changed_files, ["test_dir/test_file3", "test_file"])
- self.assertItemsEqual(self.scm.changed_files_for_revision(4), ["test_file", "test_file2"]) # Git and SVN return different orders.
- self.assertItemsEqual(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.assertItemsEqual(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.assertRegexpMatches(r3_patch, 'test3')
- self.assertNotRegexpMatches(r3_patch, 'test4')
- self.assertRegexpMatches(r3_patch, 'test2')
- self.assertRegexpMatches(self.scm.diff_for_revision(3), 'test2')
-
- 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.assertIn('fizzbuzz7.gif', 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.assertIn('fizzbuzz7.gif', 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.assertNotIn('fizzbuzz7.gif', self.scm.changed_files())
-
- # Cannot delete again.
- self.assertRaises(ScriptError, self.checkout.apply_patch, self._create_patch(git_binary_deletion))
+ self._write_text_file("test_file", "test1test2test3\n")
+ self._write_text_file("test_file2", "second file")
+ self._svn_add("test_file2")
+ self._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.
+ self._write_binary_file("test_file", u"latin1 test: \u00A0\n".encode("latin-1"))
+ self._write_binary_file("test_file2", u"utf-8 test: \u00A0\n".encode("utf-8"))
+ self._svn_commit("fourth commit")
+
+ # svn does not seem to update after commit as I would expect.
+ self._run(['svn', 'update'])
+ self._rmtree(svn_checkout_path)
+
+ def _tear_down_svn_checkout(self):
+ self._rmtree(self.temp_directory)
def _shared_test_add_recursively(self):
- os.mkdir("added_dir")
- write_into_file_at_path("added_dir/added_file", "new stuff")
+ self._mkdir("added_dir")
+ self._write_text_file("added_dir/added_file", "new stuff")
self.scm.add("added_dir/added_file")
- self.assertIn("added_dir/added_file", self.scm.added_files())
+ self.assertIn("added_dir/added_file", self.scm._added_files())
def _shared_test_delete_recursively(self):
- os.mkdir("added_dir")
- write_into_file_at_path("added_dir/added_file", "new stuff")
+ self._mkdir("added_dir")
+ self._write_text_file("added_dir/added_file", "new stuff")
self.scm.add("added_dir/added_file")
- self.assertIn("added_dir/added_file", self.scm.added_files())
+ self.assertIn("added_dir/added_file", self.scm._added_files())
self.scm.delete("added_dir/added_file")
- self.assertNotIn("added_dir", self.scm.added_files())
+ self.assertNotIn("added_dir", self.scm._added_files())
def _shared_test_delete_recursively_or_not(self):
- os.mkdir("added_dir")
- write_into_file_at_path("added_dir/added_file", "new stuff")
- write_into_file_at_path("added_dir/another_added_file", "more new stuff")
+ self._mkdir("added_dir")
+ self._write_text_file("added_dir/added_file", "new stuff")
+ self._write_text_file("added_dir/another_added_file", "more new stuff")
self.scm.add("added_dir/added_file")
self.scm.add("added_dir/another_added_file")
- self.assertIn("added_dir/added_file", self.scm.added_files())
- self.assertIn("added_dir/another_added_file", self.scm.added_files())
+ self.assertIn("added_dir/added_file", self.scm._added_files())
+ self.assertIn("added_dir/another_added_file", self.scm._added_files())
self.scm.delete("added_dir/added_file")
- self.assertIn("added_dir/another_added_file", self.scm.added_files())
+ self.assertIn("added_dir/another_added_file", self.scm._added_files())
def _shared_test_exists(self, scm, commit_function):
- os.chdir(scm.checkout_root)
+ self._chdir(scm.checkout_root)
self.assertFalse(scm.exists('foo.txt'))
- write_into_file_at_path('foo.txt', 'some stuff')
+ self._write_text_file('foo.txt', 'some stuff')
self.assertFalse(scm.exists('foo.txt'))
scm.add('foo.txt')
commit_function('adding foo')
@@ -495,157 +238,33 @@ OcmYex&reD$;sO8*F9L)B
commit_function('deleting foo')
self.assertFalse(scm.exists('foo.txt'))
- def _shared_test_head_svn_revision(self):
- self.assertEqual(self.scm.head_svn_revision(), '5')
-
def _shared_test_move(self):
- write_into_file_at_path('added_file', 'new stuff')
+ self._write_text_file('added_file', 'new stuff')
self.scm.add('added_file')
self.scm.move('added_file', 'moved_file')
- self.assertIn('moved_file', self.scm.added_files())
+ self.assertIn('moved_file', self.scm._added_files())
def _shared_test_move_recursive(self):
- os.mkdir("added_dir")
- write_into_file_at_path('added_dir/added_file', 'new stuff')
- write_into_file_at_path('added_dir/another_added_file', 'more new stuff')
+ self._mkdir("added_dir")
+ self._write_text_file('added_dir/added_file', 'new stuff')
+ self._write_text_file('added_dir/another_added_file', 'more new stuff')
self.scm.add('added_dir')
self.scm.move('added_dir', 'moved_dir')
- self.assertIn('moved_dir/added_file', self.scm.added_files())
- self.assertIn('moved_dir/another_added_file', self.scm.added_files())
-
-
-# Context manager that overrides the current timezone.
-class TimezoneOverride(object):
- def __init__(self, timezone_string):
- self._timezone_string = timezone_string
-
- def __enter__(self):
- if hasattr(time, 'tzset'):
- self._saved_timezone = os.environ.get('TZ', None)
- os.environ['TZ'] = self._timezone_string
- time.tzset()
-
- def __exit__(self, type, value, traceback):
- if hasattr(time, 'tzset'):
- if self._saved_timezone:
- os.environ['TZ'] = self._saved_timezone
- else:
- del os.environ['TZ']
- time.tzset()
-
-
-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 (as in Cupertino, CA, US)
- with TimezoneOverride('PST8PDT'):
- 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>
-%(whitespace)s
-+ Reviewed by NOBODY (OOPS!).
-+
-+ Second most awesome change ever.
-+
-+ * scm_unittest.py:
-+
-+2009-10-26 Eric Seidel <eric@webkit.org>
-+
- Reviewed by Foo Bar.
-%(whitespace)s
- Most awesome change ever.
-""" % {'whitespace': ' '}
- 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 @@
-%(whitespace)s
- Reviewed by Foo Bar.
-%(whitespace)s
-+ Second most awesome change ever.
-+
-+ * scm_unittest.py:
-+
-+2009-10-26 Eric Seidel <eric@webkit.org>
-+
-+ Reviewed by Foo Bar.
-+
- Most awesome change ever.
-%(whitespace)s
- * scm_unittest.py:
-""" % {'whitespace': ' '}
- 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.assertEqual(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.assertEqual(read_from_path('ChangeLog'), expected_changelog_contents)
+ self.assertIn('moved_dir/added_file', self.scm._added_files())
+ self.assertIn('moved_dir/another_added_file', self.scm._added_files())
+
+class SVNTest(SCMTestBase):
def setUp(self):
- SVNTestRepository.setup(self)
- os.chdir(self.svn_checkout_path)
+ super(SVNTest, self).setUp()
+ self._set_up_svn_checkout()
+ self._chdir(self.svn_checkout_path)
self.scm = detect_scm_system(self.svn_checkout_path)
self.scm.svn_server_realm = None
- # For historical reasons, we test some checkout code here too.
- self.checkout = Checkout(self.scm)
def tearDown(self):
- SVNTestRepository.tear_down(self)
+ super(SVNTest, self).tearDown()
+ self._tear_down_svn_checkout()
def test_detect_scm_system_relative_url(self):
scm = detect_scm_system(".")
@@ -653,200 +272,23 @@ class SVNTest(SCMTest):
# 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, 'Tools', '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):
self.assertEqual(self.scm.display_name(), "svn")
self.assertEqual(self.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):
- patch = self._create_patch(_svn_diff("-r5:4"))
- self._setup_webkittools_scripts_symlink(self.scm)
- Checkout(self.scm).apply_patch(patch)
-
- def test_commit_logs(self):
- # Commits have dates and usernames in them, so we can't just direct compare.
- self.assertRegexpMatches(self.scm.last_svn_commit_log(), 'fourth commit')
- self.assertRegexpMatches(self.scm.svn_commit_log(3), 'second commit')
-
- 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')
-
- def test_commit_in_subdir(self, username=None):
- write_into_file_at_path('test_dir/test_file3', 'more test content')
- os.chdir("test_dir")
- commit_text = self.scm.commit_with_message("another test commit", username)
- os.chdir("..")
- self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6')
-
- 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):
- # FIXME: https://bugs.webkit.org/show_bug.cgi?id=111669
- # This test ends up looking in the actal $HOME/.subversion for authorization,
- # which makes it fragile. For now, set it to use a realm that won't be authorized,
- # but we should really plumb through a fake_home_dir here like we do in
- # test_has_authorization_for_realm.
- self.scm.svn_server_realm = '<http://svn.example.com:80> Example'
- self.assertRaises(AuthenticationError, self._shared_test_commit_with_message)
-
- def test_has_authorization_for_realm_using_credentials_with_passtype(self):
- credentials = """
-K 8
-passtype
-V 8
-keychain
-K 15
-svn:realmstring
-V 39
-<http://svn.webkit.org:80> Mac OS Forge
-K 8
-username
-V 17
-dbates@webkit.org
-END
-"""
- self.assertTrue(self._test_has_authorization_for_realm_using_credentials(SVN.svn_server_realm, credentials))
-
- def test_has_authorization_for_realm_using_credentials_with_password(self):
- credentials = """
-K 15
-svn:realmstring
-V 39
-<http://svn.webkit.org:80> Mac OS Forge
-K 8
-username
-V 17
-dbates@webkit.org
-K 8
-password
-V 4
-blah
-END
-"""
- self.assertTrue(self._test_has_authorization_for_realm_using_credentials(SVN.svn_server_realm, credentials))
-
- def _test_has_authorization_for_realm_using_credentials(self, realm, credentials):
- 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, credentials)
- result = self.scm.has_authorization_for_realm(realm, home_directory=fake_home_dir)
- os.remove(fake_webkit_auth_file)
- os.rmdir(svn_config_dir_path)
- os.rmdir(fake_home_dir)
- return result
-
- def test_not_have_authorization_for_realm_with_credentials_missing_password_and_passtype(self):
- credentials = """
-K 15
-svn:realmstring
-V 39
-<http://svn.webkit.org:80> Mac OS Forge
-K 8
-username
-V 17
-dbates@webkit.org
-END
-"""
- self.assertFalse(self._test_has_authorization_for_realm_using_credentials(SVN.svn_server_realm, credentials))
-
- def test_not_have_authorization_for_realm_when_missing_credentials_file(self):
- 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(self.scm.has_authorization_for_realm(SVN.svn_server_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._chdir(self.svn_checkout_path)
self.scm.delete("test_file")
- self.assertIn("test_file", self.scm.deleted_files())
+ self.assertIn("test_file", self.scm._deleted_files())
def test_delete_list(self):
- os.chdir(self.svn_checkout_path)
+ self._chdir(self.svn_checkout_path)
self.scm.delete_list(["test_file", "test_file2"])
- self.assertIn("test_file", self.scm.deleted_files())
- self.assertIn("test_file2", self.scm.deleted_files())
+ self.assertIn("test_file", self.scm._deleted_files())
+ self.assertIn("test_file2", self.scm._deleted_files())
def test_delete_recursively(self):
self._shared_test_delete_recursively()
@@ -854,223 +296,93 @@ END
def test_delete_recursively_or_not(self):
self._shared_test_delete_recursively_or_not()
- def test_head_svn_revision(self):
- self._shared_test_head_svn_revision()
-
def test_move(self):
self._shared_test_move()
def test_move_recursive(self):
self._shared_test_move_recursive()
- 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.assertIn("-some content", diff)
- self.assertIn("+changed content", 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.discard_working_directory_changes()
- self.assertFalse(os.path.exists(svn_root_lock_path))
- run_command(['svn', 'update']) # Should succeed and not raise.
- def test_exists(self):
- self._shared_test_exists(self.scm, self.scm.commit_with_message)
+class GitTest(SCMTestBase):
+ def setUp(self):
+ super(GitTest, self).setUp()
+ self._set_up_git_checkouts()
-class GitTest(SCMTest):
+ def tearDown(self):
+ super(GitTest, self).tearDown()
+ self._tear_down_git_checkouts()
- def setUp(self):
- """Sets up fresh git repository with one commit. Then setups a second git
- repo that tracks the first one."""
- # FIXME: We should instead clone a git repo that is tracking an SVN repo.
- # That better matches what we do with WebKit.
- 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'])
+ def _set_up_git_checkouts(self):
+ """Sets up fresh git repository with one commit. Then sets up a second git repo that tracks the first one."""
+
+ self.untracking_checkout_path = self._mkdtemp(suffix="git_test_checkout2")
+ self._run(['git', 'init', self.untracking_checkout_path])
+
+ self._chdir(self.untracking_checkout_path)
+ self._write_text_file('foo_file', 'foo')
+ self._run(['git', 'add', 'foo_file'])
+ self._run(['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_git_checkout_path = self._mkdtemp(suffix="git_test_checkout")
+ self._run(['git', 'clone', '--quiet', self.untracking_checkout_path, self.tracking_git_checkout_path])
+ self._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 _tear_down_git_checkouts(self):
+ self._run(['rm', '-rf', self.tracking_git_checkout_path])
+ self._run(['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)
+ self.assertEqual(self.tracking_scm._remote_branch_ref(), 'refs/remotes/origin/master')
+ self._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')
+ self._run(['git', 'config', '--add', 'svn-remote.svn.fetch', 'trunk:remote1'])
+ self._run(['git', 'config', '--add', 'svn-remote.svn.fetch', 'trunk:remote2'])
+ self.assertEqual(self.tracking_scm._remote_branch_ref(), 'remote1')
def test_create_patch(self):
- write_into_file_at_path('test_file_commit1', 'contents')
- run_command(['git', 'add', 'test_file_commit1'])
+ self._write_text_file('test_file_commit1', 'contents')
+ self._run(['git', 'add', 'test_file_commit1'])
scm = self.tracking_scm
scm.commit_locally_with_message('message')
patch = scm.create_patch()
self.assertNotRegexpMatches(patch, r'Subversion Revision:')
- def test_orderfile(self):
- os.mkdir("Tools")
- os.mkdir("Source")
- os.mkdir("LayoutTests")
- os.mkdir("Websites")
-
- # Slash should always be the right path separator since we use cygwin on Windows.
- Tools_ChangeLog = "Tools/ChangeLog"
- write_into_file_at_path(Tools_ChangeLog, "contents")
- Source_ChangeLog = "Source/ChangeLog"
- write_into_file_at_path(Source_ChangeLog, "contents")
- LayoutTests_ChangeLog = "LayoutTests/ChangeLog"
- write_into_file_at_path(LayoutTests_ChangeLog, "contents")
- Websites_ChangeLog = "Websites/ChangeLog"
- write_into_file_at_path(Websites_ChangeLog, "contents")
-
- Tools_ChangeFile = "Tools/ChangeFile"
- write_into_file_at_path(Tools_ChangeFile, "contents")
- Source_ChangeFile = "Source/ChangeFile"
- write_into_file_at_path(Source_ChangeFile, "contents")
- LayoutTests_ChangeFile = "LayoutTests/ChangeFile"
- write_into_file_at_path(LayoutTests_ChangeFile, "contents")
- Websites_ChangeFile = "Websites/ChangeFile"
- write_into_file_at_path(Websites_ChangeFile, "contents")
-
- run_command(['git', 'add', 'Tools/ChangeLog'])
- run_command(['git', 'add', 'LayoutTests/ChangeLog'])
- run_command(['git', 'add', 'Source/ChangeLog'])
- run_command(['git', 'add', 'Websites/ChangeLog'])
- run_command(['git', 'add', 'Tools/ChangeFile'])
- run_command(['git', 'add', 'LayoutTests/ChangeFile'])
- run_command(['git', 'add', 'Source/ChangeFile'])
- run_command(['git', 'add', 'Websites/ChangeFile'])
- scm = self.tracking_scm
- scm.commit_locally_with_message('message')
-
- patch = scm.create_patch()
- self.assertTrue(re.search(r'Tools/ChangeLog', patch).start() < re.search(r'Tools/ChangeFile', patch).start())
- self.assertTrue(re.search(r'Websites/ChangeLog', patch).start() < re.search(r'Websites/ChangeFile', patch).start())
- self.assertTrue(re.search(r'Source/ChangeLog', patch).start() < re.search(r'Source/ChangeFile', patch).start())
- self.assertTrue(re.search(r'LayoutTests/ChangeLog', patch).start() < re.search(r'LayoutTests/ChangeFile', patch).start())
-
- self.assertTrue(re.search(r'Source/ChangeLog', patch).start() < re.search(r'LayoutTests/ChangeLog', patch).start())
- self.assertTrue(re.search(r'Tools/ChangeLog', patch).start() < re.search(r'LayoutTests/ChangeLog', patch).start())
- self.assertTrue(re.search(r'Websites/ChangeLog', patch).start() < re.search(r'LayoutTests/ChangeLog', patch).start())
-
- self.assertTrue(re.search(r'Source/ChangeFile', patch).start() < re.search(r'LayoutTests/ChangeLog', patch).start())
- self.assertTrue(re.search(r'Tools/ChangeFile', patch).start() < re.search(r'LayoutTests/ChangeLog', patch).start())
- self.assertTrue(re.search(r'Websites/ChangeFile', patch).start() < re.search(r'LayoutTests/ChangeLog', patch).start())
-
- self.assertTrue(re.search(r'Source/ChangeFile', patch).start() < re.search(r'LayoutTests/ChangeFile', patch).start())
- self.assertTrue(re.search(r'Tools/ChangeFile', patch).start() < re.search(r'LayoutTests/ChangeFile', patch).start())
- self.assertTrue(re.search(r'Websites/ChangeFile', patch).start() < re.search(r'LayoutTests/ChangeFile', patch).start())
-
def test_exists(self):
scm = self.untracking_scm
self._shared_test_exists(scm, scm.commit_locally_with_message)
- def test_head_svn_revision(self):
- scm = detect_scm_system(self.untracking_checkout_path)
- # If we cloned a git repo tracking an SVN repo, this would give the same result as
- # self._shared_test_head_svn_revision().
- self.assertEqual(scm.head_svn_revision(), '')
-
def test_rename_files(self):
scm = self.tracking_scm
-
scm.move('foo_file', 'bar_file')
scm.commit_locally_with_message('message')
- patch = scm.create_patch()
- self.assertNotRegexpMatches(patch, r'rename from ')
- self.assertNotRegexpMatches(patch, r'rename to ')
-
-
-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])
+class GitSVNTest(SCMTestBase):
def setUp(self):
- self.original_dir = os.getcwd()
-
- SVNTestRepository.setup(self)
- self._setup_git_checkout()
+ super(GitSVNTest, self).setUp()
+ self._set_up_svn_checkout()
+ self._set_up_gitsvn_checkout()
self.scm = detect_scm_system(self.git_checkout_path)
self.scm.svn_server_realm = None
- # 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()
+ super(GitSVNTest, self).tearDown()
+ self._tear_down_svn_checkout()
+ self._tear_down_gitsvn_checkout()
+
+ def _set_up_gitsvn_checkout(self):
+ self.git_checkout_path = self._mkdtemp(suffix="git_test_checkout")
+ # --quiet doesn't make git svn silent
+ self._run_silent(['git', 'svn', 'clone', '-T', 'trunk', self.svn_repo_url, self.git_checkout_path])
+ self._chdir(self.git_checkout_path)
+
+ def _tear_down_gitsvn_checkout(self):
+ self._rmtree(self.git_checkout_path)
def test_detection(self):
self.assertEqual(self.scm.display_name(), "git")
@@ -1079,111 +391,61 @@ class GitSVNTest(SCMTest):
def test_read_git_config(self):
key = 'test.git-config'
value = 'git-config value'
- run_command(['git', 'config', key, value])
+ self._run(['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'])
+ test_file = self._join(self.git_checkout_path, 'test_file')
+ self._write_text_file(test_file, 'foo')
+ self._run(['git', 'commit', '-a', '-m', 'local commit'])
- self.assertEqual(len(self.scm.local_commits()), 1)
+ 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'])
+ test_file = self._join(self.git_checkout_path, 'test_file')
+ self._write_text_file(test_file, 'foo')
+ self._run(['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)
+ 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)
+ self._run(['git', 'checkout', '-b', new_branch])
+ self.assertEqual(self._run(['git', 'symbolic-ref', 'HEAD']).strip(), 'refs/heads/' + new_branch)
- run_command(['git', 'checkout', '-b', 'bar'])
+ self._run(['git', 'checkout', '-b', 'bar'])
self.scm.delete_branch(new_branch)
- self.assertNotRegexpMatches(run_command(['git', 'branch']), r'foo')
-
- 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.assertNotRegexpMatches(diff_to_common_base, r'foo')
- self.assertRegexpMatches(diff_to_merge_base, r'foo')
+ self.assertNotRegexpMatches(self._run(['git', 'branch']), r'foo')
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)
+ svn_test_file = self._join(self.svn_checkout_path, 'test_file')
+ self._write_text_file(svn_test_file, "svn_checkout")
+ self._run(['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'])
+ git_test_file = self._join(self.git_checkout_path, 'test_file')
+ self._write_text_file(git_test_file, "git_checkout")
+ self._run(['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.
+ # Should fail due to a conflict leaving us mid-rebase.
+ # we use self._run_slient because --quiet doesn't actually make git svn silent.
+ self.assertRaises(ScriptError, self._run_silent, ['git', 'svn', '--quiet', 'rebase'])
- self.assertTrue(self.scm.rebase_in_progress())
+ self.assertTrue(self.scm._rebase_in_progress())
# Make sure our cleanup works.
- self.scm.discard_working_directory_changes()
- self.assertFalse(self.scm.rebase_in_progress())
+ self.scm._discard_working_directory_changes()
+ self.assertFalse(self.scm._rebase_in_progress())
# Make sure cleanup doesn't throw when no rebase is in progress.
- self.scm.discard_working_directory_changes()
-
- def test_commitish_parsing(self):
- # Multiple revisions are cherry-picked.
- self.assertEqual(len(self.scm.commit_ids_from_commitish_arguments(['HEAD~2'])), 1)
- self.assertEqual(len(self.scm.commit_ids_from_commitish_arguments(['HEAD', 'HEAD~2'])), 2)
-
- # ... is an invalid range specifier
- self.assertRaises(ScriptError, self.scm.commit_ids_from_commitish_arguments, ['trunk...HEAD'])
-
- def test_commitish_order(self):
- commit_range = 'HEAD~3..HEAD'
-
- actual_commits = self.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):
- # 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(self.scm)
- Checkout(self.scm).apply_patch(patch)
-
- 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')
-
- 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'])
- commit_text = self.scm.commit_with_message("yet another test commit")
-
- self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6')
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertRegexpMatches(svn_log, r'test_file_commit1')
+ self.scm._discard_working_directory_changes()
def _local_commit(self, filename, contents, message):
- write_into_file_at_path(filename, contents)
- run_command(['git', 'add', filename])
+ self._write_text_file(filename, contents)
+ self._run(['git', 'add', filename])
self.scm.commit_locally_with_message(message)
def _one_local_commit(self):
@@ -1191,8 +453,8 @@ class GitSVNTest(SCMTest):
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'])
+ self._write_text_file('test_file_commit2', 'still more test content')
+ self._run(['git', 'add', 'test_file_commit2'])
def _second_local_commit(self):
self._local_commit('test_file_commit2', 'still more test content', 'yet another test commit')
@@ -1207,144 +469,24 @@ class GitSVNTest(SCMTest):
def test_locally_commit_all_working_copy_changes(self):
self._local_commit('test_file', 'test content', 'test commit')
- write_into_file_at_path('test_file', 'changed test content')
+ self._write_text_file('test_file', 'changed test content')
self.assertTrue(self.scm.has_working_directory_changes())
self.scm.commit_locally_with_message('all working copy changes')
self.assertFalse(self.scm.has_working_directory_changes())
def test_locally_commit_no_working_copy_changes(self):
self._local_commit('test_file', 'test content', 'test commit')
- write_into_file_at_path('test_file', 'changed test content')
+ self._write_text_file('test_file', 'changed test content')
self.assertTrue(self.scm.has_working_directory_changes())
self.assertRaises(ScriptError, self.scm.commit_locally_with_message, 'no working copy changes', False)
- def test_locally_commit_selected_working_copy_changes(self):
- self._local_commit('test_file_1', 'test content 1', 'test commit 1')
- self._local_commit('test_file_2', 'test content 2', 'test commit 2')
- write_into_file_at_path('test_file_1', 'changed test content 1')
- write_into_file_at_path('test_file_2', 'changed test content 2')
- self.assertTrue(self.scm.has_working_directory_changes())
- run_command(['git', 'add', 'test_file_1'])
- self.scm.commit_locally_with_message('selected working copy changes', commit_all_working_directory_changes=False)
- self.assertTrue(self.scm.has_working_directory_changes())
- self.assertTrue(self.scm.diff_for_file('test_file_1') == '')
- self.assertFalse(self.scm.diff_for_file('test_file_2') == '')
-
- def test_revisions_changing_files_with_local_commit(self):
- self._one_local_commit()
- self.assertItemsEqual(self.scm.revisions_changing_file('test_file_commit1'), [])
-
- def test_commit_with_message(self):
- self._one_local_commit_plus_working_copy_changes()
- self.assertRaises(AmbiguousCommitError, self.scm.commit_with_message, "yet another test commit")
- commit_text = self.scm.commit_with_message("yet another test commit", force_squash=True)
-
- self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6')
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertRegexpMatches(svn_log, r'test_file_commit2')
- self.assertRegexpMatches(svn_log, r'test_file_commit1')
-
- def test_commit_with_message_git_commit(self):
- self._two_local_commits()
-
- commit_text = self.scm.commit_with_message("another test commit", git_commit="HEAD^")
- self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6')
-
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertRegexpMatches(svn_log, r'test_file_commit1')
- self.assertNotRegexpMatches(svn_log, r'test_file_commit2')
-
- def test_commit_with_message_git_commit_range(self):
- self._three_local_commits()
-
- commit_text = self.scm.commit_with_message("another test commit", git_commit="HEAD~2..HEAD")
- self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6')
-
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertNotRegexpMatches(svn_log, r'test_file_commit0')
- self.assertRegexpMatches(svn_log, r'test_file_commit1')
- self.assertRegexpMatches(svn_log, r'test_file_commit2')
-
- def test_commit_with_message_only_local_commit(self):
- self._one_local_commit()
- commit_text = self.scm.commit_with_message("another test commit")
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertRegexpMatches(svn_log, r'test_file_commit1')
-
- 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')
-
- self.assertRaises(AmbiguousCommitError, self.scm.commit_with_message, "another test commit")
- commit_text = self.scm.commit_with_message("another test commit", force_squash=True)
-
- self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6')
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertRegexpMatches(svn_log, r'test_file_commit2')
- self.assertRegexpMatches(svn_log, r'test_file_commit1')
-
- 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')
- self.assertRaises(ScriptError, self.scm.commit_with_message, "another test commit", git_commit="HEAD^")
-
- def test_commit_with_message_multiple_local_commits_always_squash(self):
- run_command(['git', 'config', 'webkit-patch.commit-should-always-squash', 'true'])
- self._two_local_commits()
- commit_text = self.scm.commit_with_message("yet another test commit")
- self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6')
-
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertRegexpMatches(svn_log, r'test_file_commit2')
- self.assertRegexpMatches(svn_log, r'test_file_commit1')
-
- def test_commit_with_message_multiple_local_commits(self):
- self._two_local_commits()
- self.assertRaises(AmbiguousCommitError, self.scm.commit_with_message, "yet another test commit")
- commit_text = self.scm.commit_with_message("yet another test commit", force_squash=True)
-
- self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6')
-
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertRegexpMatches(svn_log, r'test_file_commit2')
- self.assertRegexpMatches(svn_log, r'test_file_commit1')
-
- def test_commit_with_message_not_synced(self):
- run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3'])
- self._two_local_commits()
- self.assertRaises(AmbiguousCommitError, self.scm.commit_with_message, "another test commit")
- commit_text = self.scm.commit_with_message("another test commit", force_squash=True)
-
- self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6')
-
- svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
- self.assertNotRegexpMatches(svn_log, r'test_file2')
- self.assertRegexpMatches(svn_log, r'test_file_commit2')
- self.assertRegexpMatches(svn_log, r'test_file_commit1')
-
- def test_commit_with_message_not_synced_with_conflict(self):
- run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3'])
- self._local_commit('test_file2', 'asdf', 'asdf commit')
-
- # There's a conflict between trunk and the test_file2 modification.
- self.assertRaises(ScriptError, self.scm.commit_with_message, "another test commit", force_squash=True)
-
- def test_upstream_branch(self):
- run_command(['git', 'checkout', '-t', '-b', 'my-branch'])
- run_command(['git', 'checkout', '-t', '-b', 'my-second-branch'])
+ def _test_upstream_branch(self):
+ self._run(['git', 'checkout', '-t', '-b', 'my-branch'])
+ self._run(['git', 'checkout', '-t', '-b', 'my-second-branch'])
self.assertEqual(self.scm._upstream_branch(), 'my-branch')
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()
+ self.assertEqual(self.scm._remote_branch_ref(), 'refs/remotes/trunk')
def test_create_patch_local_plus_working_copy(self):
self._one_local_commit_plus_working_copy_changes()
@@ -1360,9 +502,9 @@ class GitSVNTest(SCMTest):
self.assertRegexpMatches(patch, r'Subversion Revision: 5')
def test_create_patch_after_merge(self):
- run_command(['git', 'checkout', '-b', 'dummy-branch', 'trunk~3'])
+ self._run(['git', 'checkout', '-b', 'dummy-branch', 'trunk~3'])
self._one_local_commit()
- run_command(['git', 'merge', 'trunk'])
+ self._run(['git', 'merge', 'trunk'])
patch = self.scm.create_patch()
self.assertRegexpMatches(patch, r'test_file_commit1')
@@ -1375,7 +517,7 @@ class GitSVNTest(SCMTest):
def test_create_patch_with_rm_and_changed_files(self):
self._one_local_commit_plus_working_copy_changes()
- os.remove('test_file_commit1')
+ self._remove('test_file_commit1')
patch = self.scm.create_patch()
patch_with_changed_files = self.scm.create_patch(changed_files=['test_file_commit1', 'test_file_commit2'])
self.assertEqual(patch, patch_with_changed_files)
@@ -1406,7 +548,7 @@ class GitSVNTest(SCMTest):
self.assertRegexpMatches(patch, r'test_file_commit1')
def test_create_patch_not_synced(self):
- run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3'])
+ self._run(['git', 'checkout', '-b', 'my-branch', 'trunk~3'])
self._two_local_commits()
patch = self.scm.create_patch()
self.assertNotRegexpMatches(patch, r'test_file2')
@@ -1416,29 +558,24 @@ class GitSVNTest(SCMTest):
def test_create_binary_patch(self):
# Create a git binary patch and check the contents.
test_file_name = 'binary_file'
- test_file_path = os.path.join(self.git_checkout_path, test_file_name)
+ test_file_path = self.fs.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])
+ self._write_binary_file(test_file_path, file_contents)
+ self._run(['git', 'add', test_file_name])
patch = self.scm.create_patch()
self.assertRegexpMatches(patch, r'\nliteral 0\n')
self.assertRegexpMatches(patch, r'\nliteral 256\n')
- # Check if we can apply the created patch.
- run_command(['git', 'rm', '-f', test_file_name])
- self._setup_webkittools_scripts_symlink(self.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'])
+ self._write_binary_file(test_file_path, file_contents)
+ self._run(['git', 'add', test_file_name])
+ self._run(['git', 'commit', '-m', 'binary diff'])
patch_from_local_commit = self.scm.create_patch('HEAD')
self.assertRegexpMatches(patch_from_local_commit, r'\nliteral 0\n')
self.assertRegexpMatches(patch_from_local_commit, r'\nliteral 256\n')
+
def test_changed_files_local_plus_working_copy(self):
self._one_local_commit_plus_working_copy_changes()
files = self.scm.changed_files()
@@ -1481,34 +618,20 @@ class GitSVNTest(SCMTest):
self.assertIn('test_file_commit1', files)
def test_changed_files_not_synced(self):
- run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3'])
- self._two_local_commits()
- files = self.scm.changed_files()
- self.assertNotIn('test_file2', files)
- self.assertIn('test_file_commit2', files)
- self.assertIn('test_file_commit1', files)
-
- def test_changed_files_not_synced(self):
- run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3'])
+ self._run(['git', 'checkout', '-b', 'my-branch', 'trunk~3'])
self._two_local_commits()
files = self.scm.changed_files()
self.assertNotIn('test_file2', files)
self.assertIn('test_file_commit2', files)
self.assertIn('test_file_commit1', 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_changed_files_upstream(self):
- run_command(['git', 'checkout', '-t', '-b', 'my-branch'])
+ self._run(['git', 'checkout', '-t', '-b', 'my-branch'])
self._one_local_commit()
- run_command(['git', 'checkout', '-t', '-b', 'my-second-branch'])
+ self._run(['git', 'checkout', '-t', '-b', 'my-second-branch'])
self._second_local_commit()
- write_into_file_at_path('test_file_commit0', 'more test content')
- run_command(['git', 'add', 'test_file_commit0'])
+ self._write_text_file('test_file_commit0', 'more test content')
+ self._run(['git', 'add', 'test_file_commit0'])
# equivalent to 'git diff my-branch..HEAD, should not include working changes
files = self.scm.changed_files(git_commit='UPSTREAM..')
@@ -1522,31 +645,19 @@ class GitSVNTest(SCMTest):
self.assertIn('test_file_commit2', files)
self.assertIn('test_file_commit0', 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_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.assertIn("test_file_commit1", self.scm.deleted_files())
+ self.assertIn("test_file_commit1", self.scm._deleted_files())
def test_delete_list(self):
self._two_local_commits()
self.scm.delete_list(["test_file_commit1", "test_file_commit2"])
- self.assertIn("test_file_commit1", self.scm.deleted_files())
- self.assertIn("test_file_commit2", self.scm.deleted_files())
+ self.assertIn("test_file_commit1", self.scm._deleted_files())
+ self.assertIn("test_file_commit2", self.scm._deleted_files())
def test_delete_recursively(self):
self._shared_test_delete_recursively()
@@ -1554,85 +665,22 @@ class GitSVNTest(SCMTest):
def test_delete_recursively_or_not(self):
self._shared_test_delete_recursively_or_not()
- def test_head_svn_revision(self):
- self._shared_test_head_svn_revision()
-
def test_move(self):
self._shared_test_move()
def test_move_recursive(self):
self._shared_test_move_recursive()
- def test_to_object_name(self):
- relpath = 'test_file_commit1'
- fullpath = os.path.realpath(os.path.join(self.git_checkout_path, relpath))
- 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.assertIn("+Updated", diff)
- self.assertIn("-more test content", diff)
-
- self.scm.add('test_file_commit1')
-
- cached_diff = self.scm.diff_for_file('test_file_commit1')
- self.assertIn("+Updated", cached_diff)
- self.assertIn("-more test content", cached_diff)
-
def test_exists(self):
self._shared_test_exists(self.scm, self.scm.commit_locally_with_message)
-# We need to split off more of these SCM tests to use mocks instead of the filesystem.
-# This class is the first part of that.
-class GitTestWithMock(unittest.TestCase):
- maxDiff = None
-
- def make_scm(self, logging_executive=False):
- # We do this should_log dance to avoid logging when Git.__init__ runs sysctl on mac to check for 64-bit support.
+class GitTestWithMock(SCMTestBase):
+ def make_scm(self):
scm = Git(cwd=".", executive=MockExecutive(), filesystem=MockFileSystem())
scm.read_git_config = lambda *args, **kw: "MOCKKEY:MOCKVALUE"
- scm._executive._should_log = logging_executive
return scm
- def test_create_patch(self):
- scm = self.make_scm(logging_executive=True)
- expected_stderr = """\
-MOCK run_command: ['git', 'merge-base', 'MOCKVALUE', 'HEAD'], cwd=%(checkout)s
-MOCK run_command: ['git', 'diff', '--binary', '--no-color', '--no-ext-diff', '--full-index', '--no-renames', '', 'MOCK output of child process', '--'], cwd=%(checkout)s
-MOCK run_command: ['git', 'rev-parse', '--show-toplevel'], cwd=%(checkout)s
-MOCK run_command: ['git', 'log', '-1', '--grep=git-svn-id:', '--date=iso', './MOCK output of child process/MOCK output of child process'], cwd=%(checkout)s
-""" % {'checkout': scm.checkout_root}
- OutputCapture().assert_outputs(self, scm.create_patch, expected_logs=expected_stderr)
-
- def test_push_local_commits_to_server_with_username_and_password(self):
- self.assertEqual(self.make_scm().push_local_commits_to_server(username='dbates@webkit.org', password='blah'), "MOCK output of child process")
-
- def test_push_local_commits_to_server_without_username_and_password(self):
- self.assertRaises(AuthenticationError, self.make_scm().push_local_commits_to_server)
-
- def test_push_local_commits_to_server_with_username_and_without_password(self):
- self.assertRaises(AuthenticationError, self.make_scm().push_local_commits_to_server, {'username': 'dbates@webkit.org'})
-
- def test_push_local_commits_to_server_without_username_and_with_password(self):
- self.assertRaises(AuthenticationError, self.make_scm().push_local_commits_to_server, {'password': 'blah'})
-
def test_timestamp_of_revision(self):
scm = self.make_scm()
scm.find_checkout_root = lambda path: ''
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/svn.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/svn.py
index 7c37ac8e387..54f2fa8e750 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/svn.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/svn.py
@@ -39,39 +39,12 @@ import tempfile
from webkitpy.common.memoized import memoized
from webkitpy.common.system.executive import Executive, ScriptError
-from .scm import AuthenticationError, SCM, commit_error_handler
+from .scm import SCM
_log = logging.getLogger(__name__)
-# A mixin class that represents common functionality for SVN and Git-SVN.
-class SVNRepository(object):
- # FIXME: These belong in common.config.urls
- svn_server_host = "svn.webkit.org"
- svn_server_realm = "<http://svn.webkit.org:80> Mac OS Forge"
-
- def has_authorization_for_realm(self, realm, home_directory=os.getenv("HOME")):
- # If we are working on a file:// repository realm will be None
- if realm is None:
- return True
- # ignore false positives for methods implemented in the mixee class. pylint: disable=E1101
- # 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()
- if not find_output or not os.path.isfile(os.path.join(home_directory, find_output)):
- return False
- # Subversion either stores the password in the credential file, indicated by the presence of the key "password",
- # or uses the system password store (e.g. Keychain on Mac OS X) as indicated by the presence of the key "passtype".
- # We assume that these keys will not coincide with the actual credential data (e.g. that a person's username
- # isn't "password") so that we can use grep.
- if self.run(["grep", "password", find_output], cwd=home_directory, return_exit_code=True) == 0:
- return True
- return self.run(["grep", "passtype", find_output], cwd=home_directory, return_exit_code=True) == 0
-
-
-class SVN(SCM, SVNRepository):
+class SVN(SCM):
executable_name = "svn"
@@ -99,7 +72,7 @@ class SVN(SCM, SVNRepository):
exit_code = executive.run_command(svn_info_args, cwd=path, return_exit_code=True)
return (exit_code == 0)
- def find_uuid(self, path):
+ def _find_uuid(self, path):
if not self.in_working_directory(path):
return None
return self.value_from_svn_info(path, 'Repository UUID')
@@ -115,64 +88,36 @@ class SVN(SCM, SVNRepository):
return match.group('value').rstrip('\r')
def find_checkout_root(self, path):
- uuid = self.find_uuid(path)
+ uuid = self._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 != self.find_uuid(path):
+ if uuid != self._find_uuid(path):
return last_path
last_path = path
(path, last_component) = self._filesystem.split(path)
if last_path == path:
return None
- @staticmethod
- def commit_success_regexp():
- return "^Committed revision (?P<svn_revision>\d+)\.$"
-
def _run_svn(self, args, **kwargs):
- return self.run([self.executable_name] + args, **kwargs)
+ return self._run([self.executable_name] + args, **kwargs)
@memoized
- def svn_version(self):
+ def _svn_version(self):
return self._run_svn(['--version', '--quiet'])
def has_working_directory_changes(self):
# FIXME: What about files which are not committed yet?
return self._run_svn(["diff"], cwd=self.checkout_root, decode_output=False) != ""
- def discard_working_directory_changes(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 [self.executable_name, 'status']
def _status_regexp(self, expected_types):
- field_count = 6 if self.svn_version() > "1.6" else 5
+ 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):
@@ -211,33 +156,13 @@ class SVN(SCM, SVNRepository):
status_command = [self.executable_name, "status"]
status_command.extend(self._patch_directories)
# ACDMR: Addded, Conflicted, Deleted, Modified or Replaced
- return self.run_status_and_extract_filenames(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 = [self.executable_name, "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 = ['log', '--quiet', '--limit=%s' % limit, path]
- for line in self._run_svn(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"))
+ return self._run_status_and_extract_filenames(status_command, self._status_regexp("ACDMR"))
+
+ 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():
@@ -256,7 +181,6 @@ class SVN(SCM, SVNRepository):
match = re.search(r"^<date>(?P<value>.+)</date>\r?$", info_output, re.MULTILINE)
return match.group('value')
- # FIXME: This method should be on Checkout.
def create_patch(self, git_commit=None, changed_files=None):
"""Returns a byte array (str()) representing the patch file.
Patch files are effectively binary since they may contain
@@ -265,112 +189,9 @@ class SVN(SCM, SVNRepository):
return ""
elif changed_files == None:
changed_files = []
- return self.run([self.script_path("svn-create-patch")] + changed_files,
+ return self._run([self._filesystem.join(self.checkout_root, 'Tools', 'Scripts', '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):
- rnd = ''.join(random.sample(string.ascii_letters, 5))
- 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_" + rnd)
-
- 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 = ['diff']
- if self._bogus_dir:
- args += ['--config-dir', self._bogus_dir]
- args.append(path)
- return self._run_svn(args, cwd=self.checkout_root)
- 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 = ['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.debug("Running 'svn %s'" % " ".join(svn_merge_args))
- # FIXME: Should this use cwd=self.checkout_root?
- self._run_svn(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, password=None, git_commit=None, force_squash=False, changed_files=None):
- # git-commit and force are not used by SVN.
- svn_commit_args = ["commit"]
-
- if not username and not self.has_authorization_for_realm(self.svn_server_realm):
- raise AuthenticationError(self.svn_server_host)
- if username:
- svn_commit_args.extend(["--username", username])
-
- svn_commit_args.extend(["-m", message])
-
- if changed_files:
- svn_commit_args.extend(changed_files)
-
- return self._run_svn(svn_commit_args, cwd=self.checkout_root, 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 blame(self, path):
return self._run_svn(['blame', path])
-
- def svn_blame(self, path):
- return self._run_svn(['blame', path])
-
- 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")
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/ports.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/ports.py
deleted file mode 100644
index abea220a4c2..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/ports.py
+++ /dev/null
@@ -1,118 +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
-import sys
-
-from webkitpy.common.system.executive import Executive
-
-
-class DeprecatedPort(object):
- results_directory = "/tmp/layout-test-results"
-
- # Subclasses must override
- port_flag_name = None
-
- # FIXME: This is only used by BotInfo.
- def name(self):
- return self.__class__
-
- def flag(self):
- if self.port_flag_name:
- return "--port=%s" % self.port_flag_name
- return None
-
- # We might need to pass scm into this function for scm.checkout_root
- def script_path(self, script_name):
- return os.path.join("Tools", "Scripts", script_name)
-
- def script_shell_command(self, script_name):
- script_path = self.script_path(script_name)
- return Executive.shell_command_for_script(script_path)
-
- @staticmethod
- def port(port_name):
- ports = {
- "chromium": ChromiumPort,
- "chromium-android": AndroidPort,
- "chromium-xvfb": ChromiumXVFBPort,
- }
- return ports.get(port_name, ChromiumPort)()
-
- def makeArgs(self):
- # FIXME: This shouldn't use a static Executive().
- args = '--makeargs="-j%s"' % Executive().cpu_count()
- if os.environ.has_key('MAKEFLAGS'):
- args = '--makeargs="%s"' % os.environ['MAKEFLAGS']
- return args
-
- def check_webkit_style_command(self):
- return self.script_shell_command("check-webkit-style")
-
- def run_webkit_unit_tests_command(self):
- return None
-
- def run_webkit_tests_command(self):
- return self.script_shell_command("run-webkit-tests")
-
- def run_python_unittests_command(self):
- return self.script_shell_command("test-webkitpy")
-
- def run_perl_unittests_command(self):
- return self.script_shell_command("test-webkitperl")
-
- def run_bindings_tests_command(self):
- return self.script_shell_command("run-bindings-tests")
-
-
-class ChromiumPort(DeprecatedPort):
- port_flag_name = "chromium"
-
- def run_webkit_tests_command(self):
- # Note: This could be run-webkit-tests now.
- command = self.script_shell_command("new-run-webkit-tests")
- command.append("--chromium")
- command.append("--skip-failing-tests")
- return command
-
- def run_webkit_unit_tests_command(self):
- return self.script_shell_command("run-chromium-webkit-unit-tests")
-
-
-class AndroidPort(ChromiumPort):
- port_flag_name = "chromium-android"
-
-
-class ChromiumXVFBPort(ChromiumPort):
- port_flag_name = "chromium-xvfb"
-
- def run_webkit_tests_command(self):
- return ["xvfb-run"] + super(ChromiumXVFBPort, self).run_webkit_tests_command()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/watchlist b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/watchlist
deleted file mode 100644
index e61f122d0ba..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/watchlist
+++ /dev/null
@@ -1,460 +0,0 @@
-# When editing this file, please run the following command to make sure you
-# haven't introduced any syntax errors:
-#
-# ./Tools/Scripts/check-webkit-style
-#
-# If you want to test your regular expressions, you can edit various files and
-# then try following command:
-#
-# ./Tools/Scripts/webkit-patch apply-watchlist-local
-#
-{
- "DEFINITIONS": {
- "ChromiumGraphics": {
- "filename": r"Source/WebCore/platform/graphics/chromium/",
- },
- "ChromiumPublicApi": {
- "filename": r"Source/WebKit/chromium/public/"
- r"|Source/Platform/chromium/public/"
- r"|Tools/DumpRenderTree/chromium/TestRunner/public",
- },
- "ChromiumTestRunner": {
- "filename": r"Tools/DumpRenderTree/chromium/TestRunner",
- },
- "AppleMacPublicApi": {
- "filename": r"Source/WebCore/bindings/objc/PublicDOMInterfaces.h"
- },
- "Forms": {
- "filename": r"Source/WebCore/html/HTML(DataList|FieldSet|Input|Keygen|Label|Legend|OptGroup|Option|Output|Select|TextArea)Element\."
- r"|Source/WebCore/html/.*Form[A-Z].*\."
- r"|Source/WebCore/html/\w*InputType\."
- r"|Source/WebCore/html/shadow/(SliderThumbElement|TextControlInnerElements)\."
- r"|Source/WebCore/rendering/Render(FileUploadControl|ListBox|MenuList|Slider|TextControl.*)\."
- },
- "Geolocation": {
- "filename": r"Source/WebCore/Modules/geolocation/"
- r"|Source/WebCore/page/GeolocationClient.h"
- r"|Source/WebCore/bindings/js/JSGeolocationCustom.cpp"
- r"|Source/WebCore/bindings/v8/custom/V8GeolocationCustom.cpp"
- r"|Source/WebCore/platform/mock/GeolocationClientMock.(h|cpp)"
- r"|Source/WebKit2/WebProcess/Geolocation/",
- },
- "GStreamerGraphics": {
- "filename": r"Source/WebCore/platform/graphics/gstreamer/",
- },
- "GStreamerAudio": {
- "filename": r"Source/WebCore/platform/audio/gstreamer/",
- },
- "WebIDL": {
- "filename": r"Source/WebCore/(?!inspector)(?!testing).*\.idl"
- },
- "ThreadingFiles": {
- "filename": r"Source/JavaScriptCore/wtf/ThreadSpecific\."
- r"|Source/JavaScriptCore/wtf/ThreadSafeRefCounted\."
- r"|Source/JavaScriptCore/wtf/ThreadingPrimitives\."
- r"|Source/JavaScriptCore/wtf/Threading\."
- r"|Source/WebCore/dom/CrossThreadTask\."
- r"|Source/WebCore/platform/CrossThreadCopier\.",
- },
- "ThreadingUsage": {
- # The intention of this regex is to detect places where people are using common threading mechanisms,
- # so that one can look them over for common mistakes. This list is long and likely to get longer over time.
- # Note the negative look-ahead to avoid new mentions of the files (for builds or includes).
- "more": r"(AllowCrossThreadAccess|AtomicallyInitialize|CrossThreadCopier|CrossThreadRefCounted|Mutex|ReadWriteLock|ThreadCondition|ThreadSafeRefCounted|ThreadSpecific"
- r"|createCallbackTask|crossThreadString|deprecatedTurnOffVerifier|threadsafeCopy)(?!\.(h|cpp))",
- },
- "WatchListScript": {
- "filename": r"Tools/Scripts/webkitpy/common/watchlist/",
- },
- "webkitpy": {
- "filename": r"Tools/Scripts/webkitpy/",
- },
- "webkitperl": {
- "filename": r"Tools/Scripts/webkitperl/"
- r"|Tools/Scripts/webkitdirs.pm"
- r"|Tools/Scripts/VCSUtils.pm"
- r"|Tools/Scripts/test-webkitperl",
- },
- "SVNScripts": {
- "filename": r"Tools/Scripts/svn-.*",
- },
- "TestFailures": {
- "filename": r"Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/",
- },
- "SecurityCritical": {
- "more": r"[Ss]ecurityOrigin(?!\.(h|cpp))",
- "less": r"[Ss]ecurityOrigin(?!\.(h|cpp))",
- "filename": r"XSS|[Ss]ecurity",
- },
- "XSS": {
- "filename": r".*XSS",
- },
- "SkiaGraphics": {
- "filename": r"Source/WebCore/platform/graphics/skia/"
- r"|Source/WebCore/platform/graphics/filters/",
- },
- "V8Bindings": {
- "filename": r"Source/WebCore/bindings/v8/",
- },
- "BindingsScripts": {
- "filename": r"Source/WebCore/bindings/scripts/",
- },
- "FrameLoader": {
- "more": r"FrameLoader\.(cpp|h)",
- },
- "Loader": {
- "filename": r"Source/WebCore/loader/",
- },
- "Rendering": {
- "filename": r"Source/WebCore/rendering/",
- },
- "RenderLayers": {
- "filename": r"Source/WebCore/rendering/RenderLayer*",
- },
- "GraphicsLayer": {
- "filename": r"Source/WebCore/platform/graphics/GraphicsLayer*",
- },
- "CoreAnimation": {
- "filename": r"Source/WebCore/platform/graphics/ca/",
- },
- "Animation": {
- "filename": r"Source/WebCore/page/animation/",
- },
- "StyleChecker": {
- "filename": r"Tools/Scripts/webkitpy/style/",
- },
- "GtkWebKit2PublicAPI": {
- "filename": r"Source/WebKit2/UIProcess/API/gtk/",
- },
- "QtBuildSystem": {
- # Project files for each target are intentionally left out, as those
- # mostly list source and header files, which would just add noise.
- "filename": r"Tools/qmake/"
- r"|WebKit.pro",
- },
- "QtWebKit2PublicAPI": {
- "filename": r"Source/WebKit2/UIProcess/API/qt/"
- r"|Source/WebKit2/UIProcess/API/cpp/qt/"
- r"|Source/WebKit2/UIProcess/API/C/qt/",
- },
- "QtGraphics": {
- "filename": r"Source/WebCore/platform/graphics/qt/"
- },
- "CoordinatedGraphics": {
- "filename": r"Source/WebKit2/WebProcess/WebPage/CoordinatedGraphics/"
- r"|Source/WebKit2/UIProcess/CoordinatedGraphics/"
- r"|Source/WebKit2/Shared/CoordinatedGraphics/"
- r"|Source/WebCore/platform/graphics/surfaces/",
- },
- "TextureMapper": {
- "filename": r"Source/WebCore/platform/graphics/texmap/",
- },
- "OpenGL": {
- "filename": r"Source/WebCore/platform/graphics/opengl/",
- },
- "QtWebKit2PlatformSpecific": {
- "filename": r"Source/WebKit2/.*\.(pri|pro)"
- r"|Source/WebKit2/Platform/qt/"
- r"|Source/WebKit2/qt/"
- r"|Source/WebKit2/PluginProcess/qt/"
- r"|Source/WebKit2/Platform/qt/"
- r"|Source/WebKit2/Shared/API/c/qt/"
- r"|Source/WebKit2/Shared/qt/"
- r"|Source/WebKit2/WebProcess/InjectedBundle/qt/"
- r"|Source/WebKit2/WebProcess/FullScreen/qt/"
- r"|Source/WebKit2/WebProcess/WebPage/qt/"
- r"|Source/WebKit2/WebProcess/qt/"
- r"|Source/WebKit2/WebProcess/Plugins/Netscape/qt/"
- r"|Source/WebKit2/WebProcess/Downloads/qt/"
- r"|Source/WebKit2/WebProcess/WebCoreSupport/qt/"
- r"|Source/WebKit2/WebProcess/Cookies/qt/"
- r"|Source/WebKit2/UIProcess/qt/"
- r"|Source/WebKit2/UIProcess/Plugins/qt/"
- r"|Source/WebKit2/UIProcess/Launcher/qt/",
- },
- "CSS": {
- "filename": r"Source/WebCore/css/",
- },
- "DOM": {
- "filename": r"Source/WebCore/dom/",
- },
- "HTML": {
- "filename": r"Source/WebCore/html/",
- },
- "DOMAttributes": {
- "filename": r"Source/WebCore/dom/.*Attr.*"
- r"|Source/WebCore/dom/NamedNodeMap\.(cpp|h|idl)"
- r"|Source/WebCore/dom/Element\.(cpp|h|idl)",
- },
- "EFL": {
- "filename": r"Source/WebKit/efl/"
- r"|Source/WebCore/platform/efl/"
- r"|Source/WTF/wtf/efl/"
- r"|Tools/EWebLauncher"
- r"|Tools/DumpRenderTree/efl/"
- r"|LayoutTests/platform/efl/",
- },
- "EFLWebKit2PublicAPI": {
- "filename": r"Source/WebKit2/UIProcess/API/efl/"
- r"|Source/WebKit2/UIProcess/API/C/efl/",
- },
- "EFLWebKit2PlatformSpecific": {
- "filename": r"Source/WebKit2/.*\.(cmake|txt)"
- r"|Source/WebKit2/Platform/efl/"
- r"|Source/WebKit2/efl/"
- r"|Source/WebKit2/Shared/API/c/efl/"
- r"|Source/WebKit2/Shared/efl/"
- r"|Source/WebKit2/WebProcess/InjectedBundle/efl/"
- r"|Source/WebKit2/WebProcess/WebPage/efl/"
- r"|Source/WebKit2/WebProcess/efl/"
- r"|Source/WebKit2/WebProcess/Downloads/efl/"
- r"|Source/WebKit2/WebProcess/WebCoreSupport/efl/"
- r"|Source/WebKit2/UIProcess/efl/"
- r"|Source/WebKit2/UIProcess/Launcher/efl/",
- },
- "CMake": {
- "filename": r".*CMakeLists\w*\.txt"
- r"|.*\w+\.cmake"
- r"|Source/cmake/",
- },
- "Selectors": {
- "filename": r"Source/WebCore/css/CSSSelector*"
- r"|Source/WebCore/css/SelectorChecker.*"
- r"|Source/WebCore/css/StyleResolver.*"
- r"|Source/WebCore/dom/SelectorQuery.*",
- },
- "SoupNetwork": {
- "filename": r"Source/WebCore/platform/network/soup/",
- },
- "ScrollingCoordinator": {
- "filename": r"Source/WebCore/page/scrolling/",
- },
- "WebKitGTKTranslations": {
- "filename": r"Source/WebKit/gtk/po/",
- },
- "Media": {
- "filename": r"(Source|LayoutTests)/.*([Mm]edia|[Aa]udio|[Vv]ideo)",
- },
- "MathML": {
- "filename": r"(Source|LayoutTests|Websites)/.*mathml",
- },
- "Editing": {
- "filename": r"Source/WebCore/editing/",
- },
- "BlackBerry": {
- "filename": r"Source/WebKit/blackberry/"
- r"|Source/WebCore/page/blackberry"
- r"|Source/WebCore/history/blackberry"
- r"|Source/WebCore/plugins/blackberry"
- r"|Source/WebCore/editing/blackberry"
- r"|Source/WebCore/Resources/blackberry"
- r"|Source/WebCore/platform/image-decoders/blackberry"
- r"|Source/WebCore/platform/blackberry"
- r"|Source/WebCore/platform/text/blackberry"
- r"|Source/WebCore/platform/network/blackberry"
- r"|Source/WebCore/platform/graphics/blackberry"
- r"|Source/WTF/wtf/blackberry"
- r"|ManualTests/blackberry"
- r"|Tools/DumpRenderTree/blackberry"
- r"|LayoutTests/platform/blackberry",
- },
- "NetworkInfo": {
- "filename": r"Source/WebCore/Modules/networkinfo",
- },
- "Battery": {
- "filename": r"Source/WebCore/Modules/battery",
- },
- "WTF": {
- "filename": r"Source/WTF/wtf",
- },
- "WebGL": {
- "filename": r"Source/WebCore/html/canvas/.*WebGL.*"
- r"|Source/WebCore/bindings/js/.*WebGL.*"
- r"|Source/WebCore/platform/graphics/gpu"
- r"|Source/WebCore/platform/graphics/opengl"
- r"|Source/WebCore/platform/graphics/ANGLE.*"
- r"|Source/WebCore/platform/graphics/.*GraphicsContext3D.*"
- r"|Source/ThirdParty/ANGLE",
- },
- "Filters": {
- "filename": r"Source/WebCore/platform/graphics/filters"
- r"|Source/WebCore/rendering/.*Filter.*"
- r"|Source/WebCore/rendering/style/.*Filter.*"
- r"|Source/WebCore/rendering/svg/.*Filter.*"
- r"|Source/WebCore/svg/graphics/filters"
- r"|Source/WebCore/svg/graphics/.*Filter.*",
- },
- "TouchAdjustment": {
- "filename": r"Source/WebCore/page/TouchAdjustment.*"
- r"|LayoutTests/touchadjustment"
- r"|Source/WebKit/blackberry/WebKitSupport/FatFingers.*",
- },
- "SVG": {
- "filename": r"Source/WebCore/svg"
- r"|Source/WebCore/rendering/svg",
- },
- "WebInspectorAPI": {
- "filename": r"Source/WebCore/inspector/InjectedScriptSource.js"
- r"|Source/WebCore/inspector/.+\.json"
- r"|Source/WebCore/inspector/.+\.idl"
- r"|Source/WebCore/page/Console.idl",
- },
- "WebSocket": {
- "filename": r"Source/WebCore/Modules/websockets"
- r"|Source/WebCore/platform/network/(|.+/)SocketStream.*",
- },
- "MediaStream": {
- "filename": r"Source/WebCore/Modules/mediastream"
- r"|Source/WebCore/platform/mediastream"
- r"|LayoutTests/fast/mediastream",
- },
- "Accessibility": {
- "filename": r"Source/WebCore/accessibility"
- r"|LayoutTests/.*accessibility",
- },
- "Cairo": {
- "filename": r"Source/WebCore/platform/graphics/cairo",
- },
- "Harfbuzz": {
- "filename": r"Source/WebCore/platform/graphics/harfbuzz",
- },
- "PerformanceTests": {
- "filename": r"PerformanceTests"
- r"|Tools/Scripts/webkitpy/performance_tests",
- },
- "GtkBuildSystem": {
- "filename": r"configure.ac"
- r"|.*GNUmakefile.(am|features.am.in)",
- },
- "ConsoleUsage": {
- "more": r"[Aa]ddConsoleMessage|reportException|logExceptionToConsole|addMessage|printErrorMessage"
- },
- "ContentSecurityPolicyUsage": {
- "more": r"[Cc]ontentSecurityPolicy(?!\.(h|cpp))",
- },
- "ContentSecurityPolicyFiles": {
- "filename": r"Source/WebCore/page/(Content|DOM)SecurityPolicy\."
- r"|LayoutTests/http/tests/security/contentSecurityPolicy"
- },
- "RegionsDevelopment": {
- "filename": r"Source/WebCore/rendering/RenderRegion\.(h|cpp)"
- r"|Source/WebCore/rendering/RenderFlowThread\.(h|cpp)"
- r"|Source/WebCore/rendering/FlowThreadController\.(h|cpp)"
- r"|Source/WebCore/rendering/RenderRegionSet\.(h|cpp)"
- r"|Source/WebCore/rendering/RenderNamedFlowThread\.(h|cpp)"
- r"|Source/WebCore/rendering/RenderBoxRegionInfo\.h"
- r"|Source/WebCore/dom/WebKitNamedFlow\.(h|cpp|idl)"
- r"|Source/WebCore/dom/(DOM)?NamedFlowCollection\.(h|cpp|idl)"
- r"|Source/WebCore/css/WebKitCSSRegionRule\.(h|cpp|idl)"
- r"|LayoutTests/fast/regions",
- },
- "RegionsExpectationsMore": {
- "filename": r"LayoutTests/platform/.*TestExpectations",
- "more": r"fast/regions/.*\.html",
- },
- "RegionsExpectationsLess": {
- "filename": r"LayoutTests/platform/.*TestExpectations",
- "less": r"fast/regions/.*\.html",
- },
- "RegionsUsage": {
- "more": r"(RenderRegion|RenderFlowThread|RenderNamedFlowThread)(?!\.(h|cpp))",
- },
- "IndexedDB": {
- "filename": r"Source/WebCore/Modules/indexeddb"
- r"|Source/WebCore/bindings/.*IDB.*\.(h|cpp)"
- r"|Source/WebCore/bindings/.*SerializedScriptValue.*\.(h|cpp)"
- r"|Source/WebKit/chromium/.*IDB.*\.(h|cpp)"
- r"|Source/WebCore/platform/leveldb"
- r"|LayoutTests/storage/indexeddb"
- r"|LayoutTests/platform/.*/storage/indexeddb",
- },
- },
- "CC_RULES": {
- # Note: All email addresses listed must be registered with bugzilla.
- # Specifically, levin@chromium.org and levin+threading@chromium.org are
- # two different accounts as far as bugzilla is concerned.
- "Accessibility": [ "cfleizach@apple.com", "dmazzoni@google.com", "apinheiro@igalia.com", "jdiggs@igalia.com", "aboxhall@chromium.org" ],
- "Animation" : [ "simon.fraser@apple.com", "dino@apple.com", "dstockwell@chromium.org" ],
- "AppleMacPublicApi": [ "timothy@apple.com" ],
- "Battery": [ "gyuyoung.kim@samsung.com" ],
- "BlackBerry": [ "mifenton@rim.com", "rwlbuis@gmail.com", "tonikitoo@webkit.org" ],
- "Cairo": [ "dominik.rottsches@intel.com" ],
- "CMake": [ "rakuco@webkit.org", "gyuyoung.kim@samsung.com" ],
- "CoordinatedGraphics" : [ "noam@webkit.org", "zeno@webkit.org", "cmarcelo@webkit.org", "luiz@webkit.org" ],
- "ConsoleUsage" : [ "mkwst+watchlist@chromium.org" ],
- "ContentSecurityPolicyFiles|ContentSecurityPolicyUsage" : [ "mkwst+watchlist@chromium.org" ],
- "CoreAnimation" : [ "simon.fraser@apple.com" ],
- "CSS": [ "alexis@webkit.org", "macpherson@chromium.org", "ojan.autocc@gmail.com", "esprehn+autocc@chromium.org"],
- "ChromiumGraphics": [ "jamesr@chromium.org", "cc-bugs@chromium.org" ],
- "ChromiumPublicApi": [ "abarth@webkit.org", "dglazkov@chromium.org", "fishd@chromium.org", "jamesr@chromium.org", "tkent+wkapi@chromium.org" ],
- "ChromiumTestRunner": [ "jochen@chromium.org" ],
- "DOM": [ "ojan.autocc@gmail.com", "esprehn+autocc@chromium.org" ],
- "DOMAttributes": [ "cmarcelo@webkit.org", ],
- "EFL": [ "rakuco@webkit.org", "gyuyoung.kim@samsung.com" ],
- "EFLWebKit2PlatformSpecific": [ "gyuyoung.kim@samsung.com", "rakuco@webkit.org" ],
- "EFLWebKit2PublicAPI": [ "gyuyoung.kim@samsung.com", "rakuco@webkit.org" ],
- "Editing": [ "mifenton@rim.com" ],
- "Filters": [ "dino@apple.com" ],
- "Forms": [ "tkent@chromium.org", "mifenton@rim.com" ],
- "FrameLoader": [ "abarth@webkit.org", "japhet@chromium.org" ],
- "Geolocation": [ "benjamin@webkit.org" ],
- "GraphicsLayer": [ "simon.fraser@apple.com" ],
- "GStreamerGraphics": [ "alexis@webkit.org", "pnormand@igalia.com", "gns@gnome.org", "mrobinson@webkit.org" ],
- "GStreamerAudio": [ "pnormand@igalia.com" ],
- "GtkBuildSystem": [ "zandobersek@gmail.com" ],
- "GtkWebKit2PublicAPI": [ "cgarcia@igalia.com", "gns@gnome.org", "mrobinson@webkit.org" ],
- "Harfbuzz": [ "dominik.rottsches@intel.com" ],
- "HTML": [ "ojan.autocc@gmail.com", "esprehn+autocc@chromium.org" ],
- "IndexedDB": [ "alecflett@chromium.org", "dgrogan@chromium.org", "jsbell@chromium.org" ],
- "Loader": [ "japhet@chromium.org" ],
- "MathML": [ "dbarton@mathscribe.com" ],
- "Media": [ "feature-media-reviews@chromium.org", "eric.carlson@apple.com", "jer.noble@apple.com" ],
- "MediaStream": [ "tommyw@google.com", "hta@google.com" ],
- "NetworkInfo": [ "gyuyoung.kim@samsung.com" ],
- "OpenGL" : [ "noam@webkit.org", "dino@apple.com" ],
- "PerformanceTests": [ "rniwa@webkit.org" ],
- "QtBuildSystem" : [ "vestbo@webkit.org", "abecsi@webkit.org" ],
- "QtGraphics" : [ "noam@webkit.org" ],
- "QtWebKit2PlatformSpecific": [ "alexis@webkit.org", "cmarcelo@webkit.org", "abecsi@webkit.org" ],
- "QtWebKit2PublicAPI": [ "alexis@webkit.org", "cmarcelo@webkit.org", "abecsi@webkit.org" ],
- "RegionsDevelopment|RegionsExpectationsMore|RegionsExpectationsLess|RegionsUsage": [ "WebkitBugTracker@adobe.com" ],
- "Rendering": [ "eric@webkit.org", "ojan.autocc@gmail.com", "esprehn+autocc@chromium.org" ],
- "RenderLayers" : [ "simon.fraser@apple.com" ],
- "SVG": ["schenney@chromium.org", "pdr@google.com", "fmalita@chromium.org", "dominik.rottsches@intel.com" ],
- "SVNScripts": [ "dbates@webkit.org" ],
- "ScrollingCoordinator": [ "andersca@apple.com", "jamesr@chromium.org", "tonikitoo@webkit.org", "cmarcelo@webkit.org", "luiz@webkit.org" ],
- "SecurityCritical": [ "abarth@webkit.org" ],
- "SkiaGraphics": [ "senorblanco@chromium.org", "junov@google.com" ],
- "Selectors": [ "allan.jensen@digia.com" ],
- "SoupNetwork": [ "rakuco@webkit.org", "gns@gnome.org", "mrobinson@webkit.org", "danw@gnome.org" ],
- "StyleChecker": [ "levin@chromium.org", ],
- "TestFailures": [ "abarth@webkit.org", "dglazkov@chromium.org", "ojan.autocc@gmail.com" ],
- "TextureMapper" : [ "noam@webkit.org", "cmarcelo@webkit.org", "luiz@webkit.org" ],
- "ThreadingFiles|ThreadingUsage": [ "levin+threading@chromium.org", ],
- "TouchAdjustment" : [ "allan.jensen@digia.com" ],
- "V8Bindings|BindingsScripts": [ "abarth@webkit.org", "japhet@chromium.org", "haraken@chromium.org" ],
- "WTF": [ "benjamin@webkit.org", "ojan.autocc@gmail.com", "cmarcelo@webkit.org" ],
- "WatchListScript": [ "levin+watchlist@chromium.org", ],
- "WebGL": [ "dino@apple.com" ],
- "WebIDL": [ "abarth@webkit.org", "ojan.autocc@gmail.com", "esprehn+autocc@chromium.org" ],
- "WebInspectorAPI": [ "timothy@apple.com", "joepeck@webkit.org", "graouts@apple.com" ],
- "WebKitGTKTranslations": [ "gns@gnome.org", "mrobinson@webkit.org" ],
- "WebSocket": [ "yutak@chromium.org", "toyoshim+watchlist@chromium.org" ],
- "XSS": [ "dbates@webkit.org", "ojan.autocc@gmail.com" ],
- "webkitperl": [ "dbates@webkit.org" ],
- "webkitpy": [ "abarth@webkit.org", "dpranke@chromium.org", "eric@webkit.org" ],
- },
- "MESSAGE_RULES": {
- "ChromiumPublicApi": [ "Please wait for approval from abarth@webkit.org, dglazkov@chromium.org, "
- "fishd@chromium.org, jamesr@chromium.org or tkent@chromium.org before "
- "submitting, as this patch contains changes to the Chromium public API. "
- "See also https://trac.webkit.org/wiki/ChromiumWebKitAPI." ],
- "AppleMacPublicApi": [ "Please wait for approval from timothy@apple.com (or another member "
- "of the Apple Safari Team) before submitting "
- "because this patch contains changes to the Apple Mac "
- "WebKit.framework public API.", ],
- "GtkWebKit2PublicAPI": [ "Thanks for the patch. If this patch contains new public API "
- "please make sure it follows the guidelines for new WebKit2 GTK+ API. "
- "See http://trac.webkit.org/wiki/WebKitGTK/AddingNewWebKit2API", ],
- },
-}
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/host.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/host.py
index a8df7a5f929..7671ea6201c 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/host.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/host.py
@@ -31,7 +31,6 @@ import logging
import os
import sys
-from webkitpy.common.checkout import Checkout
from webkitpy.common.checkout.scm.detection import SCMDetector
from webkitpy.common.memoized import memoized
from webkitpy.common.net import buildbot, web
@@ -48,9 +47,7 @@ class Host(SystemHost):
SystemHost.__init__(self)
self.web = web.Web()
- # FIXME: Checkout should own the scm object.
self._scm = None
- self._checkout = None
# Everything below this line is WebKit-specific and belongs on a higher-level object.
self.buildbot = buildbot.BuildBot()
@@ -128,14 +125,10 @@ class Host(SystemHost):
self._engage_awesome_windows_hacks()
detector = SCMDetector(self.filesystem, self.executive)
self._scm = detector.default_scm(patch_directories)
- self._checkout = Checkout(self.scm())
def scm(self):
return self._scm
- def checkout(self):
- return self._checkout
-
def buildbot_for_builder_name(self, name):
if self.port_factory.get_from_builder_name(name).is_chromium():
return self.chromium_buildbot()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/host_mock.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/host_mock.py
index c18a3effda9..82738bfdc82 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/host_mock.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/host_mock.py
@@ -26,7 +26,6 @@
# (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.checkout_mock import MockCheckout
from webkitpy.common.checkout.scm.scm_mock import MockSCM
from webkitpy.common.net.buildbot.buildbot_mock import MockBuildBot
from webkitpy.common.net.web_mock import MockWeb
@@ -43,7 +42,6 @@ class MockHost(MockSystemHost):
add_unit_tests_to_mock_filesystem(self.filesystem)
self.web = web or MockWeb()
- self._checkout = MockCheckout()
self._scm = None
# FIXME: we should never initialize the SCM by default, since the real
# object doesn't either. This has caused at least one bug (see bug 89498).
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/message_pool.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/message_pool.py
index 03056cf64fe..2e8eb7db961 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/message_pool.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/message_pool.py
@@ -56,17 +56,16 @@ from webkitpy.common.system import stack_utils
_log = logging.getLogger(__name__)
-def get(caller, worker_factory, num_workers, worker_startup_delay_secs=0.0, host=None):
+def get(caller, worker_factory, num_workers, host=None):
"""Returns an object that exposes a run() method that takes a list of test shards and runs them in parallel."""
- return _MessagePool(caller, worker_factory, num_workers, worker_startup_delay_secs, host)
+ return _MessagePool(caller, worker_factory, num_workers, host)
class _MessagePool(object):
- def __init__(self, caller, worker_factory, num_workers, worker_startup_delay_secs=0.0, host=None):
+ def __init__(self, caller, worker_factory, num_workers, host=None):
self._caller = caller
self._worker_factory = worker_factory
self._num_workers = num_workers
- self._worker_startup_delay_secs = worker_startup_delay_secs
self._workers = []
self._workers_stopped = set()
self._host = host
@@ -107,8 +106,6 @@ class _MessagePool(object):
worker = _Worker(host, self._messages_to_manager, self._messages_to_worker, self._worker_factory, worker_number, self._running_inline, self if self._running_inline else None, self._worker_log_level())
self._workers.append(worker)
worker.start()
- if self._worker_startup_delay_secs:
- time.sleep(self._worker_startup_delay_secs)
def _worker_log_level(self):
log_level = logging.NOTSET
@@ -209,6 +206,7 @@ class _Worker(multiprocessing.Process):
self.name = 'worker/%d' % worker_number
self.log_messages = []
self.log_level = log_level
+ self._running = False
self._running_inline = running_inline
self._manager = manager
@@ -245,11 +243,12 @@ class _Worker(multiprocessing.Process):
worker = self._worker
exception_msg = ""
_log.debug("%s starting" % self.name)
+ self._running = True
try:
if hasattr(worker, 'start'):
worker.start()
- while True:
+ while self._running:
message = self._messages_to_worker.get()
if message.from_user:
worker.handle(message.name, message.src, *message.args)
@@ -273,6 +272,9 @@ class _Worker(multiprocessing.Process):
self._post(name='done', args=(), from_user=False)
self._close()
+ def stop_running(self):
+ self._running = False
+
def post(self, name, *args):
self._post(name, args, from_user=True)
self._yield_to_manager()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/credentials.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/credentials.py
deleted file mode 100644
index 7038b7e3cdb..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/credentials.py
+++ /dev/null
@@ -1,163 +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 logging
-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
-
-try:
- # Use keyring, a cross platform keyring interface, as a fallback:
- # http://pypi.python.org/pypi/keyring
- import keyring
-except ImportError:
- keyring = None
-
-_log = logging.getLogger(__name__)
-
-
-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.info("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.error("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
- try:
- self._keyring.set_password(self.host, username, password)
- except:
- pass
-
- def read_credentials(self, user=User):
- 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 not username:
- username = user.prompt("%s login: " % self.host)
-
- if username and not password and self._keyring:
- try:
- password = self._keyring.get_password(self.host, username)
- except:
- pass
-
- if not password:
- password = user.prompt_password("%s password for %s: " % (self.host, username))
- self._offer_to_store_credentials_in_keyring(username, password)
-
- return (username, password)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/credentials_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/credentials_unittest.py
deleted file mode 100644
index 06999077b8a..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/net/credentials_unittest.py
+++ /dev/null
@@ -1,208 +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 tempfile
-import webkitpy.thirdparty.unittest2 as unittest
-from webkitpy.common.net.credentials import Credentials
-from webkitpy.common.system.executive import Executive
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.common.system.user_mock import MockUser
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.tool.mocktool import MockOptions
-from webkitpy.common.system.executive_mock import MockExecutive
-
-
-# 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)
-
-
-# Note: All tests should use this class instead of Credentials directly to avoid using a real Executive.
-class MockedCredentials(Credentials):
- def __init__(self, *args, **kwargs):
- if 'executive' not in kwargs:
- kwargs['executive'] = MockExecutive()
- Credentials.__init__(self, *args, **kwargs)
-
-
-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(MockedCredentials):
- def _is_mac_os_x(self):
- return False
- credentials = FakeCredentials("bugs.webkit.org")
- self.assertFalse(credentials._is_mac_os_x())
- self.assertEqual(credentials._credentials_from_keychain("foo"), ["foo", None])
-
- def test_security_output_parse(self):
- credentials = MockedCredentials("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):
- # FIXME: This test won't work if the user has a credential for foo.example.com!
- 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.assertIsNone(credentials._run_security_tool())
- outputCapture.restore_output()
-
- def _assert_security_call(self, username=None):
- executive_mock = Mock()
- credentials = MockedCredentials("example.com", executive=executive_mock)
-
- expected_logs = "Reading Keychain for example.com account and password. Click \"Allow\" to continue...\n"
- OutputCapture().assert_outputs(self, credentials._run_security_tool, [username], expected_logs=expected_logs)
-
- 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):
- credentials = MockedCredentials("example.com")
-
- saved_environ = os.environ.copy()
- os.environ['WEBKIT_BUGZILLA_USERNAME'] = "foo"
- os.environ['WEBKIT_BUGZILLA_PASSWORD'] = "bar"
- username, password = credentials._credentials_from_environment()
- self.assertEqual(username, "foo")
- self.assertEqual(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(MockedCredentials):
- 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(MockedCredentials):
- 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"))
-
- def test_keyring_without_git_repo_nor_keychain(self):
- class MockKeyring(object):
- def get_password(self, host, username):
- return "NOMNOMNOM"
-
- class FakeCredentials(MockedCredentials):
- def _credentials_from_keychain(self, username):
- return (None, None)
-
- def _credentials_from_environment(self):
- return (None, None)
-
- class FakeUser(MockUser):
- @classmethod
- def prompt(cls, message, repeat=1, raw_input=raw_input):
- return "test@webkit.org"
-
- @classmethod
- def prompt_password(cls, message, repeat=1, raw_input=raw_input):
- raise AssertionError("should not prompt for password")
-
- 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(FakeUser), ("test@webkit.org", "NOMNOMNOM"))
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem.py
index 3786c6fed22..fdf43473b13 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem.py
@@ -131,6 +131,9 @@ class FileSystem(object):
def listdir(self, path):
return os.listdir(path)
+ def walk(self, top):
+ return os.walk(top)
+
def mkdtemp(self, **kwargs):
"""Create and return a uniquely named directory.
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_mock.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
index 91a7df40b03..70dfff3fa64 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
@@ -96,7 +96,7 @@ class MockFileSystem(object):
return home_directory + self.sep + parts[1]
def path_to_module(self, module_name):
- return "/mock-checkout/Tools/Scripts/" + module_name.replace('.', '/') + ".py"
+ return "/mock-checkout/third_party/WebKit/Tools/Scripts/" + module_name.replace('.', '/') + ".py"
def chdir(self, path):
path = self.normpath(path)
@@ -201,25 +201,29 @@ class MockFileSystem(object):
return path
def listdir(self, path):
+ root, dirs, files = list(self.walk(path))[0]
+ return dirs + files
+
+ def walk(self, top):
sep = self.sep
- if not self.isdir(path):
- raise OSError("%s is not a directory" % path)
+ if not self.isdir(top):
+ raise OSError("%s is not a directory" % top)
- if not path.endswith(sep):
- path += sep
+ if not top.endswith(sep):
+ top += sep
dirs = []
files = []
for f in self.files:
- if self.exists(f) and f.startswith(path):
- remaining = f[len(path):]
+ if self.exists(f) and f.startswith(top):
+ remaining = f[len(top):]
if sep in remaining:
dir = remaining[:remaining.index(sep)]
if not dir in dirs:
dirs.append(dir)
else:
files.append(remaining)
- return dirs + files
+ return [(top[:-1], dirs, files)]
def mtime(self, path):
if self.exists(path):
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
index e57328470ee..4ad488baa2d 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
@@ -183,6 +183,15 @@ class RealFileSystemTest(unittest.TestCase, GenericFileSystemTests):
self.assertEqual(fs.listdir(d), ['foo'])
os.remove(new_file)
+ def test_walk(self):
+ fs = FileSystem()
+ with fs.mkdtemp(prefix='filesystem_unittest_') as d:
+ self.assertEqual(list(fs.walk(d)), [(d, [], [])])
+ new_file = os.path.join(d, 'foo')
+ fs.write_text_file(new_file, u'foo')
+ self.assertEqual(list(fs.walk(d)), [(d, [], ['foo'])])
+ os.remove(new_file)
+
def test_maybe_make_directory__success(self):
fs = FileSystem()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo.py
index b2c602bed35..a0c8dc347bb 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo.py
@@ -131,12 +131,13 @@ class PlatformInfo(object):
raise AssertionError('unrecognized platform string "%s"' % sys_platform)
def _determine_mac_version(self, mac_version_string):
- release_version = mac_version_string.split('.')[1]
+ release_version = int(mac_version_string.split('.')[1])
version_strings = {
- '5': 'leopard',
- '6': 'snowleopard',
- '7': 'lion',
- '8': 'mountainlion',
+ 5: 'leopard',
+ 6: 'snowleopard',
+ 7: 'lion',
+ 8: 'mountainlion',
+ 9: 'mavericks',
}
assert release_version >= min(version_strings.keys())
return version_strings.get(release_version, 'future')
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo_unittest.py
index 58fbde754ef..24fa35210a6 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/system/platforminfo_unittest.py
@@ -135,7 +135,8 @@ class TestPlatformInfo(unittest.TestCase):
self.assertEqual(self.make_info(fake_sys('darwin'), fake_platform('10.6.1')).os_version, 'snowleopard')
self.assertEqual(self.make_info(fake_sys('darwin'), fake_platform('10.7.1')).os_version, 'lion')
self.assertEqual(self.make_info(fake_sys('darwin'), fake_platform('10.8.1')).os_version, 'mountainlion')
- self.assertEqual(self.make_info(fake_sys('darwin'), fake_platform('10.9.0')).os_version, 'future')
+ self.assertEqual(self.make_info(fake_sys('darwin'), fake_platform('10.9.0')).os_version, 'mavericks')
+ self.assertEqual(self.make_info(fake_sys('darwin'), fake_platform('10.10.0')).os_version, 'future')
self.assertEqual(self.make_info(fake_sys('linux2')).os_version, 'lucid')
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/webkit_finder.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/webkit_finder.py
index d94bf6d4118..f267f0dae3a 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/webkit_finder.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/webkit_finder.py
@@ -37,6 +37,7 @@ class WebKitFinder(object):
self._sys_path = sys.path
self._env_path = os.environ['PATH'].split(os.pathsep)
self._webkit_base = None
+ self._chromium_base = None
self._depot_tools = None
def webkit_base(self):
@@ -56,9 +57,17 @@ class WebKitFinder(object):
self._webkit_base = self._filesystem.normpath(module_path[0:tools_index - 1])
return self._webkit_base
+ def chromium_base(self):
+ if not self._chromium_base:
+ self._chromium_base = self._filesystem.dirname(self._filesystem.dirname(self.webkit_base()))
+ return self._chromium_base
+
def path_from_webkit_base(self, *comps):
return self._filesystem.join(self.webkit_base(), *comps)
+ def path_from_chromium_base(self, *comps):
+ return self._filesystem.join(self.chromium_base(), *comps)
+
def path_to_script(self, script_name):
"""Returns the relative path to the script from the top of the WebKit tree."""
# This is intentionally relative in order to force callers to consider what
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/bisect_test_ordering.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/bisect_test_ordering.py
new file mode 100644
index 00000000000..a10ed15fa9a
--- /dev/null
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/bisect_test_ordering.py
@@ -0,0 +1,170 @@
+# Copyright (C) 2013 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (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 math
+import optparse
+import os
+import subprocess
+import sys
+
+from webkitpy.common.system.executive import Executive
+from webkitpy.common.system.filesystem import FileSystem
+from webkitpy.common.webkit_finder import WebKitFinder
+
+_log = logging.getLogger(__name__)
+
+
+class Bucket(object):
+ def __init__(self, tests):
+ self.tests = tests
+
+ def size(self):
+ return len(self.tests)
+
+
+class Bisector(object):
+
+ def __init__(self, tests, is_debug):
+ self.executive = Executive()
+ self.tests = tests
+ self.expected_failure = tests[-1]
+ self.is_debug = is_debug
+ self.webkit_finder = WebKitFinder(FileSystem())
+
+ def bisect(self):
+ if self.test_fails_in_isolation():
+ self.buckets = [Bucket([self.expected_failure])]
+ print '%s fails when run in isolation.' % self.expected_failure
+ self.print_result()
+ return 0
+ if not self.test_fails(self.tests):
+ _log.error('%s does not fail' % self.expected_failure)
+ return 1
+ # Split the list of test into buckets. Each bucket has at least one test required to cause
+ # the expected failure at the end. Split buckets in half until there are only buckets left
+ # with one item in them.
+ self.buckets = [Bucket(self.tests[:-1]), Bucket([self.expected_failure])]
+ while not self.is_done():
+ self.print_progress()
+ self.split_largest_bucket()
+ self.print_result()
+ self.verify_non_flaky()
+ return 0
+
+ def test_fails_in_isolation(self):
+ return self.test_bucket_list_fails([Bucket([self.expected_failure])])
+
+ def verify_non_flaky(self):
+ print 'Verifying the failure is not flaky by running 10 times.'
+ count_failures = 0
+ for i in range(0, 10):
+ if self.test_bucket_list_fails(self.buckets):
+ count_failures += 1
+ print 'Failed %d/10 times' % count_failures
+
+ def print_progress(self):
+ count = 0
+ for bucket in self.buckets:
+ count += len(bucket.tests)
+ print '%d tests left, %d buckets' % (count, len(self.buckets))
+
+ def print_result(self):
+ tests = []
+ for bucket in self.buckets:
+ tests += bucket.tests
+ extra_args = ' --debug' if self.is_debug else ''
+ print 'run-webkit-tests%s --child-processes=1 --order=none %s' % (extra_args, " ".join(tests))
+
+ def is_done(self):
+ for bucket in self.buckets:
+ if bucket.size() > 1:
+ return False
+ return True
+
+ def split_largest_bucket(self):
+ index = 0
+ largest_index = 0
+ largest_size = 0
+ for bucket in self.buckets:
+ if bucket.size() > largest_size:
+ largest_index = index
+ largest_size = bucket.size()
+ index += 1
+
+ bucket_to_split = self.buckets[largest_index]
+ halfway_point = int(largest_size / 2)
+ first_half = Bucket(bucket_to_split.tests[:halfway_point])
+ second_half = Bucket(bucket_to_split.tests[halfway_point:])
+
+ buckets_before = self.buckets[:largest_index]
+ buckets_after = self.buckets[largest_index + 1:]
+
+ # Do the second half first because it tends to be faster because the http tests are front-loaded and slow.
+ new_buckets = buckets_before + [second_half] + buckets_after
+ if self.test_bucket_list_fails(new_buckets):
+ self.buckets = new_buckets
+ return
+
+ new_buckets = buckets_before + [first_half] + buckets_after
+ if self.test_bucket_list_fails(new_buckets):
+ self.buckets = new_buckets
+ return
+
+ self.buckets = buckets_before + [first_half, second_half] + buckets_after
+
+ def test_bucket_list_fails(self, buckets):
+ tests = []
+ for bucket in buckets:
+ tests += bucket.tests
+ return self.test_fails(tests)
+
+ def test_fails(self, tests):
+ extra_args = ['--debug'] if self.is_debug else []
+ path_to_run_webkit_tests = self.webkit_finder.path_from_webkit_base('Tools', 'Scripts', 'run-webkit-tests')
+ output = self.executive.popen([path_to_run_webkit_tests, '--child-processes', '1', '--order', 'none', '--no-retry', '--no-show-results', '--verbose'] + extra_args + tests, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ failure_string = self.expected_failure + ' failed'
+ if failure_string in output.stderr.read():
+ return True
+ return False
+
+
+def main(argv):
+ logging.basicConfig()
+
+ option_parser = optparse.OptionParser()
+ option_parser.add_option('--test-list', action='store', help='file that list tests to bisect. The last test in the list is the expected failure.', metavar='FILE'),
+ option_parser.add_option('--debug', action='store_true', default=False, help='whether to use a debug build'),
+ options, args = option_parser.parse_args(argv)
+
+ tests = open(options.test_list).read().strip().split('\n')
+ bisector = Bisector(tests, is_debug=options.debug)
+ return bisector.bisect()
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/__init__.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/__init__.py
new file mode 100644
index 00000000000..ef65bee5bb7
--- /dev/null
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/__init__.py
@@ -0,0 +1 @@
+# Required for Python to search this directory for module files
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader.py
new file mode 100644
index 00000000000..0728d8abb06
--- /dev/null
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader.py
@@ -0,0 +1,97 @@
+# Copyright (C) 2013 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (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__)
+
+
+class DumpReader(object):
+ """Base class for breakpad dump readers."""
+
+ def __init__(self, host, build_dir):
+ self._host = host
+ self._build_dir = build_dir
+
+ def check_is_functional(self):
+ """This routine must be implemented by subclasses.
+
+ Returns True if this reader is functional."""
+ raise NotImplementedError()
+
+ def crash_dumps_directory(self):
+ return self._host.filesystem.join(self._build_dir, 'crash-dumps')
+
+ def clobber_old_results(self):
+ if self._host.filesystem.isdir(self.crash_dumps_directory()):
+ self._host.filesystem.rmtree(self.crash_dumps_directory())
+
+ def look_for_new_crash_logs(self, crashed_processes, start_time):
+ if not crashed_processes:
+ return None
+
+ if not self.check_is_functional():
+ return None
+
+ pid_to_minidump = dict()
+ for root, dirs, files in self._host.filesystem.walk(self.crash_dumps_directory()):
+ for dmp in [f for f in files if f.endswith(self._file_extension())]:
+ dmp_file = self._host.filesystem.join(root, dmp)
+ if self._host.filesystem.mtime(dmp_file) < start_time:
+ continue
+ pid = self._get_pid_from_dump(dmp_file)
+ if pid:
+ pid_to_minidump[pid] = dmp_file
+
+ result = dict()
+ for test, process_name, pid in crashed_processes:
+ if str(pid) in pid_to_minidump:
+ stack = self._get_stack_from_dump(pid_to_minidump[str(pid)])
+ if stack:
+ result[test] = stack
+
+ return result
+
+ def _get_pid_from_dump(self, dump_file):
+ """This routine must be implemented by subclasses.
+
+ This routine returns the PID of the crashed process that produced the given dump_file."""
+ raise NotImplementedError()
+
+ def _get_stack_from_dump(self, dump_file):
+ """This routine must be implemented by subclasses.
+
+ Returns the stack stored in the given breakpad dump_file."""
+ raise NotImplementedError()
+
+ def _file_extension(self):
+ """This routine must be implemented by subclasses.
+
+ Returns the file extension of crash dumps written by breakpad."""
+ raise NotImplementedError()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_multipart.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_multipart.py
new file mode 100644
index 00000000000..e0148df4947
--- /dev/null
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_multipart.py
@@ -0,0 +1,172 @@
+# Copyright (C) 2013 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (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 cgi
+import logging
+
+from webkitpy.common.webkit_finder import WebKitFinder
+from webkitpy.layout_tests.breakpad.dump_reader import DumpReader
+
+
+_log = logging.getLogger(__name__)
+
+
+class DumpReaderMultipart(DumpReader):
+ """Base class for Linux and Android breakpad dump reader."""
+
+ def __init__(self, host, build_dir):
+ super(DumpReaderMultipart, self).__init__(host, build_dir)
+ self._webkit_finder = WebKitFinder(host.filesystem)
+ self._breakpad_tools_available = None
+
+ def check_is_functional(self):
+ return self._check_breakpad_tools_available()
+
+ def _get_pid_from_dump(self, dump_file):
+ dump = self._read_dump(dump_file)
+ if not dump:
+ return None
+ if 'pid' in dump:
+ return dump['pid'][0]
+ return None
+
+ def _get_stack_from_dump(self, dump_file):
+ dump = self._read_dump(dump_file)
+ if not dump:
+ return None
+ if not 'upload_file_minidump' in dump:
+ return None
+
+ self._generate_breakpad_symbols_if_necessary()
+ f, temp_name = self._host.filesystem.open_binary_tempfile('dmp')
+ f.write("\r\n".join(dump['upload_file_minidump']))
+ f.close()
+
+ cmd = [self._path_to_minidump_stackwalk(), temp_name, self._symbols_dir()]
+ try:
+ stack = self._host.executive.run_command(cmd, return_stderr=False)
+ except:
+ _log.warning('Failed to execute "%s"' % ' '.join(cmd))
+ stack = None
+ finally:
+ self._host.filesystem.remove(temp_name)
+ return stack
+
+ def _read_dump(self, dump_file):
+ with self._host.filesystem.open_binary_file_for_reading(dump_file) as f:
+ boundary = f.readline().strip()[2:]
+ f.seek(0)
+ data = cgi.parse_multipart(f, {'boundary': boundary})
+ return data
+ return None
+
+ def _check_breakpad_tools_available(self):
+ if self._breakpad_tools_available != None:
+ return self._breakpad_tools_available
+
+ REQUIRED_BREAKPAD_TOOLS = [
+ 'dump_syms',
+ 'minidump_stackwalk',
+ ]
+ result = True
+ for binary in REQUIRED_BREAKPAD_TOOLS:
+ full_path = self._host.filesystem.join(self._build_dir, binary)
+ if not self._host.filesystem.exists(full_path):
+ result = False
+ _log.error('Unable to find %s' % binary)
+ _log.error(' at %s' % full_path)
+
+ if not result:
+ _log.error(" Could not find breakpad tools, unexpected crashes won't be symbolized")
+ _log.error(' Did you build the target blink_tests?')
+ _log.error('')
+
+ self._breakpad_tools_available = result
+ return self._breakpad_tools_available
+
+ def _path_to_minidump_stackwalk(self):
+ return self._host.filesystem.join(self._build_dir, "minidump_stackwalk")
+
+ def _path_to_generate_breakpad_symbols(self):
+ return self._webkit_finder.path_from_chromium_base("components", "breakpad", "tools", "generate_breakpad_symbols.py")
+
+ def _symbols_dir(self):
+ return self._host.filesystem.join(self._build_dir, 'content_shell.syms')
+
+ def _generate_breakpad_symbols_if_necessary(self):
+ if self._host.filesystem.exists(self._symbols_dir()):
+ needs_update = False
+ symbols_mtime = self._host.filesystem.mtime(self._symbols_dir())
+ for binary in self._binaries_to_symbolize():
+ full_path = self._host.filesystem.join(self._build_dir, binary)
+ if self._host.filesystem.mtime(full_path) >= symbols_mtime:
+ needs_update = True
+ if not needs_update:
+ return
+
+ _log.debug("Regenerating breakpad symbols")
+ self._host.filesystem.rmtree(self._symbols_dir())
+
+ for binary in self._binaries_to_symbolize():
+ full_path = self._host.filesystem.join(self._build_dir, binary)
+ cmd = [
+ self._path_to_generate_breakpad_symbols(),
+ '--binary=%s' % full_path,
+ '--symbols-dir=%s' % self._symbols_dir(),
+ '--build-dir=%s' % self._build_dir,
+ ]
+ try:
+ self._host.executive.run_command(cmd)
+ except:
+ _log.error('Failed to execute "%s"' % ' '.join(cmd))
+
+ def _binaries_to_symbolize(self):
+ """This routine must be implemented by subclasses.
+
+ Returns an array of binaries that need to be symbolized."""
+ raise NotImplementedError()
+
+
+class DumpReaderLinux(DumpReaderMultipart):
+ """Linux breakpad dump reader."""
+
+ def _binaries_to_symbolize(self):
+ return ['content_shell', 'libTestNetscapePlugIn.so', 'libffmpegsumo.so', 'libosmesa.so']
+
+ def _file_extension(self):
+ return 'dmp'
+
+
+class DumpReaderAndroid(DumpReaderMultipart):
+ """Android breakpad dump reader."""
+
+ def _binaries_to_symbolize(self):
+ return ['lib/libcontent_shell_content_view.so']
+
+ def _file_extension(self):
+ return 'dmp'
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_multipart_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_multipart_unittest.py
new file mode 100644
index 00000000000..63fa0dece8f
--- /dev/null
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_multipart_unittest.py
@@ -0,0 +1,109 @@
+# Copyright (C) 2013 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (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.thirdparty.unittest2 as unittest
+
+import cgi
+
+from webkitpy.common.host_mock import MockHost
+from webkitpy.common.system.executive_mock import MockExecutive
+from webkitpy.layout_tests.breakpad.dump_reader_multipart import DumpReaderMultipart
+
+
+class TestDumpReaderMultipart(unittest.TestCase):
+ _MULTIPART_DUMP = [
+ '--boundary',
+ 'Content-Disposition: form-data; name="prod"',
+ '',
+ 'content_shell',
+ '--boundary',
+ 'Content-Disposition: form-data; name="pid"',
+ '',
+ '4711',
+ '--boundary',
+ 'Content-Disposition: form-data; name="upload_file_minidump"; filename="dump"',
+ 'Content-Type: application/octet-stream',
+ '',
+ 'MDMP',
+ '--boundary--',
+ ]
+
+ def test_check_is_functional_breakpad_tools_not_found(self):
+ host = MockHost()
+
+ build_dir = "/mock-checkout/out/Debug"
+ host.filesystem.maybe_make_directory(build_dir)
+ dump_reader = DumpReaderMultipart(host, build_dir)
+ dump_reader._file_extension = lambda: 'dmp'
+ dump_reader._binaries_to_symbolize = lambda: ['content_shell']
+
+ self.assertFalse(dump_reader.check_is_functional())
+
+ def test_get_pid_from_dump(self):
+ host = MockHost()
+
+ dump_file = '/crash-dumps/dump.dmp'
+ expected_pid = '4711'
+ host.filesystem.write_text_file(dump_file, "\r\n".join(TestDumpReaderMultipart._MULTIPART_DUMP))
+ build_dir = "/mock-checkout/out/Debug"
+ host.filesystem.maybe_make_directory(build_dir)
+ host.filesystem.exists = lambda x: True
+
+ # The mock file object returned by open_binary_file_for_reading doesn't
+ # have readline(), however, the real File object does.
+ host.filesystem.open_binary_file_for_reading = host.filesystem.open_text_file_for_reading
+ dump_reader = DumpReaderMultipart(host, build_dir)
+ dump_reader._file_extension = lambda: 'dmp'
+ dump_reader._binaries_to_symbolize = lambda: ['content_shell']
+
+ self.assertTrue(dump_reader.check_is_functional())
+ self.assertEqual(expected_pid, dump_reader._get_pid_from_dump(dump_file))
+
+ def test_get_stack_from_dump(self):
+ host = MockHost()
+
+ dump_file = '/crash-dumps/dump.dmp'
+ host.filesystem.write_text_file(dump_file, "\r\n".join(TestDumpReaderMultipart._MULTIPART_DUMP))
+ build_dir = "/mock-checkout/out/Debug"
+ host.filesystem.maybe_make_directory(build_dir)
+ host.filesystem.exists = lambda x: True
+
+ # The mock file object returned by open_binary_file_for_reading doesn't
+ # have readline(), however, the real File object does.
+ host.filesystem.open_binary_file_for_reading = host.filesystem.open_text_file_for_reading
+ dump_reader = DumpReaderMultipart(host, build_dir)
+ dump_reader._file_extension = lambda: 'dmp'
+ dump_reader._binaries_to_symbolize = lambda: ['content_shell']
+
+ self.assertTrue(dump_reader.check_is_functional())
+ self.assertEqual("MOCK output of child process", dump_reader._get_stack_from_dump(dump_file))
+ self.assertEqual(2, len(host.executive.calls))
+ cmd_line = " ".join(host.executive.calls[0])
+ self.assertIn('generate_breakpad_symbols.py', cmd_line)
+ cmd_line = " ".join(host.executive.calls[1])
+ self.assertIn('minidump_stackwalk', cmd_line)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_win.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_win.py
new file mode 100644
index 00000000000..6d193f34d39
--- /dev/null
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_win.py
@@ -0,0 +1,119 @@
+# Copyright (C) 2013 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import logging
+import os
+import shlex
+
+from webkitpy.layout_tests.breakpad.dump_reader import DumpReader
+
+
+_log = logging.getLogger(__name__)
+
+
+class DumpReaderWin(DumpReader):
+ """DumpReader for windows breakpad."""
+
+ def __init__(self, host, build_dir):
+ super(DumpReaderWin, self).__init__(host, build_dir)
+ self._cdb_available = None
+
+ def check_is_functional(self):
+ return self._check_cdb_available()
+
+ def _file_extension(self):
+ return 'txt'
+
+ def _get_pid_from_dump(self, dump_file):
+ with self._host.filesystem.open_text_file_for_reading(dump_file) as f:
+ crash_keys = dict([l.split(':', 1) for l in f.read().splitlines()])
+ if 'pid' in crash_keys:
+ return crash_keys['pid']
+ return None
+
+ def _get_stack_from_dump(self, dump_file):
+ minidump = dump_file[:-3] + 'dmp'
+ cmd = [self._cdb_path, '-y', self._build_dir, '-c', '.ecxr;k30;q', '-z', minidump]
+ try:
+ stack = self._host.executive.run_command(cmd)
+ except:
+ _log.warning('Failed to execute "%s"' % ' '.join(cmd))
+ else:
+ return stack
+ return None
+
+ def _check_cdb_available(self):
+ """Checks whether we can use cdb to symbolize minidumps."""
+ if self._cdb_available != None:
+ return self._cdb_available
+
+ CDB_LOCATION_TEMPLATES = [
+ '%s\\Debugging Tools For Windows',
+ '%s\\Debugging Tools For Windows (x86)',
+ '%s\\Debugging Tools For Windows (x64)',
+ '%s\\Windows Kits\\8.0\\Debuggers\\x86',
+ '%s\\Windows Kits\\8.0\\Debuggers\\x64',
+ ]
+
+ program_files_directories = ['C:\\Program Files']
+ program_files = os.environ.get('ProgramFiles')
+ if program_files:
+ program_files_directories.append(program_files)
+ program_files = os.environ.get('ProgramFiles(x86)')
+ if program_files:
+ program_files_directories.append(program_files)
+
+ possible_cdb_locations = []
+ for template in CDB_LOCATION_TEMPLATES:
+ for program_files in program_files_directories:
+ possible_cdb_locations.append(template % program_files)
+
+ gyp_defines = os.environ.get('GYP_DEFINES', [])
+ if gyp_defines:
+ gyp_defines = shlex.split(gyp_defines)
+ if 'windows_sdk_path' in gyp_defines:
+ possible_cdb_locations.append([
+ '%s\\Debuggers\\x86' % gyp_defines['windows_sdk_path'],
+ '%s\\Debuggers\\x64' % gyp_defines['windows_sdk_path'],
+ ])
+
+ for cdb_path in possible_cdb_locations:
+ cdb = self._host.filesystem.join(cdb_path, 'cdb.exe')
+ try:
+ _ = self._host.executive.run_command([cdb, '-version'])
+ except:
+ pass
+ else:
+ self._cdb_path = cdb
+ self._cdb_available = True
+ return self._cdb_available
+
+ _log.warning("CDB is not installed; can't symbolize minidumps.")
+ _log.warning('')
+ self._cdb_available = False
+ return self._cdb_available
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_win_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_win_unittest.py
new file mode 100644
index 00000000000..b36d7bf193e
--- /dev/null
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/breakpad/dump_reader_win_unittest.py
@@ -0,0 +1,77 @@
+# Copyright (C) 2013 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (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.thirdparty.unittest2 as unittest
+
+from webkitpy.common.host_mock import MockHost
+from webkitpy.common.system.executive_mock import MockExecutive
+from webkitpy.layout_tests.breakpad.dump_reader_win import DumpReaderWin
+
+
+class TestDumpReaderWin(unittest.TestCase):
+ def test_check_is_functional_cdb_not_found(self):
+ host = MockHost()
+ host.executive = MockExecutive(should_throw=True)
+
+ build_dir = "/mock-checkout/out/Debug"
+ host.filesystem.maybe_make_directory(build_dir)
+ dump_reader = DumpReaderWin(host, build_dir)
+
+ self.assertFalse(dump_reader.check_is_functional())
+
+ def test_get_pid_from_dump(self):
+ host = MockHost()
+
+ dump_file = '/crash-dumps/dump.txt'
+ expected_pid = '4711'
+ host.filesystem.write_text_file(dump_file, 'channel:\npid:%s\nplat:Win32\nprod:content_shell\n' % expected_pid)
+ build_dir = "/mock-checkout/out/Debug"
+ host.filesystem.maybe_make_directory(build_dir)
+ dump_reader = DumpReaderWin(host, build_dir)
+
+ self.assertTrue(dump_reader.check_is_functional())
+ self.assertEqual(expected_pid, dump_reader._get_pid_from_dump(dump_file))
+
+ def test_get_stack_from_dump(self):
+ host = MockHost()
+
+ dump_file = '/crash-dumps/dump.dmp'
+ real_dump_file = '/crash-dumps/dump.dmp'
+ host.filesystem.write_text_file(dump_file, 'product:content_shell\n')
+ host.filesystem.write_binary_file(real_dump_file, 'MDMP')
+ build_dir = "/mock-checkout/out/Debug"
+ host.filesystem.maybe_make_directory(build_dir)
+ dump_reader = DumpReaderWin(host, build_dir)
+
+ self.assertTrue(dump_reader.check_is_functional())
+ host.executive.calls = []
+ self.assertEqual("MOCK output of child process", dump_reader._get_stack_from_dump(dump_file))
+ self.assertEqual(1, len(host.executive.calls))
+ cmd_line = " ".join(host.executive.calls[0])
+ self.assertIn('cdb.exe', cmd_line)
+ self.assertIn(real_dump_file, cmd_line)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py
index d4a269a2846..de73218df88 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py
@@ -80,6 +80,7 @@ class LayoutTestRunner(object):
self._expectations = expectations
self._test_inputs = test_inputs
self._retrying = retrying
+ self._shards_to_redo = []
# FIXME: rename all variables to test_run_results or some such ...
run_results = TestRunResults(self._expectations, len(test_inputs) + len(tests_to_skip))
@@ -112,8 +113,14 @@ class LayoutTestRunner(object):
start_time = time.time()
try:
- with message_pool.get(self, self._worker_factory, num_workers, self._port.worker_startup_delay_secs(), self._port.host) as pool:
+ with message_pool.get(self, self._worker_factory, num_workers, self._port.host) as pool:
pool.run(('test_list', shard.name, shard.test_inputs) for shard in all_shards)
+
+ if self._shards_to_redo:
+ num_workers -= len(self._shards_to_redo)
+ if num_workers > 0:
+ with message_pool.get(self, self._worker_factory, num_workers, self._port.host) as pool:
+ pool.run(('test_list', shard.name, shard.test_inputs) for shard in self._shards_to_redo)
except TestRunInterruptedException, e:
_log.warning(e.reason)
run_results.interrupted = True
@@ -171,10 +178,12 @@ class LayoutTestRunner(object):
exp_str = self._expectations.get_expectations_string(result.test_name)
got_str = self._expectations.expectation_to_string(result.type)
- run_results.add(result, expected, self._test_is_slow(result.test_name))
+ if result.device_failed:
+ self._printer.print_finished_test(result, False, exp_str, "Aborted")
+ return
+ run_results.add(result, expected, self._test_is_slow(result.test_name))
self._printer.print_finished_test(result, expected, exp_str, got_str)
-
self._interrupt_if_at_failure_limits(run_results)
def handle(self, name, source, *args):
@@ -192,6 +201,10 @@ class LayoutTestRunner(object):
def _handle_finished_test(self, worker_name, result, log_messages=[]):
self._update_summary_with_result(self._current_run_results, result)
+ def _handle_device_failed(self, worker_name, list_name, remaining_tests):
+ _log.warning("%s has failed" % worker_name)
+ if remaining_tests:
+ self._shards_to_redo.append(TestShard(list_name, remaining_tests))
class Worker(object):
def __init__(self, caller, results_directory, options):
@@ -226,8 +239,13 @@ class Worker(object):
def handle(self, name, source, test_list_name, test_inputs):
assert name == 'test_list'
- for test_input in test_inputs:
- self._run_test(test_input, test_list_name)
+ for i, test_input in enumerate(test_inputs):
+ device_failed = self._run_test(test_input, test_list_name)
+ if device_failed:
+ self._caller.post('device_failed', test_list_name, test_inputs[i:])
+ self._caller.stop_running()
+ return
+
self._caller.post('finished_test_list', test_list_name)
def _update_test_input(self, test_input):
@@ -250,12 +268,20 @@ class Worker(object):
self._update_test_input(test_input)
test_timeout_sec = self._timeout(test_input)
start = time.time()
- self._caller.post('started_test', test_input, test_timeout_sec)
+ device_failed = False
if self._driver and self._driver.has_crashed():
self._kill_driver()
if not self._driver:
self._driver = self._port.create_driver(self._worker_number)
+
+ if not self._driver:
+ # FIXME: Is this the best way to handle a device crashing in the middle of the test, or should we create
+ # a new failure type?
+ device_failed = True
+ return device_failed
+
+ self._caller.post('started_test', test_input, test_timeout_sec)
result = single_test_runner.run_single_test(self._port, self._options, self._results_directory,
self._name, self._driver, test_input, stop_when_done)
@@ -264,10 +290,9 @@ class Worker(object):
result.total_run_time = time.time() - start
result.test_number = self._num_tests
self._num_tests += 1
-
self._caller.post('finished_test', result)
-
self._clean_up_after_test(test_input, result)
+ return result.device_failed
def stop(self):
_log.debug("%s cleaning up" % self._name)
@@ -352,7 +377,7 @@ class Sharder(object):
return self._shard_in_two(test_inputs)
elif fully_parallel:
return self._shard_every_file(test_inputs)
- return self._shard_by_directory(test_inputs, num_workers)
+ return self._shard_by_directory(test_inputs)
def _shard_in_two(self, test_inputs):
"""Returns two lists of shards, one with all the tests requiring a lock and one with the rest.
@@ -381,18 +406,30 @@ class Sharder(object):
This mode gets maximal parallelism at the cost of much higher flakiness."""
locked_shards = []
unlocked_shards = []
+ virtual_inputs = []
+
for test_input in test_inputs:
# Note that we use a '.' for the shard name; the name doesn't really
# matter, and the only other meaningful value would be the filename,
# which would be really redundant.
if test_input.requires_lock:
locked_shards.append(TestShard('.', [test_input]))
+ elif test_input.test_name.startswith('virtual'):
+ # This violates the spirit of sharding every file, but in practice, since the
+ # virtual test suites require a different commandline flag and thus a restart
+ # of content_shell, it's too slow to shard them fully.
+ virtual_inputs.append(test_input)
else:
unlocked_shards.append(TestShard('.', [test_input]))
- return locked_shards, unlocked_shards
+ locked_virtual_shards, unlocked_virtual_shards = self._shard_by_directory(virtual_inputs)
+
+ # The locked shards still need to be limited to self._max_locked_shards in order to not
+ # overload the http server for the http tests.
+ return (self._resize_shards(locked_virtual_shards + locked_shards, self._max_locked_shards, 'locked_shard'),
+ unlocked_virtual_shards + unlocked_shards)
- def _shard_by_directory(self, test_inputs, num_workers):
+ def _shard_by_directory(self, test_inputs):
"""Returns two lists of shards, each shard containing all the files in a directory.
This is the default mode, and gets as much parallelism as we can while
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner_unittest.py
index c43716a3563..b3156bbc445 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner_unittest.py
@@ -169,7 +169,8 @@ class SharderTests(unittest.TestCase):
"ietestcenter/Javascript/11.1.5_4-4-c-1.html",
"dom/html/level2/html/HTMLAnchorElement06.html",
"perf/object-keys.html",
- "virtual/threaded/test.html",
+ "virtual/threaded/dir/test.html",
+ "virtual/threaded/fast/foo/test.html",
]
def get_test_input(self, test_file):
@@ -203,7 +204,8 @@ class SharderTests(unittest.TestCase):
'http/tests/xmlhttprequest/supported-xml-content-types.html',
'perf/object-keys.html'])])
self.assert_shards(unlocked,
- [('virtual/threaded', ['virtual/threaded/test.html']),
+ [('virtual/threaded/dir', ['virtual/threaded/dir/test.html']),
+ ('virtual/threaded/fast/foo', ['virtual/threaded/fast/foo/test.html']),
('animations', ['animations/keyframes.html']),
('dom/html/level2/html', ['dom/html/level2/html/HTMLAnchorElement03.html',
'dom/html/level2/html/HTMLAnchorElement06.html']),
@@ -211,20 +213,23 @@ class SharderTests(unittest.TestCase):
('ietestcenter/Javascript', ['ietestcenter/Javascript/11.1.5_4-4-c-1.html'])])
def test_shard_every_file(self):
- locked, unlocked = self.get_shards(num_workers=2, fully_parallel=True)
+ locked, unlocked = self.get_shards(num_workers=2, fully_parallel=True, max_locked_shards=2)
self.assert_shards(locked,
- [('.', ['http/tests/websocket/tests/unicode.htm']),
- ('.', ['http/tests/security/view-source-no-refresh.html']),
- ('.', ['http/tests/websocket/tests/websocket-protocol-ignored.html']),
- ('.', ['http/tests/xmlhttprequest/supported-xml-content-types.html']),
- ('.', ['perf/object-keys.html'])]),
+ [('locked_shard_1',
+ ['http/tests/websocket/tests/unicode.htm',
+ 'http/tests/security/view-source-no-refresh.html',
+ 'http/tests/websocket/tests/websocket-protocol-ignored.html']),
+ ('locked_shard_2',
+ ['http/tests/xmlhttprequest/supported-xml-content-types.html',
+ 'perf/object-keys.html'])]),
self.assert_shards(unlocked,
- [('.', ['animations/keyframes.html']),
+ [('virtual/threaded/dir', ['virtual/threaded/dir/test.html']),
+ ('virtual/threaded/fast/foo', ['virtual/threaded/fast/foo/test.html']),
+ ('.', ['animations/keyframes.html']),
('.', ['fast/css/display-none-inline-style-change-crash.html']),
('.', ['dom/html/level2/html/HTMLAnchorElement03.html']),
('.', ['ietestcenter/Javascript/11.1.5_4-4-c-1.html']),
- ('.', ['dom/html/level2/html/HTMLAnchorElement06.html']),
- ('.', ['virtual/threaded/test.html'])])
+ ('.', ['dom/html/level2/html/HTMLAnchorElement06.html'])])
def test_shard_in_two(self):
locked, unlocked = self.get_shards(num_workers=1, fully_parallel=False)
@@ -242,7 +247,8 @@ class SharderTests(unittest.TestCase):
'dom/html/level2/html/HTMLAnchorElement03.html',
'ietestcenter/Javascript/11.1.5_4-4-c-1.html',
'dom/html/level2/html/HTMLAnchorElement06.html',
- 'virtual/threaded/test.html'])])
+ 'virtual/threaded/dir/test.html',
+ 'virtual/threaded/fast/foo/test.html'])])
def test_shard_in_two_has_no_locked_shards(self):
locked, unlocked = self.get_shards(num_workers=1, fully_parallel=False,
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
index 4cd1f5b4fd7..7254c099cbe 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
@@ -50,7 +50,6 @@ from webkitpy.layout_tests.models import test_expectations
from webkitpy.layout_tests.models import test_failures
from webkitpy.layout_tests.models import test_run_results
from webkitpy.layout_tests.models.test_input import TestInput
-from webkitpy.layout_tests.models.test_run_results import INTERRUPTED_EXIT_STATUS
_log = logging.getLogger(__name__)
@@ -151,9 +150,11 @@ class Manager(object):
def _set_up_run(self, test_names):
self._printer.write_update("Checking build ...")
- if self._options.build and not self._port.check_build(self.needs_servers(test_names), self._printer):
- _log.error("Build check failed")
- return False
+ if self._options.build:
+ exit_code = self._port.check_build(self.needs_servers(test_names), self._printer)
+ if exit_code:
+ _log.error("Build check failed")
+ return exit_code
# This must be started before we check the system dependencies,
# since the helper may do things to make the setup correct.
@@ -164,9 +165,10 @@ class Manager(object):
# Check that the system dependencies (themes, fonts, ...) are correct.
if not self._options.nocheck_sys_deps:
self._printer.write_update("Checking system dependencies ...")
- if not self._port.check_sys_deps(self.needs_servers(test_names)):
+ exit_code = self._port.check_sys_deps(self.needs_servers(test_names))
+ if exit_code:
self._port.stop_helper()
- return False
+ return exit_code
if self._options.clobber_old_results:
self._clobber_old_results()
@@ -175,7 +177,7 @@ class Manager(object):
self._port.host.filesystem.maybe_make_directory(self._results_directory)
self._port.setup_test_run()
- return True
+ return test_run_results.OK_EXIT_STATUS
def run(self, args):
"""Run the tests and return a RunDetails object with the results."""
@@ -185,7 +187,7 @@ class Manager(object):
paths, test_names = self._collect_tests(args)
except IOError:
# This is raised if --test-list doesn't exist
- return test_run_results.RunDetails(exit_code=-1)
+ return test_run_results.RunDetails(exit_code=test_run_results.NO_TESTS_EXIT_STATUS)
self._printer.write_update("Parsing expectations ...")
self._expectations = test_expectations.TestExpectations(self._port, test_names)
@@ -196,10 +198,11 @@ class Manager(object):
# Check to make sure we're not skipping every test.
if not tests_to_run:
_log.critical('No tests to run.')
- return test_run_results.RunDetails(exit_code=-1)
+ return test_run_results.RunDetails(exit_code=test_run_results.NO_TESTS_EXIT_STATUS)
- if not self._set_up_run(tests_to_run):
- return test_run_results.RunDetails(exit_code=-1)
+ exit_code = self._set_up_run(tests_to_run)
+ if exit_code:
+ return test_run_results.RunDetails(exit_code=exit_code)
# Don't retry failures if an explicit list of tests was passed in.
if self._options.retry_failures is None:
@@ -212,7 +215,7 @@ class Manager(object):
self._start_servers(tests_to_run)
initial_results = self._run_tests(tests_to_run, tests_to_skip, self._options.repeat_each, self._options.iterations,
- int(self._options.child_processes), retrying=False)
+ self._port.num_workers(int(self._options.child_processes)), retrying=False)
# Don't retry failures when interrupted by user or failures limit exception.
should_retry_failures = should_retry_failures and not (initial_results.interrupted or initial_results.keyboard_interrupted)
@@ -237,7 +240,7 @@ class Manager(object):
# Some crash logs can take a long time to be written out so look
# for new logs after the test run finishes.
- _log.debug("looking for new crash logs")
+ self._printer.write_update("looking for new crash logs")
self._look_for_new_crash_logs(initial_results, start_time)
if retry_results:
self._look_for_new_crash_logs(retry_results, start_time)
@@ -254,7 +257,7 @@ class Manager(object):
results_path = self._filesystem.join(self._results_directory, "results.html")
self._copy_results_html_file(results_path)
if initial_results.keyboard_interrupted:
- exit_code = INTERRUPTED_EXIT_STATUS
+ exit_code = test_run_results.INTERRUPTED_EXIT_STATUS
else:
if self._options.show_results and (exit_code or (self._options.full_results_html and initial_results.total_failures)):
self._port.show_results_html_file(results_path)
@@ -353,6 +356,9 @@ class Manager(object):
if self._filesystem.isdir(self._filesystem.join(layout_tests_dir, dirname)):
self._filesystem.rmtree(self._filesystem.join(self._results_directory, dirname))
+ # Port specific clean-up.
+ self._port.clobber_old_port_specific_results()
+
def _tests_to_retry(self, run_results):
return [result.test_name for result in run_results.unexpected_results_by_name.values() if result.type != test_expectations.PASS]
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py
index 115428ecf9b..0c029234c68 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py
@@ -32,7 +32,7 @@ import re
import time
from webkitpy.layout_tests.controllers import test_result_writer
-from webkitpy.layout_tests.port.driver import DriverInput, DriverOutput
+from webkitpy.layout_tests.port.driver import DeviceFailure, DriverInput, DriverOutput
from webkitpy.layout_tests.models import test_expectations
from webkitpy.layout_tests.models import test_failures
from webkitpy.layout_tests.models.test_results import TestResult
@@ -43,7 +43,11 @@ _log = logging.getLogger(__name__)
def run_single_test(port, options, results_directory, worker_name, driver, test_input, stop_when_done):
runner = SingleTestRunner(port, options, results_directory, worker_name, driver, test_input, stop_when_done)
- return runner.run()
+ try:
+ return runner.run()
+ except DeviceFailure as e:
+ _log.error("device failed: %s", str(e))
+ return TestResult(test_input.test_name, device_failed=True)
class SingleTestRunner(object):
@@ -91,7 +95,20 @@ class SingleTestRunner(object):
image_hash = None
if self._should_fetch_expected_checksum():
image_hash = self._port.expected_checksum(self._test_name)
- return DriverInput(self._test_name, self._timeout, image_hash, self._should_run_pixel_test)
+
+ test_base = self._port.lookup_virtual_test_base(self._test_name)
+ if test_base:
+ # If the file actually exists under the virtual dir, we want to use it (largely for virtual references),
+ # but we want to use the extra command line args either way.
+ if self._filesystem.exists(self._port.abspath_for_test(self._test_name)):
+ test_name = self._test_name
+ else:
+ test_name = test_base
+ args = self._port.lookup_virtual_test_args(self._test_name)
+ else:
+ test_name = self._test_name
+ args = []
+ return DriverInput(test_name, self._timeout, image_hash, self._should_run_pixel_test, args)
def run(self):
if self._reference_files:
@@ -122,7 +139,8 @@ class SingleTestRunner(object):
# FIXME: It the test crashed or timed out, it might be better to avoid
# to write new baselines.
self._overwrite_baselines(driver_output)
- return TestResult(self._test_name, failures, driver_output.test_time, driver_output.has_stderr(), pid=driver_output.pid)
+ return TestResult(self._test_name, failures, driver_output.test_time, driver_output.has_stderr(),
+ pid=driver_output.pid)
_render_tree_dump_pattern = re.compile(r"^layer at \(\d+,\d+\) size \d+x\d+\n")
@@ -215,13 +233,15 @@ class SingleTestRunner(object):
if driver_output.crash:
# Don't continue any more if we already have a crash.
# In case of timeouts, we continue since we still want to see the text and image output.
- return TestResult(self._test_name, failures, driver_output.test_time, driver_output.has_stderr(), pid=driver_output.pid)
+ return TestResult(self._test_name, failures, driver_output.test_time, driver_output.has_stderr(),
+ pid=driver_output.pid)
failures.extend(self._compare_text(expected_driver_output.text, driver_output.text))
failures.extend(self._compare_audio(expected_driver_output.audio, driver_output.audio))
if self._should_run_pixel_test:
failures.extend(self._compare_image(expected_driver_output, driver_output))
- return TestResult(self._test_name, failures, driver_output.test_time, driver_output.has_stderr(), pid=driver_output.pid)
+ return TestResult(self._test_name, failures, driver_output.test_time, driver_output.has_stderr(),
+ pid=driver_output.pid)
def _compare_text(self, expected_text, actual_text):
failures = []
@@ -300,7 +320,8 @@ class SingleTestRunner(object):
for expectation, reference_filename in putAllMismatchBeforeMatch(self._reference_files):
reference_test_name = self._port.relative_test_filename(reference_filename)
reference_test_names.append(reference_test_name)
- reference_output = self._driver.run_test(DriverInput(reference_test_name, self._timeout, None, should_run_pixel_test=True), self._stop_when_done)
+ driver_input = DriverInput(reference_test_name, self._timeout, image_hash=None, should_run_pixel_test=True, args=self._port.lookup_virtual_test_args(reference_test_name))
+ reference_output = self._driver.run_test(driver_input, self._stop_when_done)
test_result = self._compare_output_with_reference(reference_output, test_output, reference_filename, expectation == '!=')
if (expectation == '!=' and test_result.failures) or (expectation == '==' and not test_result.failures):
@@ -313,7 +334,9 @@ class SingleTestRunner(object):
# FIXME: We don't really deal with a mix of reftest types properly. We pass in a set() to reftest_type
# and only really handle the first of the references in the result.
reftest_type = list(set([reference_file[0] for reference_file in self._reference_files]))
- return TestResult(self._test_name, test_result.failures, total_test_time + test_result.test_run_time, test_result.has_stderr, reftest_type=reftest_type, pid=test_result.pid, references=reference_test_names)
+ return TestResult(self._test_name, test_result.failures, total_test_time + test_result.test_run_time,
+ test_result.has_stderr, reftest_type=reftest_type, pid=test_result.pid,
+ references=reference_test_names)
def _compare_output_with_reference(self, reference_driver_output, actual_driver_output, reference_filename, mismatch):
total_test_time = reference_driver_output.test_time + actual_driver_output.test_time
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/layout_tests_mover_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/layout_tests_mover_unittest.py
index 733529c9053..2ad3806a8d1 100755
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/layout_tests_mover_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/layout_tests_mover_unittest.py
@@ -33,15 +33,15 @@ from webkitpy.common.host_mock import MockHost
from webkitpy.common.checkout.scm.scm_mock import MockSCM
from webkitpy.common.system.filesystem_mock import MockFileSystem
from webkitpy.layout_tests.layout_tests_mover import LayoutTestsMover
-from webkitpy.layout_tests.port.chromium import ChromiumPort
+from webkitpy.layout_tests.port import base
-class MockPort(ChromiumPort):
+class MockPort(base.Port):
def __init__(self, **kwargs):
# This sets up a mock FileSystem and SCM using that FileSystem.
host = MockHost()
- ChromiumPort.__init__(self, host, host.port_factory.all_port_names()[0], **kwargs)
+ super(MockPort, self).__init__(host, host.port_factory.all_port_names()[0], **kwargs)
host.filesystem.maybe_make_directory(self._absolute_path('platform'))
host.filesystem.maybe_make_directory(self._absolute_path('existing_directory'))
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
index 310d901a74d..eeb4a949809 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
@@ -203,7 +203,7 @@ class TestExpectationParser(object):
# FIXME: Update the original specifiers and remove this once the old syntax is gone.
_configuration_tokens_list = [
- 'Mac', 'SnowLeopard', 'Lion', 'Retina', 'MountainLion',
+ 'Mac', 'SnowLeopard', 'Lion', 'Retina', 'MountainLion', 'Mavericks',
'Win', 'XP', 'Win7',
'Linux',
'Android',
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_results.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_results.py
index d6fd10b1818..984f30a37e0 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_results.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_results.py
@@ -38,7 +38,7 @@ class TestResult(object):
def loads(string):
return cPickle.loads(string)
- def __init__(self, test_name, failures=None, test_run_time=None, has_stderr=False, reftest_type=None, pid=None, references=None):
+ def __init__(self, test_name, failures=None, test_run_time=None, has_stderr=False, reftest_type=None, pid=None, references=None, device_failed=False):
self.test_name = test_name
self.failures = failures or []
self.test_run_time = test_run_time or 0 # The time taken to execute the test itself.
@@ -46,6 +46,7 @@ class TestResult(object):
self.reftest_type = reftest_type or []
self.pid = pid
self.references = references or []
+ self.device_failed = device_failed
# FIXME: Setting this in the constructor makes this class hard to mutate.
self.type = test_failures.determine_result_type(failures)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py
index 6e905d99020..95146472146 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py
@@ -37,8 +37,34 @@ from webkitpy.layout_tests.models import test_failures
_log = logging.getLogger(__name__)
+OK_EXIT_STATUS = 0
+
+# This matches what the shell does on POSIX.
INTERRUPTED_EXIT_STATUS = signal.SIGINT + 128
+# POSIX limits status codes to 0-255. Normally run-webkit-tests returns the number
+# of tests that failed. These indicate exceptional conditions triggered by the
+# script itself, so we count backwards from 255 (aka -1) to enumerate them.
+SYS_DEPS_EXIT_STATUS = 252
+NO_TESTS_EXIT_STATUS = 253
+NO_DEVICES_EXIT_STATUS = 254
+UNEXPECTED_ERROR_EXIT_STATUS = 255
+
+ERROR_CODES = (
+ INTERRUPTED_EXIT_STATUS,
+ SYS_DEPS_EXIT_STATUS,
+ NO_TESTS_EXIT_STATUS,
+ NO_DEVICES_EXIT_STATUS,
+ UNEXPECTED_ERROR_EXIT_STATUS,
+)
+
+
+class TestRunException(Exception):
+ def __init__(self, code, msg):
+ self.code = code
+ self.msg = msg
+
+
class TestRunResults(object):
def __init__(self, expectations, num_tests):
self.total = num_tests
@@ -181,14 +207,9 @@ def summarize_results(port_obj, expectations, initial_results, retry_results, en
num_flaky += 1
elif retry_results:
retry_result_type = retry_results.unexpected_results_by_name[test_name].type
- if result_type != retry_result_type:
- if enabled_pixel_tests_in_retry and result_type == test_expectations.TEXT and retry_result_type == test_expectations.IMAGE_PLUS_TEXT:
- num_regressions += 1
- else:
- num_flaky += 1
+ num_regressions += 1
+ if not keywords[retry_result_type] in actual:
actual.append(keywords[retry_result_type])
- else:
- num_regressions += 1
else:
num_regressions += 1
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/__init__.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/__init__.py
index b2a50844c42..cc7fa86da13 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/__init__.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/__init__.py
@@ -31,5 +31,5 @@
import builders # Why is this in port?
from base import Port # It's possible we don't need to export this virtual baseclass outside the module.
-from driver import Driver, DriverInput, DriverOutput
+from driver import DeviceFailure, Driver, DriverInput, DriverOutput
from factory import platform_options, configuration_options
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py
index 5837ac20c0b..d3267f9a00f 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py
@@ -30,6 +30,7 @@ import copy
import logging
import os
import re
+import signal
import sys
import subprocess
import threading
@@ -37,7 +38,10 @@ import time
from multiprocessing.pool import ThreadPool
-from webkitpy.layout_tests.port import chromium
+from webkitpy.common.system.executive import ScriptError
+from webkitpy.layout_tests.breakpad.dump_reader_multipart import DumpReaderAndroid
+from webkitpy.layout_tests.models import test_run_results
+from webkitpy.layout_tests.port import base
from webkitpy.layout_tests.port import linux
from webkitpy.layout_tests.port import driver
from webkitpy.layout_tests.port import factory
@@ -172,8 +176,14 @@ class ContentShellDriverDetails():
def command_line_file(self):
return '/data/local/tmp/content-shell-command-line'
- def additional_command_line_flags(self):
- return ['--dump-render-tree', '--encode-binary']
+ def device_crash_dumps_directory(self):
+ return '/data/local/tmp/content-shell-crash-dumps'
+
+ def additional_command_line_flags(self, use_breakpad):
+ flags = ['--dump-render-tree', '--encode-binary']
+ if use_breakpad:
+ flags.extend(['--enable-crash-reporter', '--crash-dumps-dir=%s' % self.device_crash_dumps_directory()])
+ return flags
def device_directory(self):
return DEVICE_SOURCE_ROOT_DIR + 'content_shell/'
@@ -193,7 +203,7 @@ class AndroidCommands(object):
def file_exists(self, full_path):
assert full_path.startswith('/')
- return self.run(['shell', 'ls', full_path]).strip() == full_path
+ return self.run(['shell', 'ls', '-d', full_path]).strip() == full_path
def push(self, host_path, device_path, ignore_error=False):
return self.run(['push', host_path, device_path], ignore_error=ignore_error)
@@ -206,6 +216,12 @@ class AndroidCommands(object):
if chmod:
self.run(['shell', 'chmod', chmod, device_path])
+ def restart_adb(self):
+ pids = self.extract_pids('adbd')
+ if pids:
+ output = self.run(['shell', 'kill', '-' + str(signal.SIGTERM)] + pids)
+ self.run(['wait-for-device'])
+
def restart_as_root(self):
output = self.run(['root'])
if 'adbd is already running as root' in output:
@@ -216,6 +232,21 @@ class AndroidCommands(object):
self.run(['wait-for-device'])
+ def extract_pids(self, process_name):
+ pids = []
+ output = self.run(['shell', 'ps'])
+ for line in output.splitlines():
+ data = line.split()
+ try:
+ if process_name in data[-1]: # name is in the last column
+ if process_name == data[-1]:
+ pids.insert(0, data[1]) # PID is in the second column
+ else:
+ pids.append(data[1])
+ except IndexError:
+ pass
+ return pids
+
def run(self, command, ignore_error=False):
self._log_debug('Run adb command: ' + str(command))
if ignore_error:
@@ -223,8 +254,7 @@ class AndroidCommands(object):
else:
error_handler = None
- result = self._executive.run_command(self.adb_command() + command, error_handler=error_handler,
- debug_logging=self._debug_logging)
+ result = self._executive.run_command(self.adb_command() + command, error_handler=error_handler, debug_logging=self._debug_logging)
# We limit the length to avoid outputting too verbose commands, such as "adb logcat".
self._log_debug('Run adb result: ' + result[:80])
@@ -325,7 +355,7 @@ class AndroidDevices(object):
error_handler=executive.ignore_error, debug_logging=self._debug_logging)
devices = re_device.findall(result)
if not devices:
- raise AssertionError('Unable to find attached Android devices. ADB output: %s' % result)
+ return []
for device_serial in sorted(devices):
commands = AndroidCommands(executive, device_serial, self._debug_logging)
@@ -340,9 +370,6 @@ class AndroidDevices(object):
self._usable_devices.append(commands)
- if not self._usable_devices:
- raise AssertionError('No usable devices are available for running layout tests.')
-
return self._usable_devices
def get_device(self, executive, device_index):
@@ -361,9 +388,9 @@ class AndroidDevices(object):
# Private methods
def _battery_level_for_device(self, commands):
battery_status = commands.run(['shell', 'dumpsys', 'battery'])
- if 'Error' in battery_status:
+ if 'Error' in battery_status or "Can't find service: battery" in battery_status:
_log.warning('Unable to read the battery level from device with serial "%s".' % commands.get_serial())
- return 100
+ return 0
return int(re.findall('level: (\d+)', battery_status)[0])
@@ -372,7 +399,7 @@ class AndroidDevices(object):
return 'mScreenOn=true' in power_status or 'mScreenOn=SCREEN_ON_BIT' in power_status
-class AndroidPort(chromium.ChromiumPort):
+class AndroidPort(base.Port):
port_name = 'android'
# Avoid initializing the adb path [worker count]+1 times by storing it as a static member.
@@ -380,7 +407,7 @@ class AndroidPort(chromium.ChromiumPort):
SUPPORTED_VERSIONS = ('android')
- FALLBACK_PATHS = { 'android': [ 'android' ] + linux.LinuxPort.latest_platform_fallback_path() }
+ FALLBACK_PATHS = {'icecreamsandwich': ['android'] + linux.LinuxPort.latest_platform_fallback_path()}
# Android has aac and mp3 codecs built in.
PORT_HAS_AUDIO_CODECS_BUILT_IN = True
@@ -394,6 +421,9 @@ class AndroidPort(chromium.ChromiumPort):
self._host_port = factory.PortFactory(host).get('chromium', **kwargs)
self._server_process_constructor = self._android_server_process_constructor
+ if not self.get_option('disable_breakpad'):
+ self._dump_reader = DumpReaderAndroid(host, self._build_path())
+
if self.driver_name() != self.CONTENT_SHELL_NAME:
raise AssertionError('Layout tests on Android only support content_shell as the driver.')
@@ -429,7 +459,7 @@ class AndroidPort(chromium.ChromiumPort):
return self._build_path(MD5SUM_HOST_FILE_NAME)
def additional_drt_flag(self):
- return self._driver_details.additional_command_line_flags()
+ return self._driver_details.additional_command_line_flags(use_breakpad=not self.get_option('disable_breakpad'))
def default_timeout_ms(self):
# Android platform has less computing power than desktop platforms.
@@ -442,33 +472,47 @@ class AndroidPort(chromium.ChromiumPort):
return 0.0
def default_child_processes(self):
- usable_device_count = len(self._devices.usable_devices(self._executive))
- if not usable_device_count:
- raise AssertionError('There are no devices available to run the layout tests on.')
-
- return usable_device_count
-
- def default_baseline_search_path(self):
- return map(self._webkit_baseline_path, self.FALLBACK_PATHS['android'])
+ usable_devices = self._devices.usable_devices(self._executive)
+ if not usable_devices:
+ raise test_run_results.TestRunException(test_run_results.NO_DEVICES_EXIT_STATUS, "Unable to find any attached Android devices.")
+ return len(usable_devices)
def check_wdiff(self, logging=True):
return self._host_port.check_wdiff(logging)
def check_build(self, needs_http, printer):
- result = super(AndroidPort, self).check_build(needs_http, printer)
- result = self._check_file_exists(self.path_to_md5sum(), 'md5sum utility') and result
+ exit_status = super(AndroidPort, self).check_build(needs_http, printer)
+ if exit_status:
+ return exit_status
+
+ result = self._check_file_exists(self.path_to_md5sum(), 'md5sum utility')
result = self._check_file_exists(self.path_to_md5sum_host(), 'md5sum host utility') and result
result = self._check_file_exists(self.path_to_forwarder(), 'forwarder utility') and result
- if result:
- # FIXME: We should figure out how to handle failures here better.
- self._check_devices(printer)
+
+ if not result:
+ # There is a race condition in adb at least <= 4.3 on Linux that causes it to go offline periodically
+ # We set the processor affinity for any running adb process to attempt to work around this.
+ # See crbug.com/268450
+ if self.host.platform.is_linux():
+ pids = self._executive.running_pids(lambda name: 'adb' in name)
+ if not pids:
+ # Apparently adb is not running, which is unusual. Running any adb command should start it.
+ self._executive.run_command(['adb', 'devices'])
+ pids = self._executive.running_pids(lambda name: 'adb' in name)
+ if not pids:
+ _log.error("The adb daemon does not appear to be running.")
+ return False
+
+ for pid in pids:
+ self._executive.run_command(['taskset', '-p', '-c', '0', str(pid)])
if not result:
_log.error('For complete Android build requirements, please see:')
_log.error('')
_log.error(' http://code.google.com/p/chromium/wiki/AndroidBuildInstructions')
+ return test_run_results.UNEXPECTED_ERROR_EXIT_STATUS
- return result
+ return self._check_devices(printer)
def _check_devices(self, printer):
# Printer objects aren't threadsafe, so we need to protect calls to them.
@@ -496,14 +540,18 @@ class AndroidPort(chromium.ChromiumPort):
log_safely("preparing device", throttled=False)
try:
d._setup_test(log_safely)
+ log_safely("device prepared", throttled=False)
+ except (ScriptError, driver.DeviceFailure) as e:
+ lock.acquire()
+ _log.warning("[%s] failed to prepare_device: %s" % (serial, str(e)))
+ lock.release()
except KeyboardInterrupt:
if pool:
pool.terminate()
- log_safely("device prepared", throttled=False)
# FIXME: It would be nice if we knew how many workers we needed.
num_workers = self.default_child_processes()
- num_child_processes = self.get_option('child_processes')
+ num_child_processes = int(self.get_option('child_processes'))
if num_child_processes:
num_workers = min(num_workers, num_child_processes)
if num_workers > 1:
@@ -516,6 +564,11 @@ class AndroidPort(chromium.ChromiumPort):
else:
setup_device(0)
+ if not self._devices.prepared_devices():
+ _log.error('Could not prepare any devices for testing.')
+ return test_run_results.NO_DEVICES_EXIT_STATUS
+ return test_run_results.OK_EXIT_STATUS
+
def setup_test_run(self):
super(AndroidPort, self).setup_test_run()
@@ -527,6 +580,9 @@ class AndroidPort(chromium.ChromiumPort):
# We were called with --no-build, so assume the devices are up to date.
self._options.prepared_devices = [d.get_serial() for d in self._devices.usable_devices(self.host.executive)]
+ def num_workers(self, requested_num_workers):
+ return min(len(self._options.prepared_devices), requested_num_workers)
+
def check_sys_deps(self, needs_http):
for (font_dirs, font_file, package) in HOST_FONT_FILES:
exists = False
@@ -537,8 +593,8 @@ class AndroidPort(chromium.ChromiumPort):
break
if not exists:
_log.error('You are missing %s under %s. Try installing %s. See build instructions.' % (font_file, font_dirs, package))
- return False
- return True
+ return test_run_results.SYS_DEPS_EXIT_STATUS
+ return test_run_results.OK_EXIT_STATUS
def requires_http_server(self):
"""Chromium Android runs tests on devices, and uses the HTTP server to
@@ -563,6 +619,10 @@ class AndroidPort(chromium.ChromiumPort):
# Override to return the actual test driver's command line.
return self.create_driver(0)._android_driver_cmd_line(self.get_option('pixel_tests'), [])
+ def clobber_old_port_specific_results(self):
+ if not self.get_option('disable_breakpad'):
+ self._dump_reader.clobber_old_results()
+
# Overridden protected methods.
def _build_path(self, *comps):
@@ -747,6 +807,8 @@ class ChromiumAndroidDriver(driver.Driver):
self._android_commands = android_devices.get_device(port._executive, worker_number)
self._driver_details = driver_details
self._debug_logging = self._port._debug_logging
+ self._created_cmd_line = False
+ self._device_failed = False
# FIXME: If we taught ProfileFactory about "target" devices we could
# just use the logic in Driver instead of duplicating it here.
@@ -768,6 +830,7 @@ class ChromiumAndroidDriver(driver.Driver):
def __del__(self):
self._teardown_performance()
+ self._clean_up_cmd_line()
super(ChromiumAndroidDriver, self).__del__()
def _update_kallsyms_cache(self, output_dir):
@@ -814,7 +877,7 @@ class ChromiumAndroidDriver(driver.Driver):
self._md5sum_path = self._port.path_to_md5sum()
if not self._android_commands.file_exists(MD5SUM_DEVICE_PATH):
if not self._android_commands.push(self._md5sum_path, MD5SUM_DEVICE_PATH):
- raise AssertionError('Could not push md5sum to device')
+ self._abort('Could not push md5sum to device')
self._push_executable(log_callback)
self._push_fonts(log_callback)
@@ -828,6 +891,7 @@ class ChromiumAndroidDriver(driver.Driver):
if self._android_devices.is_device_prepared(self._android_commands.get_serial()):
return
+ self._android_commands.restart_adb()
self._android_commands.restart_as_root()
self._setup_md5sum_and_push_data_if_needed(log_callback)
self._setup_performance()
@@ -850,12 +914,16 @@ class ChromiumAndroidDriver(driver.Driver):
def _log_error(self, message):
_log.error('[%s] %s' % (self._android_commands.get_serial(), message))
+ def _log_warning(self, message):
+ _log.warning('[%s] %s' % (self._android_commands.get_serial(), message))
+
def _log_debug(self, message):
if self._debug_logging:
_log.debug('[%s] %s' % (self._android_commands.get_serial(), message))
def _abort(self, message):
- raise AssertionError('[%s] %s' % (self._android_commands.get_serial(), message))
+ self._device_failed = True
+ raise driver.DeviceFailure('[%s] %s' % (self._android_commands.get_serial(), message))
@staticmethod
def _extract_hashes_from_md5sum_output(md5sum_output):
@@ -914,23 +982,37 @@ class ChromiumAndroidDriver(driver.Driver):
self._push_file_if_needed(self._port.layout_tests_dir() + '/' + resource, DEVICE_LAYOUT_TESTS_DIR + resource, log_callback)
def _get_last_stacktrace(self):
- tombstones = self._android_commands.run(['shell', 'ls', '-n', '/data/tombstones'])
- if not tombstones or tombstones.startswith('/data/tombstones: No such file or directory'):
+ tombstones = self._android_commands.run(['shell', 'ls', '-n', '/data/tombstones/tombstone_*'])
+ if not tombstones or tombstones.startswith('/data/tombstones/tombstone_*: No such file or directory'):
self._log_error('The driver crashed, but no tombstone found!')
return ''
+
+ if tombstones.startswith('/data/tombstones/tombstone_*: Permission denied'):
+ # FIXME: crbug.com/321489 ... figure out why this happens.
+ self._log_error('The driver crashed, but we could not read the tombstones!')
+ return ''
+
tombstones = tombstones.rstrip().split('\n')
- last_tombstone = tombstones[0].split()
- for tombstone in tombstones[1:]:
+ last_tombstone = None
+ for tombstone in tombstones:
# Format of fields:
# 0 1 2 3 4 5 6
# permission uid gid size date time filename
# -rw------- 1000 1000 45859 2011-04-13 06:00 tombstone_00
fields = tombstone.split()
- if (fields[4] + fields[5] >= last_tombstone[4] + last_tombstone[5]):
+ if len(fields) != 7:
+ self._log_warning("unexpected line in tombstone output, skipping: '%s'" % tombstone)
+ continue
+
+ if not last_tombstone or fields[4] + fields[5] >= last_tombstone[4] + last_tombstone[5]:
last_tombstone = fields
else:
break
+ if not last_tombstone:
+ self._log_error('The driver crashed, but we could not find any valid tombstone!')
+ return ''
+
# Use Android tool vendor/google/tools/stack to convert the raw
# stack trace into a human readable format, if needed.
# It takes a long time, so don't do it here.
@@ -961,6 +1043,12 @@ class ChromiumAndroidDriver(driver.Driver):
if not stderr:
stderr = ''
stderr += '********* [%s] Tombstone file:\n%s' % (self._android_commands.get_serial(), self._get_last_stacktrace())
+
+ if not self._port.get_option('disable_breakpad'):
+ crashes = self._pull_crash_dumps_from_device()
+ for crash in crashes:
+ stderr += '********* [%s] breakpad minidump %s:\n%s' % (self._port.host.filesystem.basename(crash), self._android_commands.get_serial(), self._port._dump_reader._get_stack_from_dump(crash))
+
return super(ChromiumAndroidDriver, self)._get_crash_log(stdout, stderr, newer_than)
def cmd_line(self, pixel_tests, per_test_args):
@@ -992,14 +1080,6 @@ class ChromiumAndroidDriver(driver.Driver):
not self._android_commands.file_exists(self._out_fifo_path) and
not self._android_commands.file_exists(self._err_fifo_path))
- def run_test(self, driver_input, stop_when_done):
- base = self._port.lookup_virtual_test_base(driver_input.test_name)
- if base:
- driver_input = copy.copy(driver_input)
- driver_input.args = self._port.lookup_virtual_test_args(driver_input.test_name)
- driver_input.test_name = base
- return super(ChromiumAndroidDriver, self).run_test(driver_input, stop_when_done)
-
def start(self, pixel_tests, per_test_args):
# We override the default start() so that we can call _android_driver_cmd_line()
# instead of cmd_line().
@@ -1016,18 +1096,23 @@ class ChromiumAndroidDriver(driver.Driver):
super(ChromiumAndroidDriver, self).start(pixel_tests, per_test_args)
def _start(self, pixel_tests, per_test_args):
- assert self._android_devices.is_device_prepared(self._android_commands.get_serial())
+ if not self._android_devices.is_device_prepared(self._android_commands.get_serial()):
+ raise driver.DeviceFailure("%s is not prepared in _start()" % self._android_commands.get_serial())
for retries in range(3):
- if self._start_once(pixel_tests, per_test_args):
- return
+ try:
+ if self._start_once(pixel_tests, per_test_args):
+ return
+ except ScriptError as e:
+ self._abort('ScriptError("%s") in _start()' % str(e))
+
self._log_error('Failed to start the content_shell application. Retries=%d. Log:%s' % (retries, self._get_logcat()))
self.stop()
time.sleep(2)
self._abort('Failed to start the content_shell application multiple times. Giving up.')
def _start_once(self, pixel_tests, per_test_args):
- super(ChromiumAndroidDriver, self)._start(pixel_tests, per_test_args)
+ super(ChromiumAndroidDriver, self)._start(pixel_tests, per_test_args, wait_for_ready=False)
self._log_debug('Starting forwarder')
self._forwarder_process = self._port._server_process_constructor(
@@ -1039,7 +1124,21 @@ class ChromiumAndroidDriver(driver.Driver):
return False
self._android_commands.run(['logcat', '-c'])
+
+ cmd_line_file_path = self._driver_details.command_line_file()
+ original_cmd_line_file_path = cmd_line_file_path + '.orig'
+ if self._android_commands.file_exists(cmd_line_file_path) and not self._android_commands.file_exists(original_cmd_line_file_path):
+ # We check for both the normal path and the backup because we do not want to step
+ # on the backup. Otherwise, we'd clobber the backup whenever we changed the
+ # command line during the run.
+ self._android_commands.run(['shell', 'mv', cmd_line_file_path, original_cmd_line_file_path])
+
self._android_commands.run(['shell', 'echo'] + self._android_driver_cmd_line(pixel_tests, per_test_args) + ['>', self._driver_details.command_line_file()])
+ self._created_cmd_line = True
+
+ self._android_commands.run(['shell', 'rm', '-rf', self._driver_details.device_crash_dumps_directory()])
+ self._android_commands.mkdir(self._driver_details.device_crash_dumps_directory(), chmod='777')
+
start_result = self._android_commands.run(['shell', 'am', 'start', '-e', 'RunInSubThread', '-n', self._driver_details.activity_name()])
if start_result.find('Exception') != -1:
self._log_error('Failed to start the content_shell application. Exception:\n' + start_result)
@@ -1111,7 +1210,10 @@ class ChromiumAndroidDriver(driver.Driver):
return self._pid_from_android_ps_output(ps_output, self._driver_details.package_name())
def stop(self):
- self._android_commands.run(['shell', 'am', 'force-stop', self._driver_details.package_name()])
+ if not self._device_failed:
+ # Do not try to stop the application if there's something wrong with the device; adb may hang.
+ # FIXME: crbug.com/305040. Figure out if it's really hanging (and why).
+ self._android_commands.run(['shell', 'am', 'force-stop', self._driver_details.package_name()])
if self._read_stdout_process:
self._read_stdout_process.kill()
@@ -1129,7 +1231,41 @@ class ChromiumAndroidDriver(driver.Driver):
if self._android_devices.is_device_prepared(self._android_commands.get_serial()):
if not ChromiumAndroidDriver._loop_with_timeout(self._remove_all_pipes, DRIVER_START_STOP_TIMEOUT_SECS):
- raise AssertionError('Failed to remove fifo files. May be locked.')
+ self._abort('Failed to remove fifo files. May be locked.')
+
+ self._clean_up_cmd_line()
+
+ def _pull_crash_dumps_from_device(self):
+ result = []
+ if not self._android_commands.file_exists(self._driver_details.device_crash_dumps_directory()):
+ return result
+ dumps = self._android_commands.run(['shell', 'ls', self._driver_details.device_crash_dumps_directory()])
+ for dump in dumps.splitlines():
+ device_dump = '%s/%s' % (self._driver_details.device_crash_dumps_directory(), dump)
+ local_dump = self._port._filesystem.join(self._port._dump_reader.crash_dumps_directory(), dump)
+
+ # FIXME: crbug.com/321489. Figure out why these commands would fail ...
+ err = self._android_commands.run(['shell', 'chmod', '777', device_dump])
+ if not err:
+ self._android_commands.pull(device_dump, local_dump)
+ if not err:
+ self._android_commands.run(['shell', 'rm', '-f', device_dump])
+
+ if self._port._filesystem.exists(local_dump):
+ result.append(local_dump)
+ return result
+
+ def _clean_up_cmd_line(self):
+ if not self._created_cmd_line:
+ return
+
+ cmd_line_file_path = self._driver_details.command_line_file()
+ original_cmd_line_file_path = cmd_line_file_path + '.orig'
+ if self._android_commands.file_exists(original_cmd_line_file_path):
+ self._android_commands.run(['shell', 'mv', original_cmd_line_file_path, cmd_line_file_path])
+ elif self._android_commands.file_exists(cmd_line_file_path):
+ self._android_commands.run(['shell', 'rm', cmd_line_file_path])
+ self._created_cmd_line = False
def _command_from_driver_input(self, driver_input):
command = super(ChromiumAndroidDriver, self)._command_from_driver_input(driver_input)
@@ -1148,16 +1284,3 @@ class ChromiumAndroidDriver(driver.Driver):
if last_char in ('#', '$'):
return
last_char = current_char
-
- def _wait_for_server_process_output(self, server_process, deadline, text):
- output = ''
- line = server_process.read_stdout_line(deadline)
- while not server_process.timed_out and not server_process.has_crashed() and not text in line.rstrip():
- output += line
- line = server_process.read_stdout_line(deadline)
-
- if server_process.timed_out or server_process.has_crashed():
- _log.error('Failed to start the %s process: \n%s' % (server_process.name(), output))
- return False
-
- return True
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py
index 56248ed2977..01761c78cfb 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android_unittest.py
@@ -37,11 +37,17 @@ from webkitpy.common.system.executive_mock import MockExecutive2
from webkitpy.common.system.systemhost_mock import MockSystemHost
from webkitpy.layout_tests.port import android
-from webkitpy.layout_tests.port import chromium_port_testcase
+from webkitpy.layout_tests.port import port_testcase
from webkitpy.layout_tests.port import driver
from webkitpy.layout_tests.port import driver_unittest
from webkitpy.tool.mocktool import MockOptions
+# Type of tombstone test which the mocked Android Debug Bridge should execute.
+VALID_TOMBSTONE_TEST_TYPE = 0
+NO_FILES_TOMBSTONE_TEST_TYPE = 1
+NO_PERMISSION_TOMBSTONE_TEST_TYPE = 2
+INVALID_ENTRY_TOMBSTONE_TEST_TYPE = 3
+INVALID_ENTRIES_TOMBSTONE_TEST_TYPE = 4
# Any "adb" commands will be interpret by this class instead of executing actual
# commansd on the file system, which we don't want to do.
@@ -49,6 +55,7 @@ class MockAndroidDebugBridge:
def __init__(self, device_count):
self._device_count = device_count
self._last_command = None
+ self._tombstone_output = None
# Local public methods.
@@ -79,12 +86,20 @@ class MockAndroidDebugBridge:
return 'mockoutput'
if len(args) > 5 and args[5] == 'power':
return 'mScreenOn=true'
+ if len(args) > 5 and args[4] == 'cat' and args[5].find('tombstone') != -1:
+ return 'tombstone content'
+ if len(args) > 6 and args[4] == 'ls' and args[6].find('tombstone') != -1:
+ assert self._tombstone_output, 'Tombstone output needs to have been set by the test.'
+ return self._tombstone_output
return ''
def last_command(self):
return self._last_command
+ def set_tombstone_output(self, output):
+ self._tombstone_output = output
+
# Local private methods.
def _get_device_output(self):
@@ -133,8 +148,8 @@ class AndroidCommandsTest(unittest.TestCase):
def test_convenience_methods(self):
android_commands = self.make_android_commands(1, '123456789ABCDEF0')
- android_commands.file_exists('/tombstones')
- self.assertEquals('adb -s 123456789ABCDEF0 shell ls /tombstones', self._mock_executive.last_command())
+ android_commands.file_exists('/some_directory')
+ self.assertEquals('adb -s 123456789ABCDEF0 shell ls -d /some_directory', self._mock_executive.last_command())
android_commands.push('foo', 'bar')
self.assertEquals('adb -s 123456789ABCDEF0 push foo bar', self._mock_executive.last_command())
@@ -143,7 +158,7 @@ class AndroidCommandsTest(unittest.TestCase):
self.assertEquals('adb -s 123456789ABCDEF0 pull bar foo', self._mock_executive.last_command())
-class AndroidPortTest(chromium_port_testcase.ChromiumPortTestCase):
+class AndroidPortTest(port_testcase.PortTestCase):
port_name = 'android'
port_maker = android.AndroidPort
@@ -156,9 +171,12 @@ class AndroidPortTest(chromium_port_testcase.ChromiumPortTestCase):
def test_check_build(self):
host = MockSystemHost()
host.filesystem.exists = lambda p: True
- port = self.make_port(host=host)
- port.check_build(needs_http=True, printer=chromium_port_testcase.FakePrinter())
+ port = self.make_port(host=host, options=MockOptions(child_processes=1))
+ port.check_build(needs_http=True, printer=port_testcase.FakePrinter())
+ def test_check_sys_deps(self):
+ # FIXME: Do something useful here, but testing the full logic would be hard.
+ pass
def make_wdiff_available(self, port):
port._wdiff_available = True
@@ -237,3 +255,73 @@ class ChromiumAndroidTwoPortsTest(unittest.TestCase):
self.assertEqual(1, port0.driver_cmd_line().count('--foo=bar'))
self.assertEqual(0, port1.driver_cmd_line().count('--create-stdin-fifo'))
+
+
+class ChromiumAndroidDriverTombstoneTest(unittest.TestCase):
+ EXPECTED_STACKTRACE = '-rw------- 1000 1000 3604 2013-11-19 16:16 tombstone_10\ntombstone content'
+
+ def setUp(self):
+ self._mock_adb = MockAndroidDebugBridge(1)
+ self._mock_executive = MockExecutive2(run_command_fn=self._mock_adb.run_command)
+
+ self._port = android.AndroidPort(MockSystemHost(executive=self._mock_executive), 'android')
+ self._driver = android.ChromiumAndroidDriver(self._port, worker_number=0,
+ pixel_tests=True, driver_details=android.ContentShellDriverDetails(), android_devices=self._port._devices)
+
+ self._errors = []
+ self._driver._log_error = lambda msg: self._errors.append(msg)
+
+ self._warnings = []
+ self._driver._log_warning = lambda msg: self._warnings.append(msg)
+
+ # Tests that we return an empty string and log an error when no tombstones could be found.
+ def test_no_tombstones_found(self):
+ self._mock_adb.set_tombstone_output('/data/tombstones/tombstone_*: No such file or directory')
+ stacktrace = self._driver._get_last_stacktrace()
+
+ self.assertEqual(1, len(self._errors))
+ self.assertEqual('The driver crashed, but no tombstone found!', self._errors[0])
+ self.assertEqual('', stacktrace)
+
+ # Tests that an empty string will be returned if we cannot read the tombstone files.
+ def test_insufficient_tombstone_permission(self):
+ self._mock_adb.set_tombstone_output('/data/tombstones/tombstone_*: Permission denied')
+ stacktrace = self._driver._get_last_stacktrace()
+
+ self.assertEqual(1, len(self._errors))
+ self.assertEqual('The driver crashed, but we could not read the tombstones!', self._errors[0])
+ self.assertEqual('', stacktrace)
+
+ # Tests that invalid "ls" output will throw a warning when listing the tombstone files.
+ def test_invalid_tombstone_list_entry_format(self):
+ self._mock_adb.set_tombstone_output('-rw------- 1000 1000 3604 2013-11-19 16:15 tombstone_00\n' +
+ '-- invalid entry --\n' +
+ '-rw------- 1000 1000 3604 2013-11-19 16:16 tombstone_10')
+ stacktrace = self._driver._get_last_stacktrace()
+
+ self.assertEqual(1, len(self._warnings))
+ self.assertEqual(ChromiumAndroidDriverTombstoneTest.EXPECTED_STACKTRACE, stacktrace)
+
+ # Tests the case in which we can't find any valid tombstone entries at all. The tombstone
+ # output used for the mock misses the permission part.
+ def test_invalid_tombstone_list(self):
+ self._mock_adb.set_tombstone_output('1000 1000 3604 2013-11-19 16:15 tombstone_00\n' +
+ '1000 1000 3604 2013-11-19 16:15 tombstone_01\n' +
+ '1000 1000 3604 2013-11-19 16:15 tombstone_02')
+ stacktrace = self._driver._get_last_stacktrace()
+
+ self.assertEqual(3, len(self._warnings))
+ self.assertEqual(1, len(self._errors))
+ self.assertEqual('The driver crashed, but we could not find any valid tombstone!', self._errors[0])
+ self.assertEqual('', stacktrace)
+
+ # Tests that valid tombstone listings will return the contents of the most recent file.
+ def test_read_valid_tombstone_file(self):
+ self._mock_adb.set_tombstone_output('-rw------- 1000 1000 3604 2013-11-19 16:15 tombstone_00\n' +
+ '-rw------- 1000 1000 3604 2013-11-19 16:16 tombstone_10\n' +
+ '-rw------- 1000 1000 3604 2013-11-19 16:15 tombstone_02')
+ stacktrace = self._driver._get_last_stacktrace()
+
+ self.assertEqual(0, len(self._warnings))
+ self.assertEqual(0, len(self._errors))
+ self.assertEqual(ChromiumAndroidDriverTombstoneTest.EXPECTED_STACKTRACE, stacktrace)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
index 4637f585f04..f2ece188dd1 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
@@ -56,6 +56,7 @@ from webkitpy.common.system.path import cygpath
from webkitpy.common.system.systemhost import SystemHost
from webkitpy.common.webkit_finder import WebKitFinder
from webkitpy.layout_tests.layout_package.bot_test_expectations import BotTestExpectationsFactory
+from webkitpy.layout_tests.models import test_run_results
from webkitpy.layout_tests.models.test_configuration import TestConfiguration
from webkitpy.layout_tests.port import config as port_config
from webkitpy.layout_tests.port import driver
@@ -89,6 +90,69 @@ class Port(object):
# True if the port as aac and mp3 codecs built in.
PORT_HAS_AUDIO_CODECS_BUILT_IN = False
+ ALL_SYSTEMS = (
+ ('snowleopard', 'x86'),
+ ('lion', 'x86'),
+
+ # FIXME: We treat Retina (High-DPI) devices as if they are running
+ # a different operating system version. This isn't accurate, but will work until
+ # we need to test and support baselines across multiple O/S versions.
+ ('retina', 'x86'),
+
+ ('mountainlion', 'x86'),
+ ('mavericks', 'x86'),
+ ('xp', 'x86'),
+ ('win7', 'x86'),
+ ('lucid', 'x86'),
+ ('lucid', 'x86_64'),
+ # FIXME: Technically this should be 'arm', but adding a third architecture type breaks TestConfigurationConverter.
+ # If we need this to be 'arm' in the future, then we first have to fix TestConfigurationConverter.
+ ('icecreamsandwich', 'x86'),
+ )
+
+ ALL_BASELINE_VARIANTS = [
+ 'mac-mavericks', 'mac-mountainlion', 'mac-retina', 'mac-lion', 'mac-snowleopard',
+ 'win-win7', 'win-xp',
+ 'linux-x86_64', 'linux-x86',
+ ]
+
+ CONFIGURATION_SPECIFIER_MACROS = {
+ 'mac': ['snowleopard', 'lion', 'retina', 'mountainlion', 'mavericks'],
+ 'win': ['xp', 'win7'],
+ 'linux': ['lucid'],
+ 'android': ['icecreamsandwich'],
+ }
+
+ DEFAULT_BUILD_DIRECTORIES = ('out',)
+
+ # overridden in subclasses.
+ FALLBACK_PATHS = {}
+
+ SUPPORTED_VERSIONS = []
+
+ @classmethod
+ def latest_platform_fallback_path(cls):
+ return cls.FALLBACK_PATHS[cls.SUPPORTED_VERSIONS[-1]]
+
+ @classmethod
+ def _static_build_path(cls, filesystem, build_directory, chromium_base, configuration, comps):
+ if build_directory:
+ return filesystem.join(build_directory, configuration, *comps)
+
+ hits = []
+ for directory in cls.DEFAULT_BUILD_DIRECTORIES:
+ base_dir = filesystem.join(chromium_base, directory, configuration)
+ path = filesystem.join(base_dir, *comps)
+ if filesystem.exists(path):
+ hits.append((filesystem.mtime(path), path))
+
+ if hits:
+ hits.sort(reverse=True)
+ return hits[0][1] # Return the newest file found.
+
+ # We have to default to something, so pick the last one.
+ return filesystem.join(base_dir, *comps)
+
@classmethod
def determine_full_port_name(cls, host, options, port_name):
"""Return a fully-specified port name that can be used to construct objects."""
@@ -123,6 +187,7 @@ class Port(object):
self._image_differ = None
self._server_process_constructor = server_process.ServerProcess # overridable for testing
self._http_lock = None # FIXME: Why does this live on the port object?
+ self._dump_reader = 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
@@ -161,8 +226,7 @@ class Port(object):
return False
def default_pixel_tests(self):
- # FIXME: Disable until they are run by default on build.webkit.org.
- return False
+ return True
def default_smoke_test_only(self):
return False
@@ -196,14 +260,10 @@ class Port(object):
def default_max_locked_shards(self):
"""Return the number of "locked" shards to run in parallel (like the http tests)."""
- return 1
-
- def worker_startup_delay_secs(self):
- # FIXME: If we start workers up too quickly, DumpRenderTree appears
- # to thrash on something and time out its first few tests. Until
- # we can figure out what's going on, sleep a bit in between
- # workers. See https://bugs.webkit.org/show_bug.cgi?id=79147 .
- return 0.1
+ max_locked_shards = int(self.default_child_processes()) / 4
+ if not max_locked_shards:
+ return 1
+ return max_locked_shards
def baseline_path(self):
"""Return the absolute path to the directory to store new baselines in for this port."""
@@ -231,11 +291,7 @@ class Port(object):
def default_baseline_search_path(self):
"""Return a list of absolute paths to directories to search under for
baselines. The directories are searched in order."""
- search_paths = []
- search_paths.append(self.name())
- if self.name() != self.port_name:
- search_paths.append(self.port_name)
- return map(self._webkit_baseline_path, search_paths)
+ return map(self._webkit_baseline_path, self.FALLBACK_PATHS[self.version()])
@memoized
def _compare_baseline(self):
@@ -245,20 +301,55 @@ class Port(object):
return factory.get(target_port).default_baseline_search_path()
return []
- def check_build(self, needs_http, printer):
- """This routine is used to ensure that the build is up to date
- and all the needed binaries are present."""
- if self.get_option('build'):
- 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():
+ def _check_file_exists(self, 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 self._filesystem.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
+ def check_build(self, needs_http, printer):
+ result = True
+
+ dump_render_tree_binary_path = self._path_to_driver()
+ result = self._check_file_exists(dump_render_tree_binary_path,
+ 'test driver') and result
+ if not 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 = self._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 and wdiff aren't available, but we will at least log messages.
+ self._pretty_patch_available = self.check_pretty_patch()
+ self._wdiff_available = self.check_wdiff()
+
+ if self._dump_reader:
+ result = self._dump_reader.check_is_functional() and result
+
+ return test_run_results.OK_EXIT_STATUS if result else test_run_results.UNEXPECTED_ERROR_EXIT_STATUS
+
def _check_driver(self):
driver_path = self._path_to_driver()
if not self._filesystem.exists(driver_path):
@@ -276,9 +367,21 @@ class Port(object):
This step can be skipped with --nocheck-sys-deps.
Returns whether the system is properly configured."""
- if needs_http:
- return self.check_httpd()
- return True
+ cmd = [self._path_to_driver(), '--check-layout-test-sys-deps']
+
+ local_error = ScriptError()
+
+ def error_handler(script_error):
+ local_error.exit_code = script_error.exit_code
+
+ output = self._executive.run_command(cmd, error_handler=error_handler)
+ if local_error.exit_code:
+ _log.error('System dependencies check failed.')
+ _log.error('To override, invoke with --nocheck-sys-deps')
+ _log.error('')
+ _log.error(output)
+ return test_run_results.SYS_DEPS_EXIT_STATUS
+ return test_run_results.OK_EXIT_STATUS
def check_image_diff(self, override_step=None, logging=True):
"""This routine is used to check whether image_diff binary exists."""
@@ -640,6 +743,26 @@ class Port(object):
def is_test_file(filesystem, dirname, filename):
return Port._has_supported_extension(filesystem, filename) and not Port.is_reference_html_file(filesystem, dirname, filename)
+ ALL_TEST_TYPES = ['audio', 'harness', 'pixel', 'ref', 'text', 'unknown']
+
+ def test_type(self, test_name):
+ fs = self._filesystem
+ if fs.exists(self.expected_filename(test_name, '.png')):
+ return 'pixel'
+ if fs.exists(self.expected_filename(test_name, '.wav')):
+ return 'audio'
+ if self.reference_files(test_name):
+ return 'ref'
+ txt = self.expected_text(test_name)
+ if txt:
+ if 'layer at (0,0) size 800x600' in txt:
+ return 'pixel'
+ for line in txt.splitlines():
+ if line.startswith('FAIL') or line.startswith('TIMEOUT') or line.startswith('PASS'):
+ return 'harness'
+ return 'text'
+ return 'unknown'
+
def test_key(self, test_name):
"""Turns a test name into a list with two sublists, the natural key of the
dirname, and the natural key of the basename.
@@ -734,6 +857,9 @@ class Port(object):
def path_from_webkit_base(self, *comps):
return self._webkit_finder.path_from_webkit_base(*comps)
+ def path_from_chromium_base(self, *comps):
+ return self._webkit_finder.path_from_chromium_base(*comps)
+
def path_to_script(self, script_name):
return self._webkit_finder.path_to_script(script_name)
@@ -783,7 +909,7 @@ class Port(object):
return False
def is_chromium(self):
- return False
+ return True
def name(self):
"""Returns a name that uniquely identifies this particular type of port
@@ -844,13 +970,26 @@ class Port(object):
def default_results_directory(self):
"""Absolute path to the default place to store the test results."""
- # 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('layout-test-results')
+ try:
+ return self.path_from_chromium_base('webkit', self.get_option('configuration'), 'layout-test-results')
+ except AssertionError:
+ return self._build_path('layout-test-results')
def setup_test_run(self):
"""Perform port-specific work at the beginning of a test run."""
- pass
+ # Delete the disk cache if any to ensure a clean test run.
+ dump_render_tree_binary_path = self._path_to_driver()
+ cachedir = self._filesystem.dirname(dump_render_tree_binary_path)
+ cachedir = self._filesystem.join(cachedir, "cache")
+ if self._filesystem.exists(cachedir):
+ self._filesystem.rmtree(cachedir)
+
+ if self._dump_reader:
+ self._filesystem.maybe_make_directory(self._dump_reader.crash_dumps_directory())
+
+ def num_workers(self, requested_num_workers):
+ """Returns the number of available workers (possibly less than the number requested)."""
+ return requested_num_workers
def clean_up_test_run(self):
"""Perform port-specific work at the end of a test run."""
@@ -880,6 +1019,8 @@ class Port(object):
'CHROME_DEVEL_SANDBOX',
'CHROME_IPC_LOGGING',
'ASAN_OPTIONS',
+ 'VALGRIND_LIB',
+ 'VALGRIND_LIB_INNER',
]
if self.host.platform.is_linux() or self.host.platform.is_freebsd():
variables_to_copy += [
@@ -900,6 +1041,7 @@ class Port(object):
if self.host.platform.is_win():
variables_to_copy += [
'PATH',
+ 'GYP_DEFINES', # Required to locate win sdk.
]
if self.host.platform.is_cygwin():
variables_to_copy += [
@@ -930,7 +1072,15 @@ class Port(object):
"""If a port needs to reconfigure graphics settings or do other
things to ensure a known test configuration, it should override this
method."""
- pass
+ 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 = self._executive.popen([helper_path],
+ stdin=self._executive.PIPE, stdout=self._executive.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 requires_http_server(self):
"""Does the port require an HTTP server for running tests? This could
@@ -972,7 +1122,16 @@ class Port(object):
"""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
+ if self._helper:
+ _log.debug("Stopping layout test helper")
+ try:
+ self._helper.stdin.write("x\n")
+ self._helper.stdin.close()
+ self._helper.wait()
+ except IOError, e:
+ pass
+ finally:
+ self._helper = None
def stop_http_server(self):
"""Shut down the http server if it is running. Do nothing if it isn't."""
@@ -1013,7 +1172,7 @@ class Port(object):
Returns a dictionary, each key representing a macro term ('win', for example),
and value being a list of valid configuration specifiers (such as ['xp', 'vista', 'win7'])."""
- return {}
+ return self.CONFIGURATION_SPECIFIER_MACROS
def all_baseline_variants(self):
"""Returns a list of platform names sufficient to cover all the baselines.
@@ -1021,7 +1180,41 @@ class Port(object):
The list should be sorted so that a later platform will reuse
an earlier platform's baselines if they are the same (e.g.,
'snowleopard' should precede 'leopard')."""
- raise NotImplementedError
+ return self.ALL_BASELINE_VARIANTS
+
+ def _generate_all_test_configurations(self):
+ """Returns a sequence of the TestConfigurations the port supports."""
+ # By default, we assume we want to test every graphics type in
+ # every configuration on every system.
+ test_configurations = []
+ for version, architecture in self.ALL_SYSTEMS:
+ for build_type in self.ALL_BUILD_TYPES:
+ test_configurations.append(TestConfiguration(version, architecture, build_type))
+ return test_configurations
+
+ try_builder_names = frozenset([
+ 'linux_layout',
+ 'mac_layout',
+ 'win_layout',
+ 'linux_layout_rel',
+ 'mac_layout_rel',
+ 'win_layout_rel',
+ ])
+
+ def warn_if_bug_missing_in_test_expectations(self):
+ return True
+
+ def _port_specific_expectations_files(self):
+ paths = []
+ paths.append(self.path_from_chromium_base('skia', 'skia_test_expectations.txt'))
+ paths.append(self._filesystem.join(self.layout_tests_dir(), 'NeverFixTests'))
+ paths.append(self._filesystem.join(self.layout_tests_dir(), 'StaleTestExpectations'))
+ paths.append(self._filesystem.join(self.layout_tests_dir(), 'SlowTests'))
+
+ builder_name = self.get_option('builder_name', 'DUMMY_BUILDER_NAME')
+ if builder_name == 'DUMMY_BUILDER_NAME' or '(deps)' in builder_name or builder_name in self.try_builder_names:
+ paths.append(self.path_from_chromium_base('webkit', 'tools', 'layout_tests', 'test_expectations.txt'))
+ return paths
def expectations_dict(self):
"""Returns an OrderedDict of name -> expectations strings.
@@ -1067,29 +1260,13 @@ class Port(object):
_log.warning("Unexpected ignore mode: '%s'." % ignore_mode)
return {}
- def _port_specific_expectations_files(self):
- # Unlike baseline_search_path, we only want to search [WK2-PORT, PORT-VERSION, PORT] and any directories
- # included via --additional-platform-directory, not the full casade.
- search_paths = [self.port_name]
-
- non_wk2_name = self.name().replace('-wk2', '')
- if non_wk2_name != self.port_name:
- search_paths.append(non_wk2_name)
-
- search_paths.extend(self.get_option("additional_platform_directory", []))
-
- return [self._filesystem.join(self._webkit_baseline_path(d), 'TestExpectations') for d in search_paths]
-
def expectations_files(self):
return [self.path_to_generic_test_expectations_file()] + self._port_specific_expectations_files()
def 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', <webkitRepositoryPath>) tuple."""
-
- # We use LayoutTest directory here because webkit_base isn't a part of WebKit repository in Chromium port
- # where turnk isn't checked out as a whole.
- return [('blink', self.layout_tests_dir())]
+ """Returns a list of (repository_name, repository_path) tuples of its depending code base."""
+ return [('blink', self.layout_tests_dir()),
+ ('chromium', self.path_from_chromium_base('build'))]
_WDIFF_DEL = '##WDIFF_DEL##'
_WDIFF_ADD = '##WDIFF_ADD##'
@@ -1182,6 +1359,9 @@ class Port(object):
def default_configuration(self):
return self._config.default_configuration()
+ def clobber_old_port_specific_results(self):
+ pass
+
#
# PROTECTED ROUTINES
#
@@ -1219,7 +1399,7 @@ class Port(object):
if self._is_redhat_based():
return 'fedora-httpd-' + self._apache_version() + '.conf'
if self._is_debian_based():
- return 'apache2-debian-httpd.conf'
+ return 'debian-httpd-' + self._apache_version() + '.conf'
# All platforms use apache2 except for CYGWIN (and Mac OS X Tiger and prior, which we no longer support).
return "apache2-httpd.conf"
@@ -1239,14 +1419,6 @@ class Port(object):
config_file_name = self._apache_config_file_name_for_platform(sys.platform)
return self._filesystem.join(self.layout_tests_dir(), 'http', 'conf', config_file_name)
- def _build_path(self, *comps):
- build_directory = self.get_option('build_directory')
- if build_directory:
- root_directory = self._filesystem.join(build_directory, self.get_option('configuration'))
- else:
- root_directory = self._config.build_directory(self.get_option('configuration'))
- return self._filesystem.join(self._filesystem.abspath(root_directory), *comps)
-
def _path_to_driver(self, configuration=None):
"""Returns the full path to the test driver."""
return self._build_path(self.driver_name())
@@ -1302,17 +1474,21 @@ class Port(object):
given platform."""
return self._filesystem.join(self.layout_tests_dir(), 'platform', platform)
- # FIXME: Belongs on a Platform object.
- def _generate_all_test_configurations(self):
- """Generates a list of TestConfiguration instances, representing configurations
- for a platform across all OSes, architectures, build and graphics types."""
- raise NotImplementedError('Port._generate_all_test_configurations')
-
def _driver_class(self):
"""Returns the port's driver implementation."""
return driver.Driver
def _get_crash_log(self, name, pid, stdout, stderr, newer_than):
+ if stderr and 'AddressSanitizer' in stderr:
+ # Running the AddressSanitizer take a lot of memory, so we need to
+ # serialize access to it across all the concurrently running drivers.
+
+ # FIXME: investigate using LLVM_SYMBOLIZER_PATH here to reduce the overhead.
+ asan_filter_path = self.path_from_chromium_base('tools', 'valgrind', 'asan', 'asan_symbolize.py')
+ if self._filesystem.exists(asan_filter_path):
+ output = self._executive.run_command(['flock', sys.executable, asan_filter_path], input=stderr, decode_output=False)
+ stderr = self._executive.run_command(['c++filt'], input=output, decode_output=False)
+
name_str = name or '<unknown process name>'
pid_str = str(pid or '<unknown>')
stdout_lines = (stdout or '<empty>').decode('utf8', 'replace').splitlines()
@@ -1331,7 +1507,76 @@ class Port(object):
pass
def virtual_test_suites(self):
- return []
+ return [
+ VirtualTestSuite('gpu',
+ 'fast/canvas',
+ ['--enable-accelerated-2d-canvas']),
+ VirtualTestSuite('gpu',
+ 'canvas/philip',
+ ['--enable-accelerated-2d-canvas']),
+ VirtualTestSuite('threaded',
+ 'compositing/visibility',
+ ['--enable-threaded-compositing']),
+ VirtualTestSuite('threaded',
+ 'compositing/webgl',
+ ['--enable-threaded-compositing']),
+ VirtualTestSuite('gpu',
+ 'fast/hidpi',
+ ['--force-compositing-mode']),
+ VirtualTestSuite('softwarecompositing',
+ 'compositing',
+ ['--enable-software-compositing', '--disable-gpu-compositing'],
+ use_legacy_naming=True),
+ VirtualTestSuite('deferred',
+ 'fast/images',
+ ['--enable-deferred-image-decoding', '--enable-per-tile-painting', '--force-compositing-mode']),
+ VirtualTestSuite('deferred',
+ 'inspector/timeline',
+ ['--enable-deferred-image-decoding', '--enable-per-tile-painting', '--force-compositing-mode']),
+ VirtualTestSuite('gpu/compositedscrolling/overflow',
+ 'compositing/overflow',
+ ['--enable-accelerated-overflow-scroll'],
+ use_legacy_naming=True),
+ VirtualTestSuite('gpu/compositedscrolling/scrollbars',
+ 'scrollbars',
+ ['--enable-accelerated-overflow-scroll'],
+ use_legacy_naming=True),
+ VirtualTestSuite('threaded',
+ 'animations',
+ ['--enable-threaded-compositing']),
+ VirtualTestSuite('threaded',
+ 'transitions',
+ ['--enable-threaded-compositing']),
+ VirtualTestSuite('legacy-animations-engine',
+ 'animations',
+ ['--disable-web-animations-css']),
+ VirtualTestSuite('legacy-animations-engine',
+ 'transitions',
+ ['--disable-web-animations-css']),
+ VirtualTestSuite('stable',
+ 'webexposed',
+ ['--stable-release-mode']),
+ VirtualTestSuite('stable',
+ 'media/stable',
+ ['--stable-release-mode']),
+ VirtualTestSuite('android',
+ 'fullscreen',
+ ['--force-compositing-mode', '--allow-webui-compositing', '--enable-threaded-compositing',
+ '--enable-fixed-position-compositing', '--enable-accelerated-overflow-scroll', '--enable-accelerated-scrollable-frames',
+ '--enable-composited-scrolling-for-frames', '--enable-gesture-tap-highlight', '--enable-pinch',
+ '--enable-overlay-fullscreen-video', '--enable-overlay-scrollbars', '--enable-overscroll-notifications',
+ '--enable-fixed-layout', '--enable-viewport', '--disable-canvas-aa',
+ '--disable-composited-antialiasing']),
+ VirtualTestSuite('implsidepainting',
+ 'inspector/timeline',
+ ['--enable-threaded-compositing', '--enable-impl-side-painting', '--force-compositing-mode']),
+ VirtualTestSuite('fasttextautosizing',
+ 'fast/text-autosizing',
+ ['--enable-fast-text-autosizing']),
+ VirtualTestSuite('serviceworker',
+ 'http/tests/serviceworker',
+ ['--enable-service-worker']),
+ ]
@memoized
def populated_virtual_test_suites(self):
@@ -1445,10 +1690,53 @@ class Port(object):
return cygpath(path)
return path
+ def _build_path(self, *comps):
+ return self._build_path_with_configuration(None, *comps)
+
+ def _build_path_with_configuration(self, configuration, *comps):
+ # Note that we don't do the option caching that the
+ # base class does, because finding the right directory is relatively
+ # fast.
+ configuration = configuration or self.get_option('configuration')
+ return self._static_build_path(self._filesystem, self.get_option('build_directory'),
+ self.path_from_chromium_base(), configuration, comps)
+
+ 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 = self._filesystem.mtime(debug_path)
+ release_mtime = self._filesystem.mtime(release_path)
+
+ if (debug_mtime > release_mtime and configuration == 'Release' or
+ release_mtime > debug_mtime and configuration == 'Debug'):
+ most_recent_binary = 'Release' if configuration == 'Debug' else 'Debug'
+ _log.warning('You are running the %s binary. However the %s binary appears to be more recent. '
+ 'Please pass --%s.', configuration, most_recent_binary, most_recent_binary.lower())
+ _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)
class VirtualTestSuite(object):
- def __init__(self, name, base, args, tests=None):
- self.name = name
+ def __init__(self, name, base, args, use_legacy_naming=False, tests=None):
+ if use_legacy_naming:
+ self.name = 'virtual/' + name
+ else:
+ if name.find('/') != -1:
+ _log.error("Virtual test suites names cannot contain /'s: %s" % name)
+ return
+ self.name = 'virtual/' + name + '/' + base
self.base = base
self.args = args
self.tests = tests or set()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
index 5e2718d4a23..3778c04466f 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
@@ -43,6 +43,7 @@ from webkitpy.common.system.executive_mock import MockExecutive, MockExecutive2
from webkitpy.common.system.systemhost_mock import MockSystemHost
from webkitpy.layout_tests.port import Port, Driver, DriverOutput
+from webkitpy.layout_tests.port.base import VirtualTestSuite
from webkitpy.layout_tests.port.test import add_unit_tests_to_mock_filesystem, TestPort
class PortTest(unittest.TestCase):
@@ -214,14 +215,14 @@ class PortTest(unittest.TestCase):
def test_nonexistant_expectations(self):
port = self.make_port(port_name='foo')
- port.expectations_files = lambda: ['/mock-checkout/LayoutTests/platform/exists/TestExpectations', '/mock-checkout/LayoutTests/platform/nonexistant/TestExpectations']
- port._filesystem.write_text_file('/mock-checkout/LayoutTests/platform/exists/TestExpectations', '')
- self.assertEqual('\n'.join(port.expectations_dict().keys()), '/mock-checkout/LayoutTests/platform/exists/TestExpectations')
+ port.expectations_files = lambda: ['/mock-checkout/third_party/WebKit/LayoutTests/platform/exists/TestExpectations', '/mock-checkout/third_party/WebKit/LayoutTests/platform/nonexistant/TestExpectations']
+ port._filesystem.write_text_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/exists/TestExpectations', '')
+ self.assertEqual('\n'.join(port.expectations_dict().keys()), '/mock-checkout/third_party/WebKit/LayoutTests/platform/exists/TestExpectations')
def test_additional_expectations(self):
port = self.make_port(port_name='foo')
port.port_name = 'foo'
- port._filesystem.write_text_file('/mock-checkout/LayoutTests/platform/foo/TestExpectations', '')
+ port._filesystem.write_text_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/foo/TestExpectations', '')
port._filesystem.write_text_file(
'/tmp/additional-expectations-1.txt', 'content1\n')
port._filesystem.write_text_file(
@@ -231,15 +232,15 @@ class PortTest(unittest.TestCase):
port._options.additional_expectations = [
'/tmp/additional-expectations-1.txt']
- self.assertEqual('\n'.join(port.expectations_dict().values()), '\ncontent1\n')
+ self.assertEqual('\n'.join(port.expectations_dict().values()), 'content1\n')
port._options.additional_expectations = [
'/tmp/nonexistent-file', '/tmp/additional-expectations-1.txt']
- self.assertEqual('\n'.join(port.expectations_dict().values()), '\ncontent1\n')
+ self.assertEqual('\n'.join(port.expectations_dict().values()), 'content1\n')
port._options.additional_expectations = [
'/tmp/additional-expectations-1.txt', '/tmp/additional-expectations-2.txt']
- self.assertEqual('\n'.join(port.expectations_dict().values()), '\ncontent1\n\ncontent2\n')
+ self.assertEqual('\n'.join(port.expectations_dict().values()), 'content1\n\ncontent2\n')
def test_additional_env_var(self):
port = self.make_port(options=optparse.Values({'additional_env_var': ['FOO=BAR', 'BAR=FOO']}))
@@ -444,3 +445,23 @@ class KeyCompareTest(unittest.TestCase):
self.assert_cmp('/ab', '/a/a/b', -1)
self.assert_cmp('/a/a/b', '/ab', 1)
self.assert_cmp('/foo-bar/baz', '/foo/baz', -1)
+
+
+class VirtualTestSuiteTest(unittest.TestCase):
+ def test_basic(self):
+ suite = VirtualTestSuite('suite', 'base/foo', ['--args'])
+ self.assertEqual(suite.name, 'virtual/suite/base/foo')
+ self.assertEqual(suite.base, 'base/foo')
+ self.assertEqual(suite.args, ['--args'])
+
+ def test_no_slash(self):
+ suite = VirtualTestSuite('suite/bar', 'base/foo', ['--args'])
+ self.assertFalse(hasattr(suite, 'name'))
+ self.assertFalse(hasattr(suite, 'base'))
+ self.assertFalse(hasattr(suite, 'args'))
+
+ def test_legacy(self):
+ suite = VirtualTestSuite('suite/bar', 'base/foo', ['--args'], use_legacy_naming=True)
+ self.assertEqual(suite.name, 'virtual/suite/bar')
+ self.assertEqual(suite.base, 'base/foo')
+ self.assertEqual(suite.args, ['--args'])
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/builders.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/builders.py
index c07c82b791d..47439ea97fe 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/builders.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/builders.py
@@ -51,6 +51,7 @@ _exact_matches = {
"WebKit Mac10.7 (dbg)": {"port_name": "mac-lion"},
"WebKit Mac10.8": {"port_name": "mac-mountainlion"},
"WebKit Mac10.8 (retina)": {"port_name": "mac-retina"},
+ "WebKit Mac10.9": {"port_name": "mac-mavericks"},
"WebKit Android (Nexus4)": {"port_name": "android"},
}
@@ -64,6 +65,7 @@ _deps_builders = {
"mac-snowleopard": "WebKit Mac10.6 (deps)",
"mac-lion": "WebKit Mac10.6 (deps)",
"mac-mountainlion": "WebKit Mac10.6 (deps)",
+ "mac-mavericks": "WebKit Mac10.6 (deps)",
"mac-retina": "WebKit Mac10.6 (deps)",
}
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium.py
deleted file mode 100644
index 68286acc06d..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium.py
+++ /dev/null
@@ -1,422 +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.
-
-"""Chromium implementations of the Port interface."""
-
-import base64
-import errno
-import logging
-import re
-import signal
-import subprocess
-import sys
-import time
-
-from webkitpy.common.system import executive
-from webkitpy.layout_tests.models.test_configuration import TestConfiguration
-from webkitpy.layout_tests.port.base import Port, VirtualTestSuite
-
-
-_log = logging.getLogger(__name__)
-
-
-class ChromiumPort(Port):
- """Abstract base class for Chromium implementations of the Port class."""
-
- ALL_SYSTEMS = (
- ('snowleopard', 'x86'),
- ('lion', 'x86'),
-
- # FIXME: We treat Retina (High-DPI) devices as if they are running
- # a different operating system version. This isn't accurate, but will work until
- # we need to test and support baselines across multiple O/S versions.
- ('retina', 'x86'),
-
- ('mountainlion', 'x86'),
- ('xp', 'x86'),
- ('win7', 'x86'),
- ('lucid', 'x86'),
- ('lucid', 'x86_64'),
- # FIXME: Technically this should be 'arm', but adding a third architecture type breaks TestConfigurationConverter.
- # If we need this to be 'arm' in the future, then we first have to fix TestConfigurationConverter.
- ('icecreamsandwich', 'x86'),
- )
-
- ALL_BASELINE_VARIANTS = [
- 'mac-mountainlion', 'mac-retina', 'mac-lion', 'mac-snowleopard',
- 'win-win7', 'win-xp',
- 'linux-x86_64', 'linux-x86',
- ]
-
- CONFIGURATION_SPECIFIER_MACROS = {
- 'mac': ['snowleopard', 'lion', 'retina', 'mountainlion'],
- 'win': ['xp', 'win7'],
- 'linux': ['lucid'],
- 'android': ['icecreamsandwich'],
- }
-
- DEFAULT_BUILD_DIRECTORIES = ('out',)
-
- # overridden in subclasses.
- FALLBACK_PATHS = {}
-
- @classmethod
- def latest_platform_fallback_path(cls):
- return cls.FALLBACK_PATHS[cls.SUPPORTED_VERSIONS[-1]]
-
- @classmethod
- def _static_build_path(cls, filesystem, build_directory, chromium_base, webkit_base, configuration, comps):
- if build_directory:
- return filesystem.join(build_directory, configuration, *comps)
-
- hits = []
- for directory in cls.DEFAULT_BUILD_DIRECTORIES:
- base_dir = filesystem.join(chromium_base, directory, configuration)
- path = filesystem.join(base_dir, *comps)
- if filesystem.exists(path):
- hits.append((filesystem.mtime(path), path))
-
- for directory in cls.DEFAULT_BUILD_DIRECTORIES:
- base_dir = filesystem.join(webkit_base, directory, configuration)
- path = filesystem.join(base_dir, *comps)
- if filesystem.exists(path):
- hits.append((filesystem.mtime(path), path))
-
- if hits:
- hits.sort(reverse=True)
- return hits[0][1] # Return the newest file found.
-
- # We have to default to something, so pick the last one.
- return filesystem.join(base_dir, *comps)
-
- @classmethod
- def _chromium_base_dir(cls, filesystem):
- module_path = filesystem.path_to_module(cls.__module__)
- offset = module_path.find('third_party')
- if offset == -1:
- return filesystem.join(module_path[0:module_path.find('Tools')], 'Source', 'WebKit', 'chromium')
- else:
- return module_path[0:offset]
-
- def __init__(self, host, port_name, **kwargs):
- super(ChromiumPort, self).__init__(host, port_name, **kwargs)
- # All sub-classes override this, but we need an initial value for testing.
- self._chromium_base_dir_path = None
-
- def is_chromium(self):
- return True
-
- def default_max_locked_shards(self):
- """Return the number of "locked" shards to run in parallel (like the http tests)."""
- max_locked_shards = int(self.default_child_processes()) / 4
- if not max_locked_shards:
- return 1
- return max_locked_shards
-
- def default_pixel_tests(self):
- return True
-
- def default_baseline_search_path(self):
- return map(self._webkit_baseline_path, self.FALLBACK_PATHS[self.version()])
-
- def _check_file_exists(self, 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 self._filesystem.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
-
- def check_build(self, needs_http, printer):
- result = True
-
- dump_render_tree_binary_path = self._path_to_driver()
- result = self._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 = self._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 and wdiff aren't available, but we will at least log messages.
- self._pretty_patch_available = self.check_pretty_patch()
- self._wdiff_available = self.check_wdiff()
-
- return result
-
- def check_sys_deps(self, needs_http):
- result = super(ChromiumPort, self).check_sys_deps(needs_http)
-
- cmd = [self._path_to_driver(), '--check-layout-test-sys-deps']
-
- local_error = executive.ScriptError()
-
- def error_handler(script_error):
- local_error.exit_code = script_error.exit_code
-
- output = self._executive.run_command(cmd, error_handler=error_handler)
- if local_error.exit_code:
- _log.error('System dependencies check failed.')
- _log.error('To override, invoke with --nocheck-sys-deps')
- _log.error('')
- _log.error(output)
- return False
- return result
-
- 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 self._chromium_base_dir_path is None:
- self._chromium_base_dir_path = self._chromium_base_dir(self._filesystem)
- return self._filesystem.join(self._chromium_base_dir_path, *comps)
-
- def setup_environ_for_server(self, server_name=None):
- clean_env = super(ChromiumPort, self).setup_environ_for_server(server_name)
- # Webkit Linux (valgrind layout) bot needs these envvars.
- self._copy_value_from_environ_if_set(clean_env, 'VALGRIND_LIB')
- self._copy_value_from_environ_if_set(clean_env, 'VALGRIND_LIB_INNER')
- return clean_env
-
- def default_results_directory(self):
- try:
- return self.path_from_chromium_base('webkit', self.get_option('configuration'), 'layout-test-results')
- except AssertionError:
- return self._build_path('layout-test-results')
-
- def setup_test_run(self):
- super(ChromiumPort, self).setup_test_run()
- # Delete the disk cache if any to ensure a clean test run.
- dump_render_tree_binary_path = self._path_to_driver()
- cachedir = self._filesystem.dirname(dump_render_tree_binary_path)
- cachedir = self._filesystem.join(cachedir, "cache")
- if self._filesystem.exists(cachedir):
- self._filesystem.rmtree(cachedir)
-
- 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")
- try:
- self._helper.stdin.write("x\n")
- self._helper.stdin.close()
- self._helper.wait()
- except IOError, e:
- pass
- finally:
- self._helper = None
-
- def configuration_specifier_macros(self):
- return self.CONFIGURATION_SPECIFIER_MACROS
-
- def all_baseline_variants(self):
- return self.ALL_BASELINE_VARIANTS
-
- def _generate_all_test_configurations(self):
- """Returns a sequence of the TestConfigurations the port supports."""
- # By default, we assume we want to test every graphics type in
- # every configuration on every system.
- test_configurations = []
- for version, architecture in self.ALL_SYSTEMS:
- for build_type in self.ALL_BUILD_TYPES:
- test_configurations.append(TestConfiguration(version, architecture, build_type))
- return test_configurations
-
- try_builder_names = frozenset([
- 'linux_layout',
- 'mac_layout',
- 'win_layout',
- 'linux_layout_rel',
- 'mac_layout_rel',
- 'win_layout_rel',
- ])
-
- def warn_if_bug_missing_in_test_expectations(self):
- return True
-
- def _port_specific_expectations_files(self):
- paths = []
- paths.append(self.path_from_chromium_base('skia', 'skia_test_expectations.txt'))
- paths.append(self._filesystem.join(self.layout_tests_dir(), 'NeverFixTests'))
- paths.append(self._filesystem.join(self.layout_tests_dir(), 'SlowTests'))
-
- builder_name = self.get_option('builder_name', 'DUMMY_BUILDER_NAME')
- if builder_name == 'DUMMY_BUILDER_NAME' or '(deps)' in builder_name or builder_name in self.try_builder_names:
- paths.append(self.path_from_chromium_base('webkit', 'tools', 'layout_tests', 'test_expectations.txt'))
- return paths
-
- def repository_paths(self):
- repos = super(ChromiumPort, self).repository_paths()
- repos.append(('chromium', self.path_from_chromium_base('build')))
- return repos
-
- def _get_crash_log(self, name, pid, stdout, stderr, newer_than):
- if stderr and 'AddressSanitizer' in stderr:
- # Running the AddressSanitizer take a lot of memory, so we need to
- # serialize access to it across all the concurrently running drivers.
-
- # FIXME: investigate using LLVM_SYMBOLIZER_PATH here to reduce the overhead.
- asan_filter_path = self.path_from_chromium_base('tools', 'valgrind', 'asan', 'asan_symbolize.py')
- if self._filesystem.exists(asan_filter_path):
- output = self._executive.run_command(['flock', sys.executable, asan_filter_path], input=stderr, decode_output=False)
- stderr = self._executive.run_command(['c++filt'], input=output, decode_output=False)
-
- return super(ChromiumPort, self)._get_crash_log(name, pid, stdout, stderr, newer_than)
-
- def virtual_test_suites(self):
- return [
- VirtualTestSuite('virtual/gpu/fast/canvas',
- 'fast/canvas',
- ['--enable-accelerated-2d-canvas']),
- VirtualTestSuite('virtual/gpu/canvas/philip',
- 'canvas/philip',
- ['--enable-accelerated-2d-canvas']),
- VirtualTestSuite('virtual/threaded/compositing/visibility',
- 'compositing/visibility',
- ['--enable-threaded-compositing']),
- VirtualTestSuite('virtual/threaded/compositing/webgl',
- 'compositing/webgl',
- ['--enable-threaded-compositing']),
- VirtualTestSuite('virtual/gpu/fast/hidpi',
- 'fast/hidpi',
- ['--force-compositing-mode']),
- VirtualTestSuite('virtual/softwarecompositing',
- 'compositing',
- ['--enable-software-compositing', '--disable-gpu-compositing']),
- VirtualTestSuite('virtual/deferred/fast/images',
- 'fast/images',
- ['--enable-deferred-image-decoding', '--enable-per-tile-painting', '--force-compositing-mode']),
- VirtualTestSuite('virtual/gpu/compositedscrolling/overflow',
- 'compositing/overflow',
- ['--enable-accelerated-overflow-scroll']),
- VirtualTestSuite('virtual/gpu/compositedscrolling/scrollbars',
- 'scrollbars',
- ['--enable-accelerated-overflow-scroll']),
- VirtualTestSuite('virtual/threaded/animations',
- 'animations',
- ['--enable-threaded-compositing']),
- VirtualTestSuite('virtual/threaded/transitions',
- 'transitions',
- ['--enable-threaded-compositing']),
- VirtualTestSuite('virtual/web-animations-css/animations',
- 'animations',
- ['--enable-web-animations-css']),
- VirtualTestSuite('virtual/stable/webexposed',
- 'webexposed',
- ['--stable-release-mode']),
- VirtualTestSuite('virtual/stable/media',
- 'media/stable',
- ['--stable-release-mode']),
- VirtualTestSuite('virtual/android/fullscreen',
- 'fullscreen',
- ['--force-compositing-mode', '--allow-webui-compositing', '--enable-threaded-compositing',
- '--enable-fixed-position-compositing', '--enable-accelerated-overflow-scroll', '--enable-accelerated-scrollable-frames',
- '--enable-composited-scrolling-for-frames', '--enable-gesture-tap-highlight', '--enable-pinch',
- '--enable-overlay-fullscreen-video', '--enable-overlay-scrollbars', '--enable-overscroll-notifications',
- '--enable-fixed-layout', '--enable-viewport', '--disable-canvas-aa',
- '--disable-composited-antialiasing']),
- ]
-
- #
- # PROTECTED METHODS
- #
- # These routines should only be called by other methods in this file
- # or any subclasses.
- #
-
- def _build_path(self, *comps):
- return self._build_path_with_configuration(None, *comps)
-
- def _build_path_with_configuration(self, configuration, *comps):
- # Note that we don't do the option caching that the
- # base class does, because finding the right directory is relatively
- # fast.
- configuration = configuration or self.get_option('configuration')
- return self._static_build_path(self._filesystem, self.get_option('build_directory'),
- self.path_from_chromium_base(), self.path_from_webkit_base(), configuration, comps)
-
- def _path_to_image_diff(self):
- binary_name = 'image_diff'
- return self._build_path(binary_name)
-
- 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 = self._filesystem.mtime(debug_path)
- release_mtime = self._filesystem.mtime(release_path)
-
- if (debug_mtime > release_mtime and configuration == 'Release' or
- release_mtime > debug_mtime and configuration == 'Debug'):
- most_recent_binary = 'Release' if configuration == 'Debug' else 'Debug'
- _log.warning('You are running the %s binary. However the %s binary appears to be more recent. '
- 'Please pass --%s.', configuration, most_recent_binary, most_recent_binary.lower())
- _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)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium_port_testcase.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium_port_testcase.py
deleted file mode 100644
index ade4233fbb9..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium_port_testcase.py
+++ /dev/null
@@ -1,230 +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.thirdparty.unittest2 as unittest
-
-from webkitpy.common.system import logtesting
-from webkitpy.common.system.executive_mock import MockExecutive2
-from webkitpy.common.system.systemhost_mock import MockSystemHost
-from webkitpy.tool.mocktool import MockOptions
-
-import android
-import linux
-import mac
-import win
-
-from webkitpy.layout_tests.models.test_configuration import TestConfiguration
-from webkitpy.layout_tests.port import port_testcase
-
-
-class FakePrinter(object):
- def write_update(self, msg):
- pass
-
- def write_throttled_update(self, msg):
- pass
-
-
-class ChromiumPortTestCase(port_testcase.PortTestCase):
-
- def test_check_build(self):
- port = self.make_port()
- port.check_build(needs_http=True, printer=FakePrinter())
-
- def test_default_max_locked_shards(self):
- port = self.make_port()
- port.default_child_processes = lambda: 16
- self.assertEqual(port.default_max_locked_shards(), 4)
- port.default_child_processes = lambda: 2
- self.assertEqual(port.default_max_locked_shards(), 1)
-
- def test_default_pixel_tests(self):
- self.assertEqual(self.make_port().default_pixel_tests(), True)
-
- def test_missing_symbol_to_skipped_tests(self):
- # Test that we get the chromium skips and not the webkit default skips
- port = self.make_port()
- skip_dict = port._missing_symbol_to_skipped_tests()
- if port.PORT_HAS_AUDIO_CODECS_BUILT_IN:
- self.assertEqual(skip_dict, {})
- else:
- self.assertTrue('ff_mp3_decoder' in skip_dict)
- self.assertFalse('WebGLShader' in skip_dict)
-
- def test_all_test_configurations(self):
- """Validate the complete set of configurations this port knows about."""
- port = self.make_port()
- self.assertEqual(set(port.all_test_configurations()), set([
- TestConfiguration('snowleopard', 'x86', 'debug'),
- TestConfiguration('snowleopard', 'x86', 'release'),
- TestConfiguration('lion', 'x86', 'debug'),
- TestConfiguration('lion', 'x86', 'release'),
- TestConfiguration('retina', 'x86', 'debug'),
- TestConfiguration('retina', 'x86', 'release'),
- TestConfiguration('mountainlion', 'x86', 'debug'),
- TestConfiguration('mountainlion', 'x86', 'release'),
- TestConfiguration('xp', 'x86', 'debug'),
- TestConfiguration('xp', 'x86', 'release'),
- TestConfiguration('win7', 'x86', 'debug'),
- TestConfiguration('win7', 'x86', 'release'),
- TestConfiguration('lucid', 'x86', 'debug'),
- TestConfiguration('lucid', 'x86', 'release'),
- TestConfiguration('lucid', 'x86_64', 'debug'),
- TestConfiguration('lucid', 'x86_64', 'release'),
- TestConfiguration('icecreamsandwich', 'x86', 'debug'),
- TestConfiguration('icecreamsandwich', 'x86', 'release'),
- ]))
-
- class TestMacPort(mac.MacPort):
- def __init__(self, options=None):
- options = options or MockOptions()
- mac.MacPort.__init__(self, MockSystemHost(os_name='mac', os_version='leopard'), 'mac-leopard', options=options)
-
- def default_configuration(self):
- self.default_configuration_called = True
- return 'default'
-
- class TestAndroidPort(android.AndroidPort):
- def __init__(self, options=None):
- options = options or MockOptions()
- android.AndroidPort.__init__(self, MockSystemHost(os_name='android', os_version='icecreamsandwich'), 'android', options=options)
-
- def default_configuration(self):
- self.default_configuration_called = True
- return 'default'
-
- class TestLinuxPort(linux.LinuxPort):
- def __init__(self, options=None):
- options = options or MockOptions()
- linux.LinuxPort.__init__(self, MockSystemHost(os_name='linux', os_version='lucid'), 'linux-x86', options=options)
-
- def default_configuration(self):
- self.default_configuration_called = True
- return 'default'
-
- class TestWinPort(win.WinPort):
- def __init__(self, options=None):
- options = options or MockOptions()
- win.WinPort.__init__(self, MockSystemHost(os_name='win', os_version='xp'), 'win-xp', options=options)
-
- def default_configuration(self):
- self.default_configuration_called = True
- return 'default'
-
- def test_default_configuration(self):
- mock_options = MockOptions()
- port = ChromiumPortTestCase.TestLinuxPort(options=mock_options)
- self.assertEqual(mock_options.configuration, 'default') # pylint: disable=E1101
- self.assertTrue(port.default_configuration_called)
-
- mock_options = MockOptions(configuration=None)
- port = ChromiumPortTestCase.TestLinuxPort(mock_options)
- self.assertEqual(mock_options.configuration, 'default') # pylint: disable=E1101
- self.assertTrue(port.default_configuration_called)
-
- def test_diff_image(self):
- class TestPort(ChromiumPortTestCase.TestLinuxPort):
- def _path_to_image_diff(self):
- return "/path/to/image_diff"
-
- port = ChromiumPortTestCase.TestLinuxPort()
- mock_image_diff = "MOCK Image Diff"
-
- def mock_run_command(args):
- port._filesystem.write_binary_file(args[4], mock_image_diff)
- return 1
-
- # Images are different.
- port._executive = MockExecutive2(run_command_fn=mock_run_command)
- self.assertEqual(mock_image_diff, port.diff_image("EXPECTED", "ACTUAL")[0])
-
- # Images are the same.
- port._executive = MockExecutive2(exit_code=0)
- self.assertEqual(None, port.diff_image("EXPECTED", "ACTUAL")[0])
-
- # There was some error running image_diff.
- port._executive = MockExecutive2(exit_code=2)
- exception_raised = False
- try:
- port.diff_image("EXPECTED", "ACTUAL")
- except ValueError, e:
- exception_raised = True
- self.assertFalse(exception_raised)
-
- def test_diff_image_crashed(self):
- port = ChromiumPortTestCase.TestLinuxPort()
- port._executive = MockExecutive2(exit_code=2)
- self.assertEqual(port.diff_image("EXPECTED", "ACTUAL"), (None, 'image diff returned an exit code of 2'))
-
- def test_expectations_files(self):
- port = self.make_port()
- port.port_name = 'chromium'
-
- generic_path = port.path_to_generic_test_expectations_file()
- chromium_overrides_path = port.path_from_chromium_base(
- 'webkit', 'tools', 'layout_tests', 'test_expectations.txt')
- never_fix_tests_path = port._filesystem.join(port.layout_tests_dir(), 'NeverFixTests')
- slow_tests_path = port._filesystem.join(port.layout_tests_dir(), 'SlowTests')
- skia_overrides_path = port.path_from_chromium_base(
- 'skia', 'skia_test_expectations.txt')
-
- port._filesystem.write_text_file(skia_overrides_path, 'dummay text')
-
- port._options.builder_name = 'DUMMY_BUILDER_NAME'
- self.assertEqual(port.expectations_files(), [generic_path, skia_overrides_path, never_fix_tests_path, slow_tests_path, chromium_overrides_path])
-
- port._options.builder_name = 'builder (deps)'
- self.assertEqual(port.expectations_files(), [generic_path, skia_overrides_path, never_fix_tests_path, slow_tests_path, chromium_overrides_path])
-
- # A builder which does NOT observe the Chromium test_expectations,
- # but still observes the Skia test_expectations...
- port._options.builder_name = 'builder'
- self.assertEqual(port.expectations_files(), [generic_path, skia_overrides_path, never_fix_tests_path, slow_tests_path])
-
- def test_expectations_ordering(self):
- # since we don't implement self.port_name in ChromiumPort.
- pass
-
-
-class ChromiumPortLoggingTest(logtesting.LoggingTestCase):
- def test_check_sys_deps(self):
- port = ChromiumPortTestCase.TestLinuxPort()
-
- # Success
- port._executive = MockExecutive2(exit_code=0)
- self.assertTrue(port.check_sys_deps(needs_http=False))
-
- # Failure
- port._executive = MockExecutive2(exit_code=1,
- output='testing output failure')
- self.assertFalse(port.check_sys_deps(needs_http=False))
- self.assertLog([
- 'ERROR: System dependencies check failed.\n',
- 'ERROR: To override, invoke with --nocheck-sys-deps\n',
- 'ERROR: \n',
- 'ERROR: testing output failure\n'])
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py
deleted file mode 100644
index 66a791fcf42..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.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 StringIO
-import time
-
-from webkitpy.common.system import logtesting
-from webkitpy.common.system.executive_mock import MockExecutive2
-from webkitpy.common.system.systemhost_mock import MockSystemHost
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.tool.mocktool import MockOptions
-
-import chromium
-
-from webkitpy.layout_tests.port import chromium_port_testcase
-from webkitpy.layout_tests.port.driver import DriverInput
-
-
-class ChromiumPortLoggingTest(logtesting.LoggingTestCase):
-
- # FIXME: put this someplace more useful
- def test_check_sys_deps(self):
- port = chromium_port_testcase.ChromiumPortTestCase.TestLinuxPort()
-
- # Success
- port._executive = MockExecutive2(exit_code=0)
- self.assertTrue(port.check_sys_deps(needs_http=False))
-
- # Failure
- port._executive = MockExecutive2(exit_code=1,
- output='testing output failure')
- self.assertFalse(port.check_sys_deps(needs_http=False))
- self.assertLog([
- 'ERROR: System dependencies check failed.\n',
- 'ERROR: To override, invoke with --nocheck-sys-deps\n',
- 'ERROR: \n',
- 'ERROR: testing output failure\n'])
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver.py
index ddbb3db11f0..e7a439d04ba 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver.py
@@ -42,13 +42,16 @@ from webkitpy.common.system.profiler import ProfilerFactory
_log = logging.getLogger(__name__)
+DRIVER_START_TIMEOUT_SECS = 30
+
+
class DriverInput(object):
- def __init__(self, test_name, timeout, image_hash, should_run_pixel_test, args=None):
+ def __init__(self, test_name, timeout, image_hash, should_run_pixel_test, args):
self.test_name = test_name
self.timeout = timeout # in ms
self.image_hash = image_hash
self.should_run_pixel_test = should_run_pixel_test
- self.args = args or []
+ self.args = args
class DriverOutput(object):
@@ -78,6 +81,10 @@ class DriverOutput(object):
return bool(self.error)
+class DeviceFailure(Exception):
+ pass
+
+
class Driver(object):
"""object for running test(s) using content_shell or other driver."""
@@ -135,13 +142,6 @@ class Driver(object):
Returns a DriverOutput object.
"""
- base = self._port.lookup_virtual_test_base(driver_input.test_name)
- if base:
- virtual_driver_input = copy.copy(driver_input)
- virtual_driver_input.test_name = base
- virtual_driver_input.args = self._port.lookup_virtual_test_args(driver_input.test_name)
- return self.run_test(virtual_driver_input, stop_when_done)
-
start_time = time.time()
self.start(driver_input.should_run_pixel_test, driver_input.args)
test_begin_time = time.time()
@@ -260,7 +260,7 @@ class Driver(object):
environment = self._profiler.adjusted_environment(environment)
return environment
- def _start(self, pixel_tests, per_test_args):
+ def _start(self, pixel_tests, per_test_args, wait_for_ready=True):
self.stop()
self._driver_tempdir = self._port._filesystem.mkdtemp(prefix='%s-' % self._port.driver_name())
server_name = self._port.driver_name()
@@ -273,6 +273,24 @@ class Driver(object):
self._server_process.start()
self._current_cmd_line = cmd_line
+ if wait_for_ready:
+ deadline = time.time() + DRIVER_START_TIMEOUT_SECS
+ if not self._wait_for_server_process_output(self._server_process, deadline, '#READY'):
+ _log.error("content_shell took too long to startup.")
+
+ def _wait_for_server_process_output(self, server_process, deadline, text):
+ output = ''
+ line = server_process.read_stdout_line(deadline)
+ while not server_process.timed_out and not server_process.has_crashed() and not text in line.rstrip():
+ output += line
+ line = server_process.read_stdout_line(deadline)
+
+ if server_process.timed_out or server_process.has_crashed():
+ _log.error('Failed to start the %s process: \n%s' % (server_process.name(), output))
+ return False
+
+ return True
+
def _run_post_start_tasks(self):
# Remote drivers may override this to delay post-start tasks until the server has ack'd.
if self._profiler:
@@ -284,7 +302,7 @@ class Driver(object):
def stop(self):
if self._server_process:
- self._server_process.stop(self._port.driver_stop_timeout())
+ self._server_process.stop()
self._server_process = None
if self._profiler:
self._profiler.profile_after_exit()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver_unittest.py
index b0f4a6b7235..9dd5b255e12 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/driver_unittest.py
@@ -42,7 +42,7 @@ from webkitpy.tool.mocktool import MockOptions
class DriverTest(unittest.TestCase):
def make_port(self):
port = Port(MockSystemHost(), 'test', MockOptions(configuration='Release'))
- port._config.build_directory = lambda configuration: '/mock-build'
+ port._config.build_directory = lambda configuration: '/mock-checkout/out/' + configuration
return port
def _assert_wrapper(self, wrapper_string, expected_wrapper):
@@ -82,6 +82,7 @@ class DriverTest(unittest.TestCase):
"#EOF",
])
content_block = driver._read_block(0)
+ self.assertEqual(content_block.content, '')
self.assertEqual(content_block.content_type, 'my_type')
self.assertEqual(content_block.encoding, 'none')
self.assertEqual(content_block.content_hash, 'foobar')
@@ -125,9 +126,9 @@ class DriverTest(unittest.TestCase):
def test_no_timeout(self):
port = TestWebKitPort()
- port._config.build_directory = lambda configuration: '/mock-build'
+ port._config.build_directory = lambda configuration: '/mock-checkout/out/' + configuration
driver = Driver(port, 0, pixel_tests=True, no_timeout=True)
- self.assertEqual(driver.cmd_line(True, []), ['/mock-build/content_shell', '--no-timeout', '--dump-render-tree', '-'])
+ self.assertEqual(driver.cmd_line(True, []), ['/mock-checkout/out/Release/content_shell', '--no-timeout', '--dump-render-tree', '-'])
def test_check_for_driver_crash(self):
port = TestWebKitPort()
@@ -146,7 +147,7 @@ class DriverTest(unittest.TestCase):
def has_crashed(self):
return self.crashed
- def stop(self, timeout):
+ def stop(self, timeout=0.0):
pass
def assert_crash(driver, error_line, crashed, name, pid, unresponsive=False):
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux.py
index 741cc0de5b3..d582210680f 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux.py
@@ -30,7 +30,9 @@ import logging
import re
from webkitpy.common.webkit_finder import WebKitFinder
-from webkitpy.layout_tests.port import chromium
+from webkitpy.layout_tests.breakpad.dump_reader_multipart import DumpReaderLinux
+from webkitpy.layout_tests.models import test_run_results
+from webkitpy.layout_tests.port import base
from webkitpy.layout_tests.port import win
from webkitpy.layout_tests.port import config
@@ -38,7 +40,7 @@ from webkitpy.layout_tests.port import config
_log = logging.getLogger(__name__)
-class LinuxPort(chromium.ChromiumPort):
+class LinuxPort(base.Port):
port_name = 'linux'
SUPPORTED_VERSIONS = ('x86', 'x86_64')
@@ -46,14 +48,15 @@ class LinuxPort(chromium.ChromiumPort):
FALLBACK_PATHS = { 'x86_64': [ 'linux' ] + win.WinPort.latest_platform_fallback_path() }
FALLBACK_PATHS['x86'] = ['linux-x86'] + FALLBACK_PATHS['x86_64']
- DEFAULT_BUILD_DIRECTORIES = ('sconsbuild', 'out')
+ DEFAULT_BUILD_DIRECTORIES = ('out',)
@classmethod
def _determine_driver_path_statically(cls, host, options):
config_object = config.Config(host.executive, host.filesystem)
build_directory = getattr(options, 'build_directory', None)
- webkit_base = WebKitFinder(host.filesystem).webkit_base()
- chromium_base = cls._chromium_base_dir(host.filesystem)
+ finder = WebKitFinder(host.filesystem)
+ webkit_base = finder.webkit_base()
+ chromium_base = finder.chromium_base()
driver_name = getattr(options, 'driver_name', None)
if driver_name is None:
driver_name = cls.CONTENT_SHELL_NAME
@@ -61,7 +64,7 @@ class LinuxPort(chromium.ChromiumPort):
configuration = options.configuration
else:
configuration = config_object.default_configuration()
- return cls._static_build_path(host.filesystem, build_directory, chromium_base, webkit_base, configuration, [driver_name])
+ return cls._static_build_path(host.filesystem, build_directory, chromium_base, configuration, [driver_name])
@staticmethod
def _determine_architecture(filesystem, executive, driver_path):
@@ -90,19 +93,20 @@ class LinuxPort(chromium.ChromiumPort):
return port_name
def __init__(self, host, port_name, **kwargs):
- chromium.ChromiumPort.__init__(self, host, port_name, **kwargs)
+ super(LinuxPort, self).__init__(host, port_name, **kwargs)
(base, arch) = port_name.rsplit('-', 1)
assert base == 'linux'
assert arch in self.SUPPORTED_VERSIONS
assert port_name in ('linux', 'linux-x86', 'linux-x86_64')
self._version = 'lucid' # We only support lucid right now.
self._architecture = arch
+ if not self.get_option('disable_breakpad'):
+ self._dump_reader = DumpReaderLinux(host, self._build_path())
def additional_drt_flag(self):
flags = super(LinuxPort, self).additional_drt_flag()
- # FIXME: Temporarily disable the sandbox on Linux until we can get
- # stacktraces via breakpad. http://crbug.com/247431
- flags += ['--no-sandbox']
+ if not self.get_option('disable_breakpad'):
+ flags += ['--enable-crash-reporter', '--crash-dumps-dir=%s' % self._dump_reader.crash_dumps_directory()]
return flags
def default_baseline_search_path(self):
@@ -113,16 +117,35 @@ class LinuxPort(chromium.ChromiumPort):
return [self._build_path('libffmpegsumo.so')]
def check_build(self, needs_http, printer):
- result = chromium.ChromiumPort.check_build(self, needs_http, printer)
- if not result:
+ result = super(LinuxPort, self).check_build(needs_http, printer)
+
+ if 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 look_for_new_crash_logs(self, crashed_processes, start_time):
+ if self.get_option('disable_breakpad'):
+ return None
+ return self._dump_reader.look_for_new_crash_logs(crashed_processes, start_time)
+
+ def clobber_old_port_specific_results(self):
+ if not self.get_option('disable_breakpad'):
+ self._dump_reader.clobber_old_results()
+
def operating_system(self):
return 'linux'
+ def virtual_test_suites(self):
+ result = super(LinuxPort, self).virtual_test_suites()
+ result.extend([
+ base.VirtualTestSuite('linux-subpixel',
+ 'platform/linux/fast/text/subpixel',
+ ['--enable-webkit-text-subpixel-positioning']),
+ ])
+ return result
+
#
# PROTECTED METHODS
#
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py
index f8c3ba5e668..d6b7d8bc074 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py
@@ -33,10 +33,10 @@ from webkitpy.common.system.systemhost_mock import MockSystemHost
from webkitpy.tool.mocktool import MockOptions
from webkitpy.layout_tests.port import linux
-from webkitpy.layout_tests.port import chromium_port_testcase
+from webkitpy.layout_tests.port import port_testcase
-class LinuxPortTest(chromium_port_testcase.ChromiumPortTestCase):
+class LinuxPortTest(port_testcase.PortTestCase):
port_name = 'linux'
port_maker = linux.LinuxPort
@@ -93,15 +93,11 @@ class LinuxPortTest(chromium_port_testcase.ChromiumPortTestCase):
def test_build_path(self):
# Test that optional paths are used regardless of whether they exist.
options = MockOptions(configuration='Release', build_directory='/foo')
- self.assert_build_path(options, ['/mock-checkout/Source/WebKit/chromium/out/Release'], '/foo/Release')
+ self.assert_build_path(options, ['/mock-checkout/out/Release'], '/foo/Release')
# Test that optional relative paths are returned unmodified.
options = MockOptions(configuration='Release', build_directory='foo')
- self.assert_build_path(options, ['/mock-checkout/Source/WebKit/chromium/out/Release'], 'foo/Release')
-
- # Test that we prefer the legacy dir over the new dir.
- options = MockOptions(configuration='Release', build_directory=None)
- self.assert_build_path(options, ['/mock-checkout/Source/WebKit/chromium/sconsbuild/Release', '/mock-checkout/Source/WebKit/chromium/out/Release'], '/mock-checkout/Source/WebKit/chromium/sconsbuild/Release')
+ self.assert_build_path(options, ['/mock-checkout/out/Release'], 'foo/Release')
def test_driver_name_option(self):
self.assertTrue(self.make_port()._path_to_driver().endswith('content_shell'))
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py
index c925584b489..af1c6de62bc 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py
@@ -31,24 +31,29 @@
import logging
import signal
-from webkitpy.layout_tests.port import chromium
+from webkitpy.layout_tests.port import base
_log = logging.getLogger(__name__)
-class MacPort(chromium.ChromiumPort):
- SUPPORTED_VERSIONS = ('snowleopard', 'lion', 'retina', 'mountainlion')
+class MacPort(base.Port):
+ SUPPORTED_VERSIONS = ('snowleopard', 'lion', 'retina', 'mountainlion', 'mavericks')
port_name = 'mac'
- FALLBACK_PATHS = { 'mountainlion': [ 'mac' ]}
- FALLBACK_PATHS['lion'] = ['mac-lion'] + FALLBACK_PATHS['mountainlion']
- FALLBACK_PATHS['snowleopard'] = ['mac-snowleopard'] + FALLBACK_PATHS['lion']
-
# FIXME: We treat Retina (High-DPI) devices as if they are running
- # a different operating system version. This isn't accurate, but will work until
- # we need to test and support baselines across multiple O/S versions.
+ # a different operating system version. This is lame and should be fixed.
+ # Note that the retina versions fallback to the non-retina versions and so no
+ # baselines are shared between retina versions; this keeps the fallback graph as a tree
+ # and maximizes the number of baselines we can share that way.
+ # We also currently only support Retina on 10.8; we need to either upgrade to 10.9 or support both.
+
+ FALLBACK_PATHS = {}
+ FALLBACK_PATHS['mavericks'] = ['mac']
+ FALLBACK_PATHS['mountainlion'] = ['mac-mountainlion'] + FALLBACK_PATHS['mavericks']
FALLBACK_PATHS['retina'] = ['mac-retina'] + FALLBACK_PATHS['mountainlion']
+ FALLBACK_PATHS['lion'] = ['mac-lion'] + FALLBACK_PATHS['mountainlion']
+ FALLBACK_PATHS['snowleopard'] = ['mac-snowleopard'] + FALLBACK_PATHS['lion']
DEFAULT_BUILD_DIRECTORIES = ('xcodebuild', 'out')
@@ -57,13 +62,17 @@ class MacPort(chromium.ChromiumPort):
@classmethod
def determine_full_port_name(cls, host, options, port_name):
if port_name.endswith('mac'):
+ if host.platform.os_version in ('future',):
+ version = 'mavericks'
+ else:
+ version = host.platform.os_version
if host.platform.is_highdpi():
- return "mac-retina"
- return port_name + '-' + host.platform.os_version
+ version = 'retina'
+ return port_name + '-' + version
return port_name
def __init__(self, host, port_name, **kwargs):
- chromium.ChromiumPort.__init__(self, host, port_name, **kwargs)
+ super(MacPort, self).__init__(host, port_name, **kwargs)
self._version = port_name[port_name.index('mac-') + len('mac-'):]
assert self._version in self.SUPPORTED_VERSIONS
@@ -71,8 +80,8 @@ class MacPort(chromium.ChromiumPort):
return [self._build_path('ffmpegsumo.so')]
def check_build(self, needs_http, printer):
- result = chromium.ChromiumPort.check_build(self, needs_http, printer)
- if not result:
+ result = super(MacPort, self).check_build(needs_http, printer)
+ if result:
_log.error('For complete Mac build requirements, please see:')
_log.error('')
_log.error(' http://code.google.com/p/chromium/wiki/MacBuildInstructions')
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py
index 225c786350a..abf2b248d58 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py
@@ -29,11 +29,11 @@
import webkitpy.thirdparty.unittest2 as unittest
from webkitpy.layout_tests.port import mac
-from webkitpy.layout_tests.port import chromium_port_testcase
+from webkitpy.layout_tests.port import port_testcase
from webkitpy.tool.mocktool import MockOptions
-class MacPortTest(chromium_port_testcase.ChromiumPortTestCase):
+class MacPortTest(port_testcase.PortTestCase):
os_name = 'mac'
os_version = 'snowleopard'
port_name = 'mac'
@@ -44,7 +44,7 @@ class MacPortTest(chromium_port_testcase.ChromiumPortTestCase):
self.assertEqual(expected, port.name())
def test_versions(self):
- self.assertTrue(self.make_port().name() in ('mac-snowleopard', 'mac-lion', 'mac-mountainlion'))
+ self.assertTrue(self.make_port().name() in ('mac-snowleopard', 'mac-lion', 'mac-mountainlion', 'mac-mavericks'))
self.assert_name(None, 'snowleopard', 'mac-snowleopard')
self.assert_name('mac', 'snowleopard', 'mac-snowleopard')
@@ -53,6 +53,8 @@ class MacPortTest(chromium_port_testcase.ChromiumPortTestCase):
self.assert_name(None, 'lion', 'mac-lion')
self.assert_name(None, 'mountainlion', 'mac-mountainlion')
+ self.assert_name(None, 'mavericks', 'mac-mavericks')
+ self.assert_name(None, 'future', 'mac-mavericks')
self.assert_name('mac', 'lion', 'mac-lion')
self.assertRaises(AssertionError, self.assert_name, None, 'tiger', 'should-raise-assertion-so-this-value-does-not-matter')
@@ -65,6 +67,9 @@ class MacPortTest(chromium_port_testcase.ChromiumPortTestCase):
self.assertEqual(port.baseline_path(), port._webkit_baseline_path('mac-lion'))
port = self.make_port(port_name='mac-mountainlion')
+ self.assertEqual(port.baseline_path(), port._webkit_baseline_path('mac-mountainlion'))
+
+ port = self.make_port(port_name='mac-mavericks')
self.assertEqual(port.baseline_path(), port._webkit_baseline_path('mac'))
def test_operating_system(self):
@@ -73,15 +78,15 @@ class MacPortTest(chromium_port_testcase.ChromiumPortTestCase):
def test_build_path(self):
# Test that optional paths are used regardless of whether they exist.
options = MockOptions(configuration='Release', build_directory='/foo')
- self.assert_build_path(options, ['/mock-checkout/Source/WebKit/chromium/out/Release'], '/foo/Release')
+ self.assert_build_path(options, ['/mock-checkout/out/Release'], '/foo/Release')
# Test that optional relative paths are returned unmodified.
options = MockOptions(configuration='Release', build_directory='foo')
- self.assert_build_path(options, ['/mock-checkout/Source/WebKit/chromium/out/Release'], 'foo/Release')
+ self.assert_build_path(options, ['/mock-checkout/out/Release'], 'foo/Release')
# Test that we prefer the legacy dir over the new dir.
options = MockOptions(configuration='Release', build_directory=None)
- self.assert_build_path(options, ['/mock-checkout/Source/WebKit/chromium/xcodebuild/Release', '/mock-checkout/Source/WebKit/chromium/out/Release'], '/mock-checkout/Source/WebKit/chromium/xcodebuild/Release')
+ self.assert_build_path(options, ['/mock-checkout/xcodebuild/Release', '/mock-checkout/out/Release'], '/mock-checkout/xcodebuild/Release')
def test_build_path_timestamps(self):
options = MockOptions(configuration='Release', build_directory=None)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py
index 7992a00a12c..1541fd9098e 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py
@@ -41,6 +41,7 @@ import logging
import optparse
import os
import sys
+import types
# Since we execute this script directly as part of the unit tests, we need to ensure
# that Tools/Scripts is in sys.path for the next imports to work correctly.
@@ -48,6 +49,7 @@ script_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.
if script_dir not in sys.path:
sys.path.append(script_dir)
+from webkitpy.common import read_checksum_from_png
from webkitpy.common.system.systemhost import SystemHost
from webkitpy.layout_tests.port.driver import DriverInput, DriverOutput
from webkitpy.layout_tests.port.factory import PortFactory
@@ -64,6 +66,8 @@ class MockDRTPort(object):
def __init__(self, host, port_name, **kwargs):
self.__delegate = PortFactory(host).get(port_name.replace('mock-', ''), **kwargs)
+ self.__delegate_driver_class = self.__delegate._driver_class
+ self.__delegate._driver_class = types.MethodType(self._driver_class, self.__delegate)
def __getattr__(self, name):
return getattr(self.__delegate, name)
@@ -74,18 +78,17 @@ class MockDRTPort(object):
def check_sys_deps(self, needs_http):
return True
- def _driver_class(self):
+ def _driver_class(self, delegate):
return self._mocked_driver_maker
- @staticmethod
- def _mocked_driver_maker(port, worker_number, pixel_tests, no_timeout=False):
- path_to_this_file = port.host.filesystem.abspath(__file__.replace('.pyc', '.py'))
- driver = port.__delegate._driver_class()(port, worker_number, pixel_tests, no_timeout)
- driver.cmd_line = port._overriding_cmd_line(driver.cmd_line,
- port.__delegate._path_to_driver(),
+ def _mocked_driver_maker(self, port, worker_number, pixel_tests, no_timeout=False):
+ path_to_this_file = self.host.filesystem.abspath(__file__.replace('.pyc', '.py'))
+ driver = self.__delegate_driver_class()(self, worker_number, pixel_tests, no_timeout)
+ driver.cmd_line = self._overriding_cmd_line(driver.cmd_line,
+ self.__delegate._path_to_driver(),
sys.executable,
path_to_this_file,
- port.__delegate.name())
+ self.__delegate.name())
return driver
@staticmethod
@@ -122,47 +125,44 @@ class MockDRTPort(object):
def release_http_lock(self):
pass
- def show_results_html_file(self, results_filename):
- pass
-
def _make_wdiff_available(self):
self.__delegate._wdiff_available = True
+ def setup_environ_for_server(self, server_name):
+ env = self.__delegate.setup_environ_for_server()
+ # We need to propagate PATH down so the python code can find the checkout.
+ env['PATH'] = os.environ['PATH']
+ return env
+
+ def lookup_virtual_test_args(self, test_name):
+ suite = self.__delegate.lookup_virtual_suite(test_name)
+ return suite.args + ['--virtual-test-suite-name', suite.name, '--virtual-test-suite-base', suite.base]
def main(argv, host, stdin, stdout, stderr):
"""Run the tests."""
options, args = parse_options(argv)
- if options.test_shell:
- drt = MockTestShell(options, args, host, stdin, stdout, stderr)
- else:
- drt = MockDRT(options, args, host, stdin, stdout, stderr)
+ drt = MockDRT(options, args, host, stdin, stdout, stderr)
return drt.run()
def parse_options(argv):
- # FIXME: We have to do custom arg parsing instead of using the optparse
- # module. First, Chromium and non-Chromium DRTs have a different argument
- # syntax. Chromium uses --pixel-tests=<path>, and non-Chromium uses
- # --pixel-tests as a boolean flag. Second, we don't want to have to list
- # every command line flag DRT accepts, but optparse complains about
- # unrecognized flags. At some point it might be good to share a common
- # DRT options class between this file and webkit.py and chromium.py
- # just to get better type checking.
- platform_index = argv.index('--platform')
- platform = argv[platform_index + 1]
-
- pixel_tests = False
- pixel_path = None
- test_shell = '--test-shell' in argv
- if test_shell:
- for arg in argv:
- if arg.startswith('--pixel-tests'):
- pixel_tests = True
- pixel_path = arg[len('--pixel-tests='):]
- else:
- pixel_tests = '--pixel-tests' in argv
- options = optparse.Values({'test_shell': test_shell, 'platform': platform, 'pixel_tests': pixel_tests, 'pixel_path': pixel_path})
+ # We do custom arg parsing instead of using the optparse module
+ # because we don't want to have to list every command line flag DRT
+ # accepts, and optparse complains about unrecognized flags.
+
+ def get_arg(arg_name):
+ if arg_name in argv:
+ index = argv.index(arg_name)
+ return argv[index + 1]
+ return None
+
+ options = optparse.Values({
+ 'actual_directory': get_arg('--actual-directory'),
+ 'platform': get_arg('--platform'),
+ 'virtual_test_suite_base': get_arg('--virtual-test-suite-base'),
+ 'virtual_test_suite_name': get_arg('--virtual-test-suite-name'),
+ })
return (options, argv)
@@ -195,21 +195,28 @@ class MockDRT(object):
def input_from_line(self, line):
vals = line.strip().split("'")
- if len(vals) == 1:
- uri = vals[0]
- checksum = None
- else:
- uri = vals[0]
- checksum = vals[1]
+ uri = vals[0]
+ checksum = None
+ should_run_pixel_tests = False
+ if len(vals) == 2 and vals[1] == '--pixel-test':
+ should_run_pixel_tests = True
+ elif len(vals) == 3 and vals[1] == '--pixel-test':
+ should_run_pixel_tests = True
+ checksum = vals[2]
+ elif len(vals) != 1:
+ raise NotImplementedError
+
if uri.startswith('http://') or uri.startswith('https://'):
test_name = self._driver.uri_to_test(uri)
else:
test_name = self._port.relative_test_filename(uri)
- return DriverInput(test_name, 0, checksum, self._options.pixel_tests)
+ return DriverInput(test_name, 0, checksum, should_run_pixel_tests, args=[])
def output_for_test(self, test_input, is_reftest):
port = self._port
+ if self._options.virtual_test_suite_name:
+ test_input.test_name = test_input.test_name.replace(self._options.virtual_test_suite_base, self._options.virtual_test_suite_name)
actual_text = port.expected_text(test_input.test_name)
actual_audio = port.expected_audio(test_input.test_name)
actual_image = None
@@ -223,10 +230,25 @@ class MockDRT(object):
actual_text = 'not reference text\n'
actual_checksum = 'not-mock-checksum'
actual_image = 'not blank'
- elif self._options.pixel_tests and test_input.image_hash:
+ elif test_input.should_run_pixel_test and test_input.image_hash:
actual_checksum = port.expected_checksum(test_input.test_name)
actual_image = port.expected_image(test_input.test_name)
+ if self._options.actual_directory:
+ actual_path = port._filesystem.join(self._options.actual_directory, test_input.test_name)
+ root, _ = port._filesystem.splitext(actual_path)
+ text_path = root + '-actual.txt'
+ if port._filesystem.exists(text_path):
+ actual_text = port._filesystem.read_binary_file(text_path)
+ audio_path = root + '-actual.wav'
+ if port._filesystem.exists(audio_path):
+ actual_audio = port._filesystem.read_binary_file(audio_path)
+ image_path = root + '-actual.png'
+ if port._filesystem.exists(image_path):
+ actual_image = port._filesystem.read_binary_file(image_path)
+ with port._filesystem.open_binary_file_for_reading(image_path) as filehandle:
+ actual_checksum = read_checksum_from_png.read_checksum(filehandle)
+
return DriverOutput(actual_text, actual_image, actual_checksum, actual_audio)
def write_test_output(self, test_input, output, is_reftest):
@@ -234,6 +256,7 @@ class MockDRT(object):
self._stdout.write('Content-Type: audio/wav\n')
self._stdout.write('Content-Transfer-Encoding: base64\n')
self._stdout.write(base64.b64encode(output.audio))
+ self._stdout.write('\n')
else:
self._stdout.write('Content-Type: text/plain\n')
# FIXME: Note that we don't ensure there is a trailing newline!
@@ -243,7 +266,7 @@ class MockDRT(object):
self._stdout.write('#EOF\n')
- if self._options.pixel_tests and output.image_hash:
+ if test_input.should_run_pixel_test and output.image_hash:
self._stdout.write('\n')
self._stdout.write('ActualHash: %s\n' % output.image_hash)
self._stdout.write('ExpectedHash: %s\n' % test_input.image_hash)
@@ -257,43 +280,6 @@ class MockDRT(object):
self._stderr.flush()
-class MockTestShell(MockDRT):
- def input_from_line(self, line):
- vals = line.strip().split()
- if len(vals) == 3:
- uri, timeout, checksum = vals
- else:
- uri, timeout = vals
- checksum = None
-
- test_name = self._driver.uri_to_test(uri)
- return DriverInput(test_name, timeout, checksum, self._options.pixel_tests)
-
- def output_for_test(self, test_input, is_reftest):
- # FIXME: This is a hack to make virtual tests work. Need something more general.
- original_test_name = test_input.test_name
- if '--enable-accelerated-2d-canvas' in self._args and 'canvas' in test_input.test_name:
- test_input.test_name = 'platform/chromium/virtual/gpu/' + test_input.test_name
- output = super(MockTestShell, self).output_for_test(test_input, is_reftest)
- test_input.test_name = original_test_name
- return output
-
- def write_test_output(self, test_input, output, is_reftest):
- self._stdout.write("#URL:%s\n" % self._driver.test_to_uri(test_input.test_name))
- if self._options.pixel_tests and output.image_hash:
- self._stdout.write("#MD5:%s\n" % output.image_hash)
- if output.image:
- self._host.filesystem.maybe_make_directory(self._host.filesystem.dirname(self._options.pixel_path))
- self._host.filesystem.write_binary_file(self._options.pixel_path, output.image)
- if output.text:
- self._stdout.write(output.text)
-
- if output.text and not output.text.endswith('\n'):
- self._stdout.write('\n')
- self._stdout.write('#EOF\n')
- self._stdout.flush()
-
-
if __name__ == '__main__':
# Note that the Mock in MockDRT refers to the fact that it is emulating a
# real DRT, and as such, it needs access to a real SystemHost, not a MockSystemHost.
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py
index 1dde3b99514..f577b2e6f98 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py
@@ -59,6 +59,9 @@ class MockDRTPortTest(port_testcase.PortTestCase):
def test_check_sys_deps(self):
pass
+ def test_default_max_locked_shards(self):
+ pass
+
def test_diff_image(self):
pass
@@ -76,20 +79,16 @@ class MockDRTPortTest(port_testcase.PortTestCase):
class MockDRTTest(unittest.TestCase):
- def input_line(self, port, test_name, checksum=None):
+ def input_line(self, port, test_name, pixel_tests, checksum=None):
url = port.create_driver(0).test_to_uri(test_name)
if url.startswith('file://'):
url = url[len('file://'):]
-
+ if pixel_tests:
+ url += "'--pixel-test"
if checksum:
- return url + "'" + checksum + '\n'
+ url += "'" + checksum
return url + '\n'
- def extra_args(self, pixel_tests):
- if pixel_tests:
- return ['--pixel-tests', '-']
- return ['-']
-
def make_drt(self, options, args, host, stdin, stdout, stderr):
return mock_drt.MockDRT(options, args, host, stdin, stdout, stderr)
@@ -99,7 +98,7 @@ class MockDRTTest(unittest.TestCase):
if not expected_checksum:
expected_checksum = port.expected_checksum(test_name)
if not drt_input:
- drt_input = self.input_line(port, test_name, expected_checksum)
+ drt_input = self.input_line(port, test_name, pixel_tests, expected_checksum)
text_output = expected_text or port.expected_text(test_name) or ''
if not drt_output:
@@ -127,7 +126,7 @@ class MockDRTTest(unittest.TestCase):
drt_input, drt_output = self.make_input_output(port, test_name,
pixel_tests, expected_checksum, drt_output, drt_input=None, expected_text=expected_text)
- args = ['--platform', port_name] + self.extra_args(pixel_tests)
+ args = ['--dump-render-tree', '--platform', port_name, '-']
stdin = newstringio.StringIO(drt_input)
stdout = newstringio.StringIO()
stderr = newstringio.StringIO()
@@ -141,7 +140,7 @@ class MockDRTTest(unittest.TestCase):
# We use the StringIO.buflist here instead of getvalue() because
# the StringIO might be a mix of unicode/ascii and 8-bit strings.
self.assertEqual(stdout.buflist, drt_output)
- self.assertEqual(stderr.getvalue(), '' if options.test_shell else '#EOF\n')
+ self.assertEqual(stderr.getvalue(), '#EOF\n')
def test_main(self):
host = MockSystemHost()
@@ -149,7 +148,7 @@ class MockDRTTest(unittest.TestCase):
stdin = newstringio.StringIO()
stdout = newstringio.StringIO()
stderr = newstringio.StringIO()
- res = mock_drt.main(['--platform', 'test'] + self.extra_args(False),
+ res = mock_drt.main(['--dump-render-tree', '--platform', 'test', '-'],
host, stdin, stdout, stderr)
self.assertEqual(res, 0)
self.assertEqual(stdout.getvalue(), '')
@@ -184,66 +183,19 @@ class MockDRTTest(unittest.TestCase):
self.assertTest('failures/expected/missing_text.html', True)
def test_reftest_match(self):
- self.assertTest('passes/reftest.html', False, expected_checksum='mock-checksum', expected_text='reference text\n')
self.assertTest('passes/reftest.html', True, expected_checksum='mock-checksum', expected_text='reference text\n')
def test_reftest_mismatch(self):
- self.assertTest('passes/mismatch.html', False, expected_checksum='mock-checksum', expected_text='reference text\n')
self.assertTest('passes/mismatch.html', True, expected_checksum='mock-checksum', expected_text='reference text\n')
-
-class MockTestShellTest(MockDRTTest):
- def extra_args(self, pixel_tests):
- if pixel_tests:
- return ['--pixel-tests=/tmp/png_result0.png']
- return []
-
- def make_drt(self, options, args, host, stdin, stdout, stderr):
- options.test_shell = True
-
- # We have to set these by hand because --platform test won't trigger
- # the Chromium code paths.
- options.pixel_path = '/tmp/png_result0.png'
- options.pixel_tests = True
-
- return mock_drt.MockTestShell(options, args, host, stdin, stdout, stderr)
-
- def input_line(self, port, test_name, checksum=None):
- url = port.create_driver(0).test_to_uri(test_name)
- if checksum:
- return url + ' 6000 ' + checksum + '\n'
- return url + ' 6000\n'
-
- def expected_output(self, port, test_name, pixel_tests, text_output, expected_checksum):
- url = port.create_driver(0).test_to_uri(test_name)
- output = ['#URL:%s\n' % url]
- if expected_checksum:
- output.append('#MD5:%s\n' % expected_checksum)
- if text_output:
- output.append(text_output)
- if not text_output.endswith('\n'):
- output.append('\n')
- output.append('#EOF\n')
- return output
-
- def test_pixeltest__fails(self):
- host = MockSystemHost()
- url = '#URL:file://'
- url = url + '%s/failures/expected/image_checksum.html' % PortFactory(host).get('test').layout_tests_dir()
- self.assertTest('failures/expected/image_checksum.html', pixel_tests=True,
- expected_checksum='image_checksum',
- drt_output=[url + '\n',
- '#MD5:image_checksum-checksum\n',
- 'image_checksum-txt',
- '\n',
- '#EOF\n'],
- host=host)
- self.assertEqual(host.filesystem.written_files,
- {'/tmp/png_result0.png': 'image_checksum\x8a-pngtEXtchecksum\x00image_checksum-checksum'})
-
- def test_test_shell_parse_options(self):
- options, args = mock_drt.parse_options(['--platform', 'mac', '--test-shell',
- '--pixel-tests=/tmp/png_result0.png'])
- self.assertTrue(options.test_shell)
- self.assertTrue(options.pixel_tests)
- self.assertEqual(options.pixel_path, '/tmp/png_result0.png')
+ def test_audio(self):
+ self.assertTest('passes/audio.html', pixel_tests=True,
+ drt_output=['Content-Type: audio/wav\n',
+ 'Content-Transfer-Encoding: base64\n',
+ 'YXVkaW8td2F2',
+ '\n',
+ '#EOF\n',
+ '#EOF\n'])
+
+ def test_virtual(self):
+ self.assertTest('virtual/passes/text.html', True)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
index 05a50016a81..1886a66a8a3 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
@@ -36,11 +36,12 @@ import sys
import time
import webkitpy.thirdparty.unittest2 as unittest
-from webkitpy.common.system.executive_mock import MockExecutive
+from webkitpy.common.system.executive_mock import MockExecutive, MockExecutive2
from webkitpy.common.system.filesystem_mock import MockFileSystem
from webkitpy.common.system.outputcapture import OutputCapture
from webkitpy.common.system.systemhost_mock import MockSystemHost
-from webkitpy.layout_tests.port.base import Port
+from webkitpy.layout_tests.models import test_run_results
+from webkitpy.layout_tests.port.base import Port, TestConfiguration
from webkitpy.layout_tests.port.server_process_mock import MockServerProcess
from webkitpy.layout_tests.servers import http_server_base
from webkitpy.tool.mocktool import MockOptions
@@ -68,6 +69,15 @@ class TestWebKitPort(Port):
return ["accessibility", ]
+class FakePrinter(object):
+ def write_update(self, msg):
+ pass
+
+ def write_throttled_update(self, msg):
+ pass
+
+
+
class PortTestCase(unittest.TestCase):
"""Tests that all Port implementations must pass."""
HTTP_PORTS = (8000, 8080, 8443)
@@ -91,10 +101,38 @@ class PortTestCase(unittest.TestCase):
def make_wdiff_available(self, port):
port._wdiff_available = True
+ def test_check_build(self):
+ port = self.make_port()
+ port._check_file_exists = lambda path, desc: True
+ if port._dump_reader:
+ port._dump_reader.check_is_functional = lambda: True
+ port._options.build = True
+ port._check_driver_build_up_to_date = lambda config: True
+ oc = OutputCapture()
+ try:
+ oc.capture_output()
+ self.assertEqual(port.check_build(needs_http=True, printer=FakePrinter()),
+ test_run_results.OK_EXIT_STATUS)
+ finally:
+ out, err, logs = oc.restore_output()
+ self.assertIn('pretty patches', logs) # We should get a warning about PrettyPatch being missing,
+ self.assertNotIn('build requirements', logs) # but not the driver itself.
+
+ port._check_file_exists = lambda path, desc: False
+ port._check_driver_build_up_to_date = lambda config: False
+ try:
+ oc.capture_output()
+ self.assertEqual(port.check_build(needs_http=True, printer=FakePrinter()),
+ test_run_results.UNEXPECTED_ERROR_EXIT_STATUS)
+ finally:
+ out, err, logs = oc.restore_output()
+ self.assertIn('pretty patches', logs) # And, hereere we should get warnings about both.
+ self.assertIn('build requirements', logs)
+
def test_default_max_locked_shards(self):
port = self.make_port()
port.default_child_processes = lambda: 16
- self.assertEqual(port.default_max_locked_shards(), 1)
+ self.assertEqual(port.default_max_locked_shards(), 4)
port.default_child_processes = lambda: 2
self.assertEqual(port.default_max_locked_shards(), 1)
@@ -158,20 +196,39 @@ class PortTestCase(unittest.TestCase):
self.assertEqual(port.diff_image('foo', ''), ('foo', None))
def test_diff_image(self):
+ def _path_to_image_diff():
+ return "/path/to/image_diff"
+
port = self.make_port()
- self.proc = None
+ port._path_to_image_diff = _path_to_image_diff
+
+ mock_image_diff = "MOCK Image Diff"
+
+ def mock_run_command(args):
+ port._filesystem.write_binary_file(args[4], mock_image_diff)
+ return 1
- def make_proc(port, nm, cmd, env):
- self.proc = MockServerProcess(port, nm, cmd, env, lines=['diff: 100% failed\n', 'diff: 100% failed\n'])
- return self.proc
+ # Images are different.
+ port._executive = MockExecutive2(run_command_fn=mock_run_command)
+ self.assertEqual(mock_image_diff, port.diff_image("EXPECTED", "ACTUAL")[0])
- port._server_process_constructor = make_proc
- port.setup_test_run()
- self.assertEqual(port.diff_image('foo', 'bar'), ('', 100.0, None))
+ # Images are the same.
+ port._executive = MockExecutive2(exit_code=0)
+ self.assertEqual(None, port.diff_image("EXPECTED", "ACTUAL")[0])
- port.clean_up_test_run()
- self.assertTrue(self.proc.stopped)
- self.assertEqual(port._image_differ, None)
+ # There was some error running image_diff.
+ port._executive = MockExecutive2(exit_code=2)
+ exception_raised = False
+ try:
+ port.diff_image("EXPECTED", "ACTUAL")
+ except ValueError, e:
+ exception_raised = True
+ self.assertFalse(exception_raised)
+
+ def test_diff_image_crashed(self):
+ port = self.make_port()
+ port._executive = MockExecutive2(exit_code=2)
+ self.assertEqual(port.diff_image("EXPECTED", "ACTUAL"), (None, 'image diff returned an exit code of 2'))
def test_check_wdiff(self):
port = self.make_port()
@@ -191,15 +248,45 @@ class PortTestCase(unittest.TestCase):
self.assertEqual(diff_txt, port._wdiff_error_html)
self.assertFalse(port.wdiff_available())
+ def test_missing_symbol_to_skipped_tests(self):
+ # Test that we get the chromium skips and not the webkit default skips
+ port = self.make_port()
+ skip_dict = port._missing_symbol_to_skipped_tests()
+ if port.PORT_HAS_AUDIO_CODECS_BUILT_IN:
+ self.assertEqual(skip_dict, {})
+ else:
+ self.assertTrue('ff_mp3_decoder' in skip_dict)
+ self.assertFalse('WebGLShader' in skip_dict)
+
def test_test_configuration(self):
port = self.make_port()
self.assertTrue(port.test_configuration())
def test_all_test_configurations(self):
+ """Validate the complete set of configurations this port knows about."""
port = self.make_port()
- self.assertTrue(len(port.all_test_configurations()) > 0)
- self.assertTrue(port.test_configuration() in port.all_test_configurations(), "%s not in %s" % (port.test_configuration(), port.all_test_configurations()))
-
+ self.assertEqual(set(port.all_test_configurations()), set([
+ TestConfiguration('snowleopard', 'x86', 'debug'),
+ TestConfiguration('snowleopard', 'x86', 'release'),
+ TestConfiguration('lion', 'x86', 'debug'),
+ TestConfiguration('lion', 'x86', 'release'),
+ TestConfiguration('retina', 'x86', 'debug'),
+ TestConfiguration('retina', 'x86', 'release'),
+ TestConfiguration('mountainlion', 'x86', 'debug'),
+ TestConfiguration('mountainlion', 'x86', 'release'),
+ TestConfiguration('mavericks', 'x86', 'debug'),
+ TestConfiguration('mavericks', 'x86', 'release'),
+ TestConfiguration('xp', 'x86', 'debug'),
+ TestConfiguration('xp', 'x86', 'release'),
+ TestConfiguration('win7', 'x86', 'debug'),
+ TestConfiguration('win7', 'x86', 'release'),
+ TestConfiguration('lucid', 'x86', 'debug'),
+ TestConfiguration('lucid', 'x86', 'release'),
+ TestConfiguration('lucid', 'x86_64', 'debug'),
+ TestConfiguration('lucid', 'x86_64', 'release'),
+ TestConfiguration('icecreamsandwich', 'x86', 'debug'),
+ TestConfiguration('icecreamsandwich', 'x86', 'release'),
+ ]))
def test_get_crash_log(self):
port = self.make_port()
self.assertEqual(port._get_crash_log(None, None, None, None, newer_than=None),
@@ -234,6 +321,38 @@ class PortTestCase(unittest.TestCase):
port.host.filesystem.maybe_make_directory(directory)
self.assertEqual(port._build_path(), expected_path)
+ def test_expectations_files(self):
+ port = self.make_port()
+
+ generic_path = port.path_to_generic_test_expectations_file()
+ chromium_overrides_path = port.path_from_chromium_base(
+ 'webkit', 'tools', 'layout_tests', 'test_expectations.txt')
+ never_fix_tests_path = port._filesystem.join(port.layout_tests_dir(), 'NeverFixTests')
+ stale_tests_path = port._filesystem.join(port.layout_tests_dir(), 'StaleTestExpectations')
+ slow_tests_path = port._filesystem.join(port.layout_tests_dir(), 'SlowTests')
+ skia_overrides_path = port.path_from_chromium_base(
+ 'skia', 'skia_test_expectations.txt')
+
+ port._filesystem.write_text_file(skia_overrides_path, 'dummay text')
+
+ port._options.builder_name = 'DUMMY_BUILDER_NAME'
+ self.assertEqual(port.expectations_files(), [generic_path, skia_overrides_path, never_fix_tests_path, stale_tests_path, slow_tests_path, chromium_overrides_path])
+
+ port._options.builder_name = 'builder (deps)'
+ self.assertEqual(port.expectations_files(), [generic_path, skia_overrides_path, never_fix_tests_path, stale_tests_path, slow_tests_path, chromium_overrides_path])
+
+ # A builder which does NOT observe the Chromium test_expectations,
+ # but still observes the Skia test_expectations...
+ port._options.builder_name = 'builder'
+ self.assertEqual(port.expectations_files(), [generic_path, skia_overrides_path, never_fix_tests_path, stale_tests_path, slow_tests_path])
+
+ def test_check_sys_deps(self):
+ port = self.make_port()
+ port._executive = MockExecutive2(exit_code=0)
+ self.assertEqual(port.check_sys_deps(needs_http=False), test_run_results.OK_EXIT_STATUS)
+ port._executive = MockExecutive2(exit_code=1, output='testing output failure')
+ self.assertEqual(port.check_sys_deps(needs_http=False), test_run_results.SYS_DEPS_EXIT_STATUS)
+
def test_expectations_ordering(self):
port = self.make_port()
for path in port.expectations_files():
@@ -275,29 +394,6 @@ class PortTestCase(unittest.TestCase):
result_directories = set(TestWebKitPort(symbols_string=symbols_string)._skipped_tests_for_unsupported_features(test_list=['webaudio/codec-tests/mp3/foo.html']))
self.assertEqual(result_directories, expected_directories)
- def test_expectations_files(self):
- port = TestWebKitPort()
-
- def platform_dirs(port):
- return [port.host.filesystem.basename(port.host.filesystem.dirname(f)) for f in port.expectations_files()]
-
- self.assertEqual(platform_dirs(port), ['LayoutTests', 'testwebkitport'])
-
- port = TestWebKitPort(port_name="testwebkitport-version")
- self.assertEqual(platform_dirs(port), ['LayoutTests', 'testwebkitport', 'testwebkitport-version'])
-
- port = TestWebKitPort(port_name="testwebkitport-version",
- options=MockOptions(additional_platform_directory=["internal-testwebkitport"]))
- self.assertEqual(platform_dirs(port), ['LayoutTests', 'testwebkitport', 'testwebkitport-version', 'internal-testwebkitport'])
-
- def test_test_expectations(self):
- # Check that we read the expectations file
- host = MockSystemHost()
- host.filesystem.write_text_file('/mock-checkout/LayoutTests/platform/testwebkitport/TestExpectations',
- 'BUG_TESTEXPECTATIONS SKIP : fast/html/article-element.html = FAIL\n')
- port = TestWebKitPort(host=host)
- self.assertEqual(''.join(port.expectations_dict().values()), 'BUG_TESTEXPECTATIONS SKIP : fast/html/article-element.html = FAIL\n')
-
def _assert_config_file_for_platform(self, port, platform, config_file):
self.assertEqual(port._apache_config_file_name_for_platform(platform), config_file)
@@ -327,7 +423,8 @@ class PortTestCase(unittest.TestCase):
port = TestWebKitPort()
port._is_debian_based = lambda: True
- self._assert_config_file_for_platform(port, 'linux2', 'apache2-debian-httpd.conf')
+ port._apache_version = lambda: '2.2'
+ self._assert_config_file_for_platform(port, 'linux2', 'debian-httpd-2.2.conf')
self._assert_config_file_for_platform(port, 'mac', 'apache2-httpd.conf')
self._assert_config_file_for_platform(port, 'win32', 'apache2-httpd.conf') # win32 isn't a supported sys.platform. AppleWin/WinCairo/WinCE ports all use cygwin.
@@ -348,7 +445,7 @@ class PortTestCase(unittest.TestCase):
# Mock out _apache_config_file_name_for_platform to ignore the passed sys.platform value.
port._apache_config_file_name_for_platform = lambda platform: 'httpd.conf'
- self.assertEqual(port._path_to_apache_config_file(), '/mock-checkout/LayoutTests/http/conf/httpd.conf')
+ self.assertEqual(port._path_to_apache_config_file(), '/mock-checkout/third_party/WebKit/LayoutTests/http/conf/httpd.conf')
# Check that even if we mock out _apache_config_file_name, the environment variable takes precedence.
saved_environ = os.environ.copy()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py
index 58ccb5c7ad3..f9ec1a7dc54 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py
@@ -363,7 +363,7 @@ class ServerProcess(object):
if not self._proc:
self._start()
- def stop(self, timeout_secs=3.0):
+ def stop(self, timeout_secs=0.0):
if not self._proc:
return (None, None)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py
index 99cf2d5757b..607bc516617 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py
@@ -30,7 +30,7 @@
class MockServerProcess(object):
def __init__(self, port_obj=None, name=None, cmd=None, env=None, universal_newlines=False, treat_no_data_as_crash=False, logging=False, lines=None, crashed=False):
self.timed_out = False
- self.lines = lines or []
+ self.lines = lines or ['#READY']
self.crashed = crashed
self.writes = []
self.cmd = cmd
@@ -71,7 +71,7 @@ class MockServerProcess(object):
def start(self):
self.started = True
- def stop(self, kill_directly=False):
+ def stop(self, timeout_sec=0.0):
self.stopped = True
return
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py
index 8257e324181..1afbf5b9f3d 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/test.py
@@ -31,9 +31,10 @@ import copy
import sys
import time
-from webkitpy.layout_tests.port import Port, Driver, DriverOutput
+from webkitpy.layout_tests.port import DeviceFailure, Driver, DriverOutput, Port
from webkitpy.layout_tests.port.base import VirtualTestSuite
from webkitpy.layout_tests.models.test_configuration import TestConfiguration
+from webkitpy.layout_tests.models import test_run_results
from webkitpy.common.system.filesystem_mock import MockFileSystem
from webkitpy.common.system.crashlogs import CrashLogs
@@ -51,6 +52,7 @@ class TestInstance(object):
self.error = ''
self.timeout = False
self.is_reftest = False
+ self.device_failure = False
# The values of each field are treated as raw byte strings. They
# will be converted to unicode strings where appropriate using
@@ -98,18 +100,19 @@ class TestList(object):
return self.tests[item]
#
-# These numbers may need to be updated whenever we add or delete tests.
+# These numbers may need to be updated whenever we add or delete tests. This includes virtual tests.
#
-TOTAL_TESTS = 105
-TOTAL_SKIPS = 25
+TOTAL_TESTS = 110
+TOTAL_SKIPS = 28
UNEXPECTED_PASSES = 1
-UNEXPECTED_FAILURES = 23
+UNEXPECTED_FAILURES = 25
def unit_test_list():
tests = TestList()
tests.add('failures/expected/crash.html', crash=True)
tests.add('failures/expected/exception.html', exception=True)
+ tests.add('failures/expected/device_failure.html', device_failure=True)
tests.add('failures/expected/timeout.html', timeout=True)
tests.add('failures/expected/missing_text.html', expected_text=None)
tests.add('failures/expected/needsrebaseline.html', actual_text='needsrebaseline text')
@@ -141,6 +144,7 @@ def unit_test_list():
tests.add('failures/expected/newlines_with_excess_CR.html',
expected_text="foo\r\r\r\n", actual_text="foo\n")
tests.add('failures/expected/text.html', actual_text='text_fail-png')
+ tests.add('failures/expected/crash_then_text.html')
tests.add('failures/expected/skip_text.html', actual_text='text diff')
tests.add('failures/flaky/text.html')
tests.add('failures/unexpected/missing_text.html', expected_text=None)
@@ -171,6 +175,7 @@ layer at (0,0) size 800x34
actual_checksum='text-image-checksum_fail-checksum')
tests.add('failures/unexpected/skip_pass.html')
tests.add('failures/unexpected/text.html', actual_text='text_fail-txt')
+ tests.add('failures/unexpected/text_then_crash.html')
tests.add('failures/unexpected/timeout.html', timeout=True)
tests.add('http/tests/passes/text.html')
tests.add('http/tests/passes/image.html')
@@ -199,6 +204,10 @@ layer at (0,0) size 800x34
# For reftests.
tests.add_reftest('passes/reftest.html', 'passes/reftest-expected.html', same_image=True)
+
+ # This adds a different virtual reference to ensure that that also works.
+ tests.add('virtual/passes/reftest-expected.html', actual_checksum='xxx', actual_image='XXX', is_reftest=True)
+
tests.add_reftest('passes/mismatch.html', 'passes/mismatch-expected-mismatch.html', same_image=False)
tests.add_reftest('passes/svgreftest.svg', 'passes/svgreftest-expected.svg', same_image=True)
tests.add_reftest('passes/xhtreftest.xht', 'passes/xhtreftest-expected.html', same_image=True)
@@ -272,6 +281,7 @@ def add_unit_tests_to_mock_filesystem(filesystem):
if not filesystem.exists('/mock-checkout/LayoutTests/TestExpectations'):
filesystem.write_text_file('/mock-checkout/LayoutTests/TestExpectations', """
Bug(test) failures/expected/crash.html [ Crash ]
+Bug(test) failures/expected/crash_then_text.html [ Failure ]
Bug(test) failures/expected/image.html [ ImageOnlyFailure ]
Bug(test) failures/expected/needsrebaseline.html [ NeedsRebaseline ]
Bug(test) failures/expected/needsmanualrebaseline.html [ NeedsManualRebaseline ]
@@ -290,6 +300,7 @@ Bug(test) failures/expected/text.html [ Failure ]
Bug(test) failures/expected/timeout.html [ Timeout ]
Bug(test) failures/expected/keyboard.html [ WontFix ]
Bug(test) failures/expected/exception.html [ WontFix ]
+Bug(test) failures/expected/device_failure.html [ WontFix ]
Bug(test) failures/unexpected/pass.html [ Failure ]
Bug(test) passes/skipped/skip.html [ Skip ]
Bug(test) passes/text.html [ Pass ]
@@ -354,6 +365,14 @@ class TestPort(Port):
'test-win-win7', 'test-win-xp',
)
+ FALLBACK_PATHS = {
+ 'xp': ['test-win-win7', 'test-win-xp'],
+ 'win7': ['test-win-win7'],
+ 'leopard': ['test-mac-leopard', 'test-mac-snowleopard'],
+ 'snowleopard': ['test-mac-snowleopard'],
+ 'lucid': ['test-linux-x86_64', 'test-win-win7'],
+ }
+
@classmethod
def determine_full_port_name(cls, host, options, port_name):
if port_name == 'test':
@@ -392,6 +411,11 @@ class TestPort(Port):
}
self._version = version_map[self._name]
+ def repository_paths(self):
+ """Returns a list of (repository_name, repository_path) tuples of its depending code base."""
+ # FIXME: We override this just to keep the perf tests happy.
+ return [('blink', self.layout_tests_dir())]
+
def buildbot_archives_baselines(self):
return self._name != 'test-win-xp'
@@ -403,27 +427,14 @@ class TestPort(Port):
# the mock_drt Driver. We return something, but make sure it's useless.
return 'MOCK _path_to_driver'
- def baseline_search_path(self):
- search_paths = {
- 'test-mac-snowleopard': ['test-mac-snowleopard'],
- 'test-mac-leopard': ['test-mac-leopard', 'test-mac-snowleopard'],
- 'test-win-win7': ['test-win-win7'],
- 'test-win-xp': ['test-win-xp', 'test-win-win7'],
- 'test-linux-x86_64': ['test-linux-x86_64', 'test-win-win7'],
- }
- return [self._webkit_baseline_path(d) for d in search_paths[self.name()]]
-
def default_child_processes(self):
return 1
- def worker_startup_delay_secs(self):
- return 0
-
def check_build(self, needs_http, printer):
- return True
+ return test_run_results.OK_EXIT_STATUS
def check_sys_deps(self, needs_http):
- return True
+ return test_run_results.OK_EXIT_STATUS
def default_configuration(self):
return 'Release'
@@ -506,6 +517,9 @@ class TestPort(Port):
def path_to_generic_test_expectations_file(self):
return self._generic_expectations_path
+ def _port_specific_expectations_files(self):
+ return [self._filesystem.join(self._webkit_baseline_path(d), 'TestExpectations') for d in ['test', 'test-win-xp']]
+
def all_test_configurations(self):
"""Returns a sequence of the TestConfigurations the port supports."""
# By default, we assume we want to test every graphics type in
@@ -539,8 +553,8 @@ class TestPort(Port):
def virtual_test_suites(self):
return [
- VirtualTestSuite('virtual/passes', 'passes', ['--virtual-arg']),
- VirtualTestSuite('virtual/skipped', 'failures/expected', ['--virtual-arg2']),
+ VirtualTestSuite('passes', 'passes', ['--virtual-arg'], use_legacy_naming=True),
+ VirtualTestSuite('skipped', 'failures/expected', ['--virtual-arg2'], use_legacy_naming=True),
]
@@ -558,13 +572,6 @@ class TestDriver(Driver):
return [self._port._path_to_driver()] + [pixel_tests_flag] + self._port.get_option('additional_drt_flag', []) + per_test_args
def run_test(self, driver_input, stop_when_done):
- base = self._port.lookup_virtual_test_base(driver_input.test_name)
- if base:
- virtual_driver_input = copy.copy(driver_input)
- virtual_driver_input.test_name = base
- virtual_driver_input.args = self._port.lookup_virtual_test_args(driver_input.test_name)
- return self.run_test(virtual_driver_input, stop_when_done)
-
if not self.started:
self.started = True
self.pid = TestDriver.next_pid
@@ -578,14 +585,36 @@ class TestDriver(Driver):
raise KeyboardInterrupt
if test.exception:
raise ValueError('exception from ' + test_name)
+ if test.device_failure:
+ raise DeviceFailure('device failure in ' + test_name)
audio = None
actual_text = test.actual_text
+ crash = test.crash
+ web_process_crash = test.web_process_crash
- if 'flaky' in test_name and not test_name in self._port._flakes:
+ if 'flaky/text.html' in test_name and not test_name in self._port._flakes:
self._port._flakes.add(test_name)
actual_text = 'flaky text failure'
+ if 'crash_then_text.html' in test_name:
+ if test_name in self._port._flakes:
+ actual_text = 'text failure'
+ else:
+ self._port._flakes.add(test_name)
+ crashed_process_name = self._port.driver_name()
+ crashed_pid = 1
+ crash = True
+
+ if 'text_then_crash.html' in test_name:
+ if test_name in self._port._flakes:
+ crashed_process_name = self._port.driver_name()
+ crashed_pid = 1
+ crash = True
+ else:
+ self._port._flakes.add(test_name)
+ actual_text = 'text failure'
+
if actual_text and test_args and test_name == 'passes/args.html':
actual_text = actual_text + ' ' + ' '.join(test_args)
@@ -593,10 +622,10 @@ class TestDriver(Driver):
audio = base64.b64decode(test.actual_audio)
crashed_process_name = None
crashed_pid = None
- if test.crash:
+ if crash:
crashed_process_name = self._port.driver_name()
crashed_pid = 1
- elif test.web_process_crash:
+ elif web_process_crash:
crashed_process_name = 'WebProcess'
crashed_pid = 2
@@ -613,7 +642,7 @@ class TestDriver(Driver):
else:
image = test.actual_image
return DriverOutput(actual_text, image, test.actual_checksum, audio,
- crash=test.crash or test.web_process_crash, crashed_process_name=crashed_process_name,
+ crash=(crash or web_process_crash), crashed_process_name=crashed_process_name,
crashed_pid=crashed_pid, crash_log=crash_log,
test_time=time.time() - start_time, timeout=test.timeout, error=test.error, pid=self.pid)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py
index df9db13fbc4..d421db93ad8 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py
@@ -31,13 +31,16 @@
import os
import logging
-import chromium
+from webkitpy.layout_tests.breakpad.dump_reader_win import DumpReaderWin
+from webkitpy.layout_tests.models import test_run_results
+from webkitpy.layout_tests.port import base
+from webkitpy.layout_tests.servers import crash_service
_log = logging.getLogger(__name__)
-class WinPort(chromium.ChromiumPort):
+class WinPort(base.Port):
port_name = 'win'
# FIXME: Figure out how to unify this with base.TestConfiguration.all_systems()?
@@ -61,12 +64,42 @@ class WinPort(chromium.ChromiumPort):
return port_name
def __init__(self, host, port_name, **kwargs):
- chromium.ChromiumPort.__init__(self, host, port_name, **kwargs)
+ super(WinPort, self).__init__(host, port_name, **kwargs)
self._version = port_name[port_name.index('win-') + len('win-'):]
assert self._version in self.SUPPORTED_VERSIONS, "%s is not in %s" % (self._version, self.SUPPORTED_VERSIONS)
+ if not self.get_option('disable_breakpad'):
+ self._dump_reader = DumpReaderWin(host, self._build_path())
+ self._crash_service = None
+ self._crash_service_available = None
+
+ def additional_drt_flag(self):
+ flags = super(WinPort, self).additional_drt_flag()
+ if not self.get_option('disable_breakpad'):
+ flags += ['--enable-crash-reporter', '--crash-dumps-dir=%s' % self._dump_reader.crash_dumps_directory()]
+ return flags
+
+ def setup_test_run(self):
+ super(WinPort, self).setup_test_run()
+
+ if not self.get_option('disable_breakpad'):
+ assert not self._crash_service, 'Already running a crash service'
+ if self._crash_service_available == None:
+ self._crash_service_available = self._check_crash_service_available()
+ if not self._crash_service_available:
+ return
+ service = crash_service.CrashService(self, self._dump_reader.crash_dumps_directory())
+ service.start()
+ self._crash_service = service
+
+ def clean_up_test_run(self):
+ super(WinPort, self).clean_up_test_run()
+
+ if self._crash_service:
+ self._crash_service.stop()
+ self._crash_service = None
def setup_environ_for_server(self, server_name=None):
- env = chromium.ChromiumPort.setup_environ_for_server(self, server_name)
+ env = super(WinPort, self).setup_environ_for_server(server_name)
# FIXME: lighttpd depends on some environment variable we're not whitelisting.
# We should add the variable to an explicit whitelist in base.Port.
@@ -92,8 +125,13 @@ class WinPort(chromium.ChromiumPort):
return []
def check_build(self, needs_http, printer):
- result = chromium.ChromiumPort.check_build(self, needs_http, printer)
- if not result:
+ result = super(WinPort, self).check_build(needs_http, printer)
+
+ self._crash_service_available = self._check_crash_service_available()
+ if not self._crash_service_available:
+ result = test_run_results.UNEXPECTED_ERROR_EXIT_STATUS
+
+ if result:
_log.error('For complete Windows build requirements, please see:')
_log.error('')
_log.error(' http://dev.chromium.org/developers/how-tos/build-instructions-windows')
@@ -139,9 +177,31 @@ class WinPort(chromium.ChromiumPort):
binary_name = 'LayoutTestHelper.exe'
return self._build_path(binary_name)
+ def _path_to_crash_service(self):
+ binary_name = 'content_shell_crash_service.exe'
+ return self._build_path(binary_name)
+
def _path_to_image_diff(self):
binary_name = 'image_diff.exe'
return self._build_path(binary_name)
def _path_to_wdiff(self):
return self.path_from_chromium_base('third_party', 'cygwin', 'bin', 'wdiff.exe')
+
+ def _check_crash_service_available(self):
+ """Checks whether the crash service binary is present."""
+ result = self._check_file_exists(self._path_to_crash_service(), "content_shell_crash_service.exe")
+ if not result:
+ _log.error(" Could not find crash service, unexpected crashes won't be symbolized.")
+ _log.error(' Did you build the target all_webkit?')
+ _log.error('')
+ return result
+
+ def look_for_new_crash_logs(self, crashed_processes, start_time):
+ if self.get_option('disable_breakpad'):
+ return None
+ return self._dump_reader.look_for_new_crash_logs(crashed_processes, start_time)
+
+ def clobber_old_port_specific_results(self):
+ if not self.get_option('disable_breakpad'):
+ self._dump_reader.clobber_old_results()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py
index cfe6a0786f6..66962ae2145 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py
@@ -32,12 +32,12 @@ import webkitpy.thirdparty.unittest2 as unittest
from webkitpy.common.system import outputcapture
from webkitpy.common.system.executive_mock import MockExecutive
from webkitpy.common.system.filesystem_mock import MockFileSystem
-from webkitpy.layout_tests.port import chromium_port_testcase
+from webkitpy.layout_tests.port import port_testcase
from webkitpy.layout_tests.port import win
from webkitpy.tool.mocktool import MockOptions
-class WinPortTest(chromium_port_testcase.ChromiumPortTestCase):
+class WinPortTest(port_testcase.PortTestCase):
port_name = 'win'
port_maker = win.WinPort
os_name = 'win'
@@ -59,12 +59,12 @@ class WinPortTest(chromium_port_testcase.ChromiumPortTestCase):
def test_setup_environ_for_server_cygpath(self):
port = self.make_port()
env = port.setup_environ_for_server(port.driver_name())
- self.assertEqual(env['CYGWIN_PATH'], '/mock-checkout/Source/WebKit/chromium/third_party/cygwin/bin')
+ self.assertEqual(env['CYGWIN_PATH'], '/mock-checkout/third_party/cygwin/bin')
def test_setup_environ_for_server_register_cygwin(self):
port = self.make_port(options=MockOptions(register_cygwin=True, results_directory='/'))
port._executive = MockExecutive(should_log=True)
- expected_logs = "MOCK run_command: ['/mock-checkout/Source/WebKit/chromium/third_party/cygwin/setup_mount.bat'], cwd=None\n"
+ expected_logs = "MOCK run_command: ['/mock-checkout/third_party/cygwin/setup_mount.bat'], cwd=None\n"
output = outputcapture.OutputCapture()
output.assert_outputs(self, port.setup_environ_for_server, expected_logs=expected_logs)
@@ -100,15 +100,15 @@ class WinPortTest(chromium_port_testcase.ChromiumPortTestCase):
def test_build_path(self):
# Test that optional paths are used regardless of whether they exist.
options = MockOptions(configuration='Release', build_directory='/foo')
- self.assert_build_path(options, ['/mock-checkout/Source/WebKit/chromium/out/Release'], '/foo/Release')
+ self.assert_build_path(options, ['/mock-checkout/out/Release'], '/foo/Release')
# Test that optional relative paths are returned unmodified.
options = MockOptions(configuration='Release', build_directory='foo')
- self.assert_build_path(options, ['/mock-checkout/Source/WebKit/chromium/out/Release'], 'foo/Release')
+ self.assert_build_path(options, ['/mock-checkout/out/Release'], 'foo/Release')
# Test that we prefer the legacy dir over the new dir.
options = MockOptions(configuration='Release', build_directory=None)
- self.assert_build_path(options, ['/mock-checkout/Source/WebKit/chromium/build/Release', '/mock-checkout/Source/WebKit/chromium/out'], '/mock-checkout/Source/WebKit/chromium/build/Release')
+ self.assert_build_path(options, ['/mock-checkout/build/Release', '/mock-checkout/out'], '/mock-checkout/build/Release')
def test_build_path_timestamps(self):
options = MockOptions(configuration='Release', build_directory=None)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/comments.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_types.py
index 771953e698c..af80c749577 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/comments.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_types.py
@@ -1,5 +1,6 @@
-# Copyright (c) 2009 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple Inc. All rights reserved.
+#!/usr/bin/python
+#
+# Copyright (C) 2013 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
@@ -26,17 +27,28 @@
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (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.config import urls
+import optparse
+
+from webkitpy.layout_tests.controllers import layout_test_finder
+
+def main(host, argv):
+ port = host.port_factory.get()
-def bug_comment_from_svn_revision(svn_revision):
- return "Committed r%s: <%s>" % (svn_revision, urls.view_revision_url(svn_revision))
+ parser = optparse.OptionParser()
+ parser.add_option('--test-list', action='append')
+ parser.add_option('--type', action='append',
+ help='limit to tests of type X (valid values %s)' % port.ALL_TEST_TYPES)
+ options, args = parser.parse_args(argv)
+ finder = layout_test_finder.LayoutTestFinder(port, options)
+ _, tests = finder.find_tests(options, args)
-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)
+ for test_name in tests:
+ test_type = port.test_type(test_name)
+ if options.type:
+ if test_type in options.type:
+ host.print_(test_name)
+ else:
+ host.print_(test_name, test_type)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/ports_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_types_unittest.py
index 319158625fb..8d980a40334 100644..100755
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/common/config/ports_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/print_layout_test_types_unittest.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (C) 2013 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
@@ -28,13 +28,32 @@
import webkitpy.thirdparty.unittest2 as unittest
-from webkitpy.common.config.ports import *
+from webkitpy.common.host_mock import MockHost
+from webkitpy.layout_tests.print_layout_test_types import main
-class DeprecatedPortTest(unittest.TestCase):
- def test_chromium_port(self):
- self.assertEqual(ChromiumPort().flag(), "--port=chromium")
- self.assertEqual(ChromiumPort().run_webkit_tests_command(), DeprecatedPort().script_shell_command("new-run-webkit-tests") + ["--chromium", "--skip-failing-tests"])
+class PrintLayoutTestTimesTest(unittest.TestCase):
- def test_chromium_xvfb_port(self):
- self.assertEqual(ChromiumXVFBPort().run_webkit_tests_command(), ['xvfb-run'] + DeprecatedPort().script_shell_command('new-run-webkit-tests') + ['--chromium', '--skip-failing-tests'])
+ def check(self, args, expected_output, files=None):
+ host = MockHost()
+ files = files or {}
+ for path, contents in files.items():
+ host.filesystem.write_binary_file(path, contents)
+ orig_get = host.port_factory.get
+ host.port_factory.get = lambda *args, **kwargs: orig_get('test')
+ main(host, args)
+ self.assertEqual(host.stdout.getvalue(), expected_output)
+
+ def test_test_list(self):
+ files = {'/tmp/test_list': 'passes/image.html'}
+ self.check(['--test-list', '/tmp/test_list'], 'passes/image.html pixel\n', files=files)
+
+ def test_type(self):
+ self.check(['--type', 'audio', 'passes'], 'passes/audio.html\n')
+
+ def test_basic(self):
+ self.check(['failures/unexpected/missing_image.html', 'passes/image.html', 'passes/audio.html', 'passes/reftest.html'],
+ 'failures/unexpected/missing_image.html text\n'
+ 'passes/image.html pixel\n'
+ 'passes/audio.html audio\n'
+ 'passes/reftest.html ref\n')
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
index 1f22807b5ab..c92e13172c9 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
@@ -36,7 +36,7 @@ import traceback
from webkitpy.common.host import Host
from webkitpy.layout_tests.controllers.manager import Manager
-from webkitpy.layout_tests.models.test_run_results import INTERRUPTED_EXIT_STATUS
+from webkitpy.layout_tests.models import test_run_results
from webkitpy.layout_tests.port import configuration_options, platform_options
from webkitpy.layout_tests.views import buildbot_results
from webkitpy.layout_tests.views import printing
@@ -45,10 +45,6 @@ from webkitpy.layout_tests.views import printing
_log = logging.getLogger(__name__)
-# This is a randomly chosen exit code that can be tested against to
-# indicate that an unexpected exception occurred.
-EXCEPTIONAL_EXIT_STATUS = 254
-
def main(argv, stdout, stderr):
options, args = parse_args(argv)
@@ -71,23 +67,26 @@ def main(argv, stdout, stderr):
except NotImplementedError, e:
# FIXME: is this the best way to handle unsupported port names?
print >> stderr, str(e)
- return EXCEPTIONAL_EXIT_STATUS
+ return test_run_results.UNEXPECTED_ERROR_EXIT_STATUS
try:
run_details = run(port, options, args, stderr)
- if run_details.exit_code != -1 and not run_details.initial_results.keyboard_interrupted:
+ if run_details.exit_code not in test_run_results.ERROR_CODES and not run_details.initial_results.keyboard_interrupted:
bot_printer = buildbot_results.BuildBotPrinter(stdout, options.debug_rwt_logging)
bot_printer.print_results(run_details)
return run_details.exit_code
# We need to still handle KeyboardInterrupt, atleast for webkitpy unittest cases.
except KeyboardInterrupt:
- return INTERRUPTED_EXIT_STATUS
+ return test_run_results.INTERRUPTED_EXIT_STATUS
+ except test_run_results.TestRunException as e:
+ print >> stderr, e.msg
+ return e.code
except BaseException as e:
if isinstance(e, Exception):
print >> stderr, '\n%s raised: %s' % (e.__class__.__name__, str(e))
traceback.print_exc(file=stderr)
- return EXCEPTIONAL_EXIT_STATUS
+ return test_run_results.UNEXPECTED_ERROR_EXIT_STATUS
def parse_args(args):
@@ -252,6 +251,8 @@ def parse_args(args):
help="Output per-test profile information, using the specified profiler."),
optparse.make_option("--driver-logging", action="store_true",
help="Print detailed logging of the driver/content_shell"),
+ optparse.make_option("--disable-breakpad", action="store_true",
+ help="Don't use breakpad to symbolize unexpected crashes."),
]))
option_group_definitions.append(("Miscellaneous Options", [
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py
index 3b36471ec0b..1bf13fb5308 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py
@@ -50,7 +50,7 @@ from webkitpy.common.host_mock import MockHost
from webkitpy.layout_tests import port
from webkitpy.layout_tests import run_webkit_tests
-from webkitpy.layout_tests.models.test_run_results import INTERRUPTED_EXIT_STATUS
+from webkitpy.layout_tests.models import test_run_results
from webkitpy.layout_tests.port import Port
from webkitpy.layout_tests.port import test
from webkitpy.test.skip import skip_if
@@ -283,6 +283,12 @@ class RunTest(unittest.TestCase, StreamTestingMixin):
self.assertRaises(BaseException, logging_run,
['--child-processes', '2', '--skipped=ignore', 'failures/expected/exception.html', 'passes/text.html'], tests_included=True, shared_port=False)
+ def test_device_failure(self):
+ # Test that we handle a device going offline during a test properly.
+ details, regular_output, _ = logging_run(['failures/expected/device_failure.html'], tests_included=True)
+ self.assertEqual(details.exit_code, 0)
+ self.assertTrue('worker/0 has failed' in regular_output.getvalue())
+
def test_full_results_html(self):
host = MockHost()
details, _, _ = logging_run(['--full-results-html'], host=host)
@@ -293,7 +299,7 @@ class RunTest(unittest.TestCase, StreamTestingMixin):
# Note that this also tests running a test marked as SKIP if
# you specify it explicitly.
details, _, _ = logging_run(['failures/expected/keyboard.html', '--child-processes', '1'], tests_included=True)
- self.assertEqual(details.exit_code, INTERRUPTED_EXIT_STATUS)
+ self.assertEqual(details.exit_code, test_run_results.INTERRUPTED_EXIT_STATUS)
if self.should_test_processes:
_, regular_output, _ = logging_run(['failures/expected/keyboard.html', 'passes/text.html', '--child-processes', '2', '--skipped=ignore'], tests_included=True, shared_port=False)
@@ -301,12 +307,12 @@ class RunTest(unittest.TestCase, StreamTestingMixin):
def test_no_tests_found(self):
details, err, _ = logging_run(['resources'], tests_included=True)
- self.assertEqual(details.exit_code, -1)
+ self.assertEqual(details.exit_code, test_run_results.NO_TESTS_EXIT_STATUS)
self.assertContains(err, 'No tests to run.\n')
def test_no_tests_found_2(self):
details, err, _ = logging_run(['foo'], tests_included=True)
- self.assertEqual(details.exit_code, -1)
+ self.assertEqual(details.exit_code, test_run_results.NO_TESTS_EXIT_STATUS)
self.assertContains(err, 'No tests to run.\n')
def test_natural_order(self):
@@ -463,7 +469,7 @@ class RunTest(unittest.TestCase, StreamTestingMixin):
self.assertEqual(['passes/text.html'], tests_run)
host.filesystem.remove(filename)
details, err, user = logging_run(['--test-list=%s' % filename], tests_included=True, host=host)
- self.assertEqual(details.exit_code, -1)
+ self.assertEqual(details.exit_code, test_run_results.NO_TESTS_EXIT_STATUS)
self.assertNotEmpty(err)
def test_test_list_with_prefix(self):
@@ -521,6 +527,25 @@ class RunTest(unittest.TestCase, StreamTestingMixin):
self.assertTrue(json_string.find('"num_regressions":2') != -1)
self.assertTrue(json_string.find('"num_flaky":0') != -1)
+ def test_different_failure_on_retry(self):
+ # This tests that if a test fails two different ways -- both unexpected
+ # -- we treat it as a failure rather than a flaky result. We use the
+ # initial failure for simplicity and consistency w/ the flakiness
+ # dashboard, even if the second failure is worse.
+
+ details, err, _ = logging_run(['--retry-failures', 'failures/unexpected/text_then_crash.html'], tests_included=True)
+ self.assertEqual(details.exit_code, 1)
+ self.assertEqual(details.summarized_failing_results['tests']['failures']['unexpected']['text_then_crash.html']['actual'],
+ 'TEXT CRASH')
+
+ # If we get a test that fails two different ways -- but the second one is expected --
+ # we should treat it as a flaky result and report the initial unexpected failure type
+ # to the dashboard. However, the test should be considered passing.
+ details, err, _ = logging_run(['--retry-failures', 'failures/expected/crash_then_text.html'], tests_included=True)
+ self.assertEqual(details.exit_code, 0)
+ self.assertEqual(details.summarized_failing_results['tests']['failures']['expected']['crash_then_text.html']['actual'],
+ 'CRASH FAIL')
+
def test_pixel_test_directories(self):
host = MockHost()
@@ -623,7 +648,7 @@ class RunTest(unittest.TestCase, StreamTestingMixin):
host = MockHost()
details, err, _ = logging_run(['--debug-rwt-logging', 'failures/unexpected'], tests_included=True, host=host)
- self.assertEqual(details.exit_code, 17) # FIXME: This should be a constant in test.py .
+ self.assertEqual(details.exit_code, test.UNEXPECTED_FAILURES - 7) # FIXME: This should be a constant in test.py .
self.assertTrue('Retrying' in err.getvalue())
def test_retrying_default_value_test_list(self):
@@ -638,7 +663,7 @@ class RunTest(unittest.TestCase, StreamTestingMixin):
filename = '/tmp/foo.txt'
host.filesystem.write_text_file(filename, 'failures')
details, err, _ = logging_run(['--debug-rwt-logging', '--test-list=%s' % filename], tests_included=True, host=host)
- self.assertEqual(details.exit_code, 17)
+ self.assertEqual(details.exit_code, test.UNEXPECTED_FAILURES - 7)
self.assertTrue('Retrying' in err.getvalue())
def test_retrying_and_flaky_tests(self):
@@ -738,6 +763,10 @@ class RunTest(unittest.TestCase, StreamTestingMixin):
# The list of references should be empty since the test crashed and we didn't run any references.
self.assertEqual(test_results[0].references, [])
+ def test_reftest_with_virtual_reference(self):
+ _, err, _ = logging_run(['--details', 'virtual/passes/reftest.html'], tests_included=True)
+ self.assertTrue('ref: virtual/passes/reftest-expected.html' in err.getvalue())
+
def test_additional_platform_directory(self):
self.assertTrue(passing_run(['--additional-platform-directory', '/tmp/foo']))
self.assertTrue(passing_run(['--additional-platform-directory', '/tmp/../foo']))
@@ -784,7 +813,7 @@ class RunTest(unittest.TestCase, StreamTestingMixin):
stderr = StringIO.StringIO()
res = run_webkit_tests.main(['--platform', 'foo'], stdout, stderr)
- self.assertEqual(res, run_webkit_tests.EXCEPTIONAL_EXIT_STATUS)
+ self.assertEqual(res, test_run_results.UNEXPECTED_ERROR_EXIT_STATUS)
self.assertEqual(stdout.getvalue(), '')
self.assertTrue('unsupported platform' in stderr.getvalue())
@@ -796,7 +825,7 @@ class RunTest(unittest.TestCase, StreamTestingMixin):
port_name = 'mac-lion'
out = StringIO.StringIO()
err = StringIO.StringIO()
- self.assertEqual(run_webkit_tests.main(['--platform', port_name, 'fast/harness/results.html'], out, err), -1)
+ self.assertEqual(run_webkit_tests.main(['--platform', port_name, 'fast/harness/results.html'], out, err), test_run_results.UNEXPECTED_ERROR_EXIT_STATUS)
def test_verbose_in_child_processes(self):
# When we actually run multiple processes, we may have to reconfigure logging in the
@@ -962,15 +991,6 @@ class PortTest(unittest.TestCase):
def disabled_test_mac_lion(self):
self.assert_mock_port_works('mac-lion')
- def disabled_test_mac_lion_in_test_shell_mode(self):
- self.assert_mock_port_works('mac-lion', args=['--additional-drt-flag=--test-shell'])
-
- def disabled_test_qt_linux(self):
- self.assert_mock_port_works('qt-linux')
-
- def disabled_test_mac_lion(self):
- self.assert_mock_port_works('mac-lion')
-
class MainTest(unittest.TestCase):
def test_exception_handling(self):
@@ -983,7 +1003,7 @@ class MainTest(unittest.TestCase):
def successful_run(port, options, args, stderr):
class FakeRunDetails(object):
- exit_code = -1
+ exit_code = test_run_results.UNEXPECTED_ERROR_EXIT_STATUS
return FakeRunDetails()
@@ -995,14 +1015,14 @@ class MainTest(unittest.TestCase):
try:
run_webkit_tests.run = interrupting_run
res = run_webkit_tests.main([], stdout, stderr)
- self.assertEqual(res, INTERRUPTED_EXIT_STATUS)
+ self.assertEqual(res, test_run_results.INTERRUPTED_EXIT_STATUS)
run_webkit_tests.run = successful_run
res = run_webkit_tests.main(['--platform', 'test'], stdout, stderr)
- self.assertEqual(res, -1)
+ self.assertEqual(res, test_run_results.UNEXPECTED_ERROR_EXIT_STATUS)
run_webkit_tests.run = exception_raising_run
res = run_webkit_tests.main([], stdout, stderr)
- self.assertEqual(res, run_webkit_tests.EXCEPTIONAL_EXIT_STATUS)
+ self.assertEqual(res, test_run_results.UNEXPECTED_ERROR_EXIT_STATUS)
finally:
run_webkit_tests.run = orig_run_fn
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py
index eb64d82990e..e00a9a0cb44 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py
@@ -60,7 +60,7 @@ class LayoutTestApacheHttpd(http_server_base.HttpServerBase):
self._pid_file = self._filesystem.join(self._runtime_path, '%s.pid' % self._name)
test_dir = self._port_obj.layout_tests_dir()
- js_test_resources_dir = self._filesystem.join(test_dir, "fast", "js", "resources")
+ js_test_resources_dir = self._filesystem.join(test_dir, "resources")
media_resources_dir = self._filesystem.join(test_dir, "media")
mime_types_path = self._filesystem.join(test_dir, "http", "conf", "mime.types")
cert_file = self._filesystem.join(test_dir, "http", "conf", "webkit-httpd.pem")
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/crash_service.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/crash_service.py
new file mode 100644
index 00000000000..08ead87d1c2
--- /dev/null
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/crash_service.py
@@ -0,0 +1,87 @@
+# Copyright (C) 2013 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (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 crash service used by layout tests."""
+
+import logging
+import os
+import time
+
+from webkitpy.layout_tests.servers import http_server_base
+
+
+_log = logging.getLogger(__name__)
+
+
+class CrashService(http_server_base.HttpServerBase):
+
+ def __init__(self, port_obj, crash_dumps_dir):
+ """Args:
+ crash_dumps_dir: the absolute path to the directory where to store crash dumps
+ """
+ # Webkit tests
+ http_server_base.HttpServerBase.__init__(self, port_obj)
+ self._name = 'CrashService'
+ self._crash_dumps_dir = crash_dumps_dir
+
+ self._pid_file = self._filesystem.join(self._runtime_path, '%s.pid' % self._name)
+
+ def _spawn_process(self):
+ start_cmd = [self._port_obj._path_to_crash_service(),
+ '--dumps-dir=%s' % self._crash_dumps_dir,
+ '--no-window']
+ _log.debug('Starting crash service, cmd = "%s"' % " ".join(start_cmd))
+ process = self._executive.popen(start_cmd, shell=False, stderr=self._executive.PIPE)
+ pid = process.pid
+ self._filesystem.write_text_file(self._pid_file, str(pid))
+ return pid
+
+ def _stop_running_server(self):
+ # FIXME: It would be nice if we had a cleaner way of killing this process.
+ # Currently we throw away the process object created in _spawn_process,
+ # since there doesn't appear to be any way to kill the server any more
+ # cleanly using it than just killing the pid, and we need to support
+ # killing a pid directly anyway for run-webkit-httpd and run-webkit-websocketserver.
+ self._wait_for_action(self._check_and_kill)
+ if self._filesystem.exists(self._pid_file):
+ self._filesystem.remove(self._pid_file)
+
+ def _check_and_kill(self):
+ if self._executive.check_running_pid(self._pid):
+ host = self._port_obj.host
+ if host.platform.is_win() and not host.platform.is_cygwin():
+ # FIXME: https://bugs.webkit.org/show_bug.cgi?id=106838
+ # We need to kill all of the child processes as well as the
+ # parent, so we can't use executive.kill_process().
+ #
+ # If this is actually working, we should figure out a clean API.
+ self._executive.run_command(["taskkill.exe", "/f", "/t", "/pid", self._pid], error_handler=self._executive.ignore_error)
+ else:
+ self._executive.kill_process(self._pid)
+ return False
+ return True
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/crash_service_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/crash_service_unittest.py
new file mode 100644
index 00000000000..4204cb46967
--- /dev/null
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/crash_service_unittest.py
@@ -0,0 +1,81 @@
+# Copyright (C) 2013 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (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
+import webkitpy.thirdparty.unittest2 as unittest
+
+from webkitpy.common.host_mock import MockHost
+from webkitpy.layout_tests.port import test
+from webkitpy.layout_tests.servers.crash_service import CrashService
+from webkitpy.layout_tests.servers.http_server_base import ServerError
+
+
+class TestCrashService(unittest.TestCase):
+ def test_start_cmd(self):
+ # Fails on win - see https://bugs.webkit.org/show_bug.cgi?id=84726
+ if sys.platform in ('cygwin', 'win32'):
+ return
+
+ host = MockHost()
+ test_port = test.TestPort(host)
+ test_port._path_to_crash_service = lambda: "/mock/crash_service"
+
+ server = CrashService(test_port, "/mock/crash_dumps_dir")
+ self.assertRaises(ServerError, server.start)
+
+ def test_win32_start_and_stop(self):
+ host = MockHost()
+ test_port = test.TestPort(host)
+ test_port._path_to_crash_service = lambda: "/mock/crash_service"
+
+ host.platform.is_win = lambda: True
+ host.platform.is_cygwin = lambda: False
+
+ server = CrashService(test_port, "/mock/crash_dumps_dir")
+ server._check_that_all_ports_are_available = lambda: True
+ server._is_server_running_on_all_ports = lambda: True
+
+ server.start()
+ self.assertNotEquals(host.executive.calls, [])
+
+ def wait_for_action(action):
+ if action():
+ return True
+ return action()
+
+ def mock_returns(return_values):
+ def return_value_thunk(*args, **kwargs):
+ return return_values.pop(0)
+ return return_value_thunk
+
+ host.executive.check_running_pid = mock_returns([True, False])
+ server._wait_for_action = wait_for_action
+
+ server.stop()
+ self.assertEqual(['taskkill.exe', '/f', '/t', '/pid', 42], host.executive.calls[1])
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/http_server.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/http_server.py
index 604f76b8951..1fbf1321231 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/http_server.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/http_server.py
@@ -65,7 +65,7 @@ class Lighttpd(http_server_base.HttpServerBase):
self._layout_tests_dir = self._port_obj.layout_tests_dir()
self._webkit_tests = os.path.join(self._layout_tests_dir, 'http', 'tests')
- self._js_test_resource = os.path.join(self._layout_tests_dir, 'fast', 'js', 'resources')
+ self._js_test_resource = os.path.join(self._layout_tests_dir, 'resources')
self._media_resource = os.path.join(self._layout_tests_dir, 'media')
# Self generated certificate for SSL server (for client cert get
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/http_server_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/http_server_unittest.py
index 5507d989057..740cd2b5692 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/http_server_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/http_server_unittest.py
@@ -45,7 +45,7 @@ class TestHttpServer(unittest.TestCase):
host = MockHost()
test_port = test.TestPort(host)
host.filesystem.write_text_file(
- "/mock-checkout/Tools/Scripts/webkitpy/layout_tests/servers/lighttpd.conf", "Mock Config\n")
+ "/mock-checkout/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/lighttpd.conf", "Mock Config\n")
host.filesystem.write_text_file(
"/usr/lib/lighttpd/liblightcomp.dylib", "Mock dylib")
@@ -57,7 +57,7 @@ class TestHttpServer(unittest.TestCase):
config_file = host.filesystem.read_text_file("/mock/output_dir/lighttpd.conf")
self.assertEqual(re.findall(r"alias.url.+", config_file), [
- 'alias.url = ( "/js-test-resources" => "/test.checkout/LayoutTests/fast/js/resources" )',
+ 'alias.url = ( "/js-test-resources" => "/test.checkout/LayoutTests/resources" )',
'alias.url += ( "/mock/one-additional-dir" => "/mock-checkout/one-additional-dir" )',
'alias.url += ( "/mock/another-additional-dir" => "/mock-checkout/one-additional-dir" )',
'alias.url += ( "/media-resources" => "/test.checkout/LayoutTests/media" )',
@@ -67,7 +67,7 @@ class TestHttpServer(unittest.TestCase):
host = MockHost()
test_port = test.TestPort(host)
host.filesystem.write_text_file(
- "/mock-checkout/Tools/Scripts/webkitpy/layout_tests/servers/lighttpd.conf", "Mock Config\n")
+ "/mock-checkout/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/lighttpd.conf", "Mock Config\n")
host.filesystem.write_text_file(
"/usr/lib/lighttpd/liblightcomp.dylib", "Mock dylib")
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/websocket_server.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/websocket_server.py
index 2ffdc321dc6..9f47dee0d32 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/websocket_server.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/servers/websocket_server.py
@@ -93,11 +93,11 @@ class PyWebSocket(http_server.Lighttpd):
# The port objects are supposed to abstract this.
if self._root:
self._layout_tests = self._filesystem.abspath(self._root)
- self._web_socket_tests = self._filesystem.abspath(self._filesystem.join(self._root, 'http', 'tests', 'websocket', 'tests'))
+ self._web_socket_tests = self._filesystem.abspath(self._filesystem.join(self._root, 'http', 'tests', 'websocket'))
else:
try:
self._layout_tests = self._port_obj.layout_tests_dir()
- self._web_socket_tests = self._filesystem.join(self._layout_tests, 'http', 'tests', 'websocket', 'tests')
+ self._web_socket_tests = self._filesystem.join(self._layout_tests, 'http', 'tests', 'websocket')
except:
self._web_socket_tests = None
@@ -126,10 +126,9 @@ class PyWebSocket(http_server.Lighttpd):
python_interp, '-u', pywebsocket_script,
'--server-host', 'localhost',
'--port', str(self._port),
- # FIXME: Don't we have a self._port_obj.layout_test_path?
- '--document-root', self._filesystem.join(self._layout_tests, 'http', 'tests'),
+ '--document-root', self._web_socket_tests,
'--scan-dir', self._web_socket_tests,
- '--cgi-paths', '/websocket/tests',
+ '--cgi-paths', '/',
'--log-file', error_log,
]
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py
index 55b045f1775..598f638a81f 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py
@@ -77,7 +77,7 @@ class Printer(object):
def print_config(self, results_directory):
self._print_default("Using port '%s'" % self._port.name())
self._print_default("Test configuration: %s" % self._port.test_configuration())
- self._print_default("Placing test results in %s" % results_directory)
+ self._print_default("View the test results at file://%s/results.html" % results_directory)
# FIXME: should these options be in printing_options?
if self._options.new_baseline:
@@ -388,8 +388,13 @@ class Printer(object):
self._print_default(' base: %s' % base)
self._print_default(' args: %s' % args)
- for extension in ('.txt', '.png', '.wav'):
- self._print_baseline(test_name, extension)
+ references = self._port.reference_files(test_name)
+ if references:
+ for _, filename in references:
+ self._print_default(' ref: %s' % self._port.relative_test_filename(filename))
+ else:
+ for extension in ('.txt', '.png', '.wav'):
+ self._print_baseline(test_name, extension)
self._print_default(' exp: %s' % exp_str)
self._print_default(' got: %s' % got_str)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py
index 1554cc1e959..a28639add64 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py
@@ -132,7 +132,7 @@ class Testprinter(unittest.TestCase):
printer.print_config('/tmp')
self.assertIn("Using port 'test-mac-leopard'", err.getvalue())
self.assertIn('Test configuration: <leopard, x86, release>', err.getvalue())
- self.assertIn('Placing test results in /tmp', err.getvalue())
+ self.assertIn('View the test results at file:///tmp', err.getvalue())
self.assertIn('Baseline search path: test-mac-leopard -> test-mac-snowleopard -> generic', err.getvalue())
self.assertIn('Using Release build', err.getvalue())
self.assertIn('Pixel tests enabled', err.getvalue())
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftest.py
index 72e1e6aa68c..7abd6d7b836 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftest.py
@@ -163,6 +163,7 @@ class PerfTest(object):
_metrics_regex = re.compile(r'^(?P<metric>Time|Malloc|JS Heap):')
_statistics_keys = ['avg', 'median', 'stdev', 'min', 'max', 'unit', 'values']
_score_regex = re.compile(r'^(?P<key>' + r'|'.join(_statistics_keys) + r')\s+(?P<value>([0-9\.]+(,\s+)?)+)\s*(?P<unit>.*)')
+ _console_regex = re.compile(r'^CONSOLE MESSAGE:')
def _run_with_driver(self, driver, time_out_ms):
output = self.run_single(driver, self.test_path(), time_out_ms)
@@ -175,6 +176,7 @@ class PerfTest(object):
description_match = self._description_regex.match(line)
metric_match = self._metrics_regex.match(line)
score = self._score_regex.match(line)
+ console_match = self._console_regex.match(line)
if description_match:
self._description = description_match.group('description')
@@ -186,6 +188,9 @@ class PerfTest(object):
metric = self._ensure_metrics(current_metric, score.group('unit'))
metric.append_group(map(lambda value: float(value), score.group('value').split(', ')))
+ elif console_match:
+ # Ignore console messages such as deprecation warnings.
+ continue
else:
_log.error('ERROR: ' + line)
return False
@@ -199,7 +204,7 @@ class PerfTest(object):
return self._metrics[metric_name]
def run_single(self, driver, test_path, time_out_ms, should_run_pixel_test=False):
- return driver.run_test(DriverInput(test_path, time_out_ms, image_hash=None, should_run_pixel_test=should_run_pixel_test), stop_when_done=False)
+ return driver.run_test(DriverInput(test_path, time_out_ms, image_hash=None, should_run_pixel_test=should_run_pixel_test, args=[]), stop_when_done=False)
def run_failed(self, output):
if output.error:
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py
index 5e6aec7d170..2b2b2d0f513 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py
@@ -183,7 +183,7 @@ class PerfTestsRunner(object):
def write_throttled_update(self, msg):
pass
- if not self._port.check_build(needs_http=needs_http, printer=FakePrinter()):
+ if self._port.check_build(needs_http=needs_http, printer=FakePrinter()):
_log.error("Build not up to date for %s" % self._port._path_to_driver())
return self.EXIT_CODE_BAD_BUILD
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker.py
index f7cd3e5ef4f..e7a2dec01ae 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checker.py
@@ -144,7 +144,7 @@ _PATH_RULES_SPECIFIER = [
([# Jinja templates: files have .cpp or .h extensions, but contain
# template code, which can't be handled, so disable tests.
"Source/bindings/templates",
- "Source/core/scripts/templates"],
+ "Source/build/scripts/templates"],
["-"]),
([# IDL compiler reference output
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp.py
index 806f9413470..52519527f81 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp.py
@@ -36,7 +36,6 @@
"""Support for check-webkit-style."""
-import codecs
import math # for log
import os
import os.path
@@ -47,9 +46,7 @@ import sys
import unicodedata
from webkitpy.common.memoized import memoized
-
-# The key to use to provide a class to fake loading a header file.
-INCLUDE_IO_INJECTION_KEY = 'include_header_io'
+from webkitpy.common.system.filesystem import FileSystem
# Headers that we consider STL headers.
_STL_HEADERS = frozenset([
@@ -121,12 +118,6 @@ _OTHER_HEADER = 2
_MOC_HEADER = 3
-# A dictionary of items customize behavior for unit test. For example,
-# INCLUDE_IO_INJECTION_KEY allows providing a custom io class which allows
-# for faking a header file.
-_unit_test_config = {}
-
-
# 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.
@@ -3284,11 +3275,9 @@ def check_language(filename, clean_lines, line_number, file_extension, include_s
error(line_number, 'runtime/unsigned', 1,
'Omit int when using unsigned')
- # Check that we're not using static_cast<Text*>.
- if search(r'\bstatic_cast<Text\*>', line):
- error(line_number, 'readability/check', 4,
- 'Consider using toText helper function in WebCore/dom/Text.h '
- 'instead of static_cast<Text*>')
+ # Check for usage of static_cast<Classname*>.
+ check_for_object_static_cast(filename, line_number, line, error)
+
def check_identifier_name_in_declaration(filename, line_number, line, file_state, error):
"""Checks if identifier names contain any underscores.
@@ -3421,6 +3410,144 @@ def check_identifier_name_in_declaration(filename, line_number, line, file_state
number_of_identifiers += 1
line = line[matched.end():]
+
+def check_for_toFoo_definition(filename, pattern, error):
+ """ Reports for using static_cast instead of toFoo convenience function.
+
+ This function will output warnings to make sure you are actually using
+ the added toFoo conversion functions rather than directly hard coding
+ the static_cast<Classname*> call. For example, you should toHTMLELement(Node*)
+ to convert Node* to HTMLElement*, instead of static_cast<HTMLElement*>(Node*)
+
+ Args:
+ filename: The name of the header file in which to check for toFoo definition.
+ pattern: The conversion function pattern to grep for.
+ error: The function to call with any errors found.
+ """
+ def get_abs_filepath(filename):
+ fileSystem = FileSystem()
+ base_dir = fileSystem.path_to_module(FileSystem.__module__).split('WebKit', 1)[0]
+ base_dir = ''.join((base_dir, 'WebKit/Source'))
+ for root, dirs, names in os.walk(base_dir):
+ if filename in names:
+ return os.path.join(root, filename)
+ return None
+
+ def grep(lines, pattern, error):
+ matches = []
+ function_state = None
+ for line_number in xrange(lines.num_lines()):
+ line = (lines.elided[line_number]).rstrip()
+ try:
+ if pattern in line:
+ if not function_state:
+ function_state = _FunctionState(1)
+ detect_functions(lines, line_number, function_state, error)
+ # Exclude the match of dummy conversion function. Dummy function is just to
+ # catch invalid conversions and shouldn't be part of possible alternatives.
+ result = re.search(r'%s(\s+)%s' % ("void", pattern), line)
+ if not result:
+ matches.append([line, function_state.body_start_position.row, function_state.end_position.row + 1])
+ function_state = None
+ except UnicodeDecodeError:
+ # There would be no non-ascii characters in the codebase ever. The only exception
+ # would be comments/copyright text which might have non-ascii characters. Hence,
+ # it is prefectly safe to catch the UnicodeDecodeError and just pass the line.
+ pass
+
+ return matches
+
+ def check_in_mock_header(filename, matches=None):
+ if not filename == 'Foo.h':
+ return False
+
+ header_file = None
+ try:
+ header_file = CppChecker.fs.read_text_file(filename)
+ except IOError:
+ return False
+ line_number = 0
+ for line in header_file:
+ line_number += 1
+ matched = re.search(r'\btoFoo\b', line)
+ if matched:
+ matches.append(['toFoo', line_number, line_number + 3])
+ return True
+
+ # For unit testing only, avoid header search and lookup locally.
+ matches = []
+ mock_def_found = check_in_mock_header(filename, matches)
+ if mock_def_found:
+ return matches
+
+ # Regular style check flow. Search for actual header file & defs.
+ file_path = get_abs_filepath(filename)
+ if not file_path:
+ return None
+ try:
+ f = open(file_path)
+ clean_lines = CleansedLines(f.readlines())
+ finally:
+ f.close()
+
+ # Make a list of all genuine alternatives to static_cast.
+ matches = grep(clean_lines, pattern, error)
+ return matches
+
+
+def check_for_object_static_cast(processing_file, line_number, line, error):
+ """Checks for a Cpp-style static cast on objects by looking for the pattern.
+
+ Args:
+ processing_file: The name of the processing 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.
+ """
+ matched = search(r'\bstatic_cast<(\s*\w*:?:?\w+\s*\*+\s*)>', line)
+ if not matched:
+ return
+
+ class_name = re.sub('[\*]', '', matched.group(1))
+ class_name = class_name.strip()
+ # Ignore (for now) when the casting is to void*,
+ if class_name == 'void':
+ return
+
+ namespace_pos = class_name.find(':')
+ if not namespace_pos == -1:
+ class_name = class_name[namespace_pos + 2:]
+
+ header_file = ''.join((class_name, '.h'))
+ matches = check_for_toFoo_definition(header_file, ''.join(('to', class_name)), error)
+ # Ignore (for now) if not able to find the header where toFoo might be defined.
+ # TODO: Handle cases where Classname might be defined in some other header or cpp file.
+ if matches is None:
+ return
+
+ report_error = True
+ # Ensure found static_cast instance is not from within toFoo definition itself.
+ if (os.path.basename(processing_file) == header_file):
+ for item in matches:
+ if line_number in range(item[1], item[2]):
+ report_error = False
+ break
+
+ if report_error:
+ if len(matches):
+ # toFoo is defined - enforce using it.
+ # TODO: Suggest an appropriate toFoo from the alternatives present in matches.
+ error(line_number, 'runtime/casting', 4,
+ 'static_cast of class objects is not allowed. Use to%s defined in %s.' %
+ (class_name, header_file))
+ else:
+ # No toFoo defined - enforce definition & usage.
+ # TODO: Automate the generation of toFoo() to avoid any slippages ever.
+ error(line_number, 'runtime/casting', 4,
+ 'static_cast of class objects is not allowed. Add to%s in %s and use it instead.' %
+ (class_name, header_file))
+
+
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.
@@ -3593,7 +3720,7 @@ def files_belong_to_same_module(filename_cpp, filename_h):
return files_belong_to_same_module, common_path
-def update_include_state(filename, include_state, io=codecs):
+def update_include_state(filename, include_state):
"""Fill up the include_state with new includes found from the file.
Args:
@@ -3604,10 +3731,9 @@ def update_include_state(filename, include_state, io=codecs):
Returns:
True if a header was succesfully added. False otherwise.
"""
- io = _unit_test_config.get(INCLUDE_IO_INJECTION_KEY, codecs)
header_file = None
try:
- header_file = io.open(filename, 'r', 'utf8', 'replace')
+ header_file = CppChecker.fs.read_text_file(filename)
except IOError:
return False
line_number = 0
@@ -3875,8 +4001,10 @@ class CppChecker(object):
'whitespace/todo',
])
+ fs = None
+
def __init__(self, file_path, file_extension, handle_style_error,
- min_confidence):
+ min_confidence, fs=None):
"""Create a CppChecker instance.
Args:
@@ -3888,6 +4016,7 @@ class CppChecker(object):
self.file_path = file_path
self.handle_style_error = handle_style_error
self.min_confidence = min_confidence
+ CppChecker.fs = fs or FileSystem()
# Useful for unit testing.
def __eq__(self, other):
@@ -3914,9 +4043,6 @@ class CppChecker(object):
# FIXME: Remove this function (requires refactoring unit tests).
-def process_file_data(filename, file_extension, lines, error, min_confidence, unit_test_config):
- global _unit_test_config
- _unit_test_config = unit_test_config
- checker = CppChecker(filename, file_extension, error, min_confidence)
+def process_file_data(filename, file_extension, lines, error, min_confidence, fs=None):
+ checker = CppChecker(filename, file_extension, error, min_confidence, fs)
checker.check(lines)
- _unit_test_config = {}
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py
index ab53f175ab6..6df53dac3bc 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py
@@ -35,7 +35,6 @@
# FIXME: Add a good test that tests UpdateIncludeState.
-import codecs
import os
import random
import re
@@ -43,6 +42,7 @@ import webkitpy.thirdparty.unittest2 as unittest
import cpp as cpp_style
from cpp import CppChecker
from ..filter import FilterConfiguration
+from webkitpy.common.system.filesystem import FileSystem
# This class works as an error collector and replaces cpp_style.Error
# function for the unit tests. We also verify each category we see
@@ -99,17 +99,6 @@ class ErrorCollector:
sys.exit('FATAL ERROR: There are no tests for category "%s"' % category)
-# 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."""
@@ -246,16 +235,16 @@ class CppStyleTestBase(unittest.TestCase):
# 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, unit_test_config={}):
+ def process_file_data(self, filename, file_extension, lines, error, fs=None):
"""Call cpp_style.process_file_data() with the min_confidence."""
return cpp_style.process_file_data(filename, file_extension, lines,
- error, self.min_confidence, unit_test_config)
+ error, self.min_confidence, fs)
- def perform_lint(self, code, filename, basic_error_rules, unit_test_config={}, lines_to_check=None):
+ def perform_lint(self, code, filename, basic_error_rules, fs=None, lines_to_check=None):
error_collector = ErrorCollector(self.assertTrue, FilterConfiguration(basic_error_rules), lines_to_check)
lines = code.split('\n')
extension = filename.split('.')[1]
- self.process_file_data(filename, extension, lines, error_collector, unit_test_config)
+ self.process_file_data(filename, extension, lines, error_collector, fs)
return error_collector.results()
# Perform lint on single line of input and return the error message.
@@ -304,11 +293,15 @@ class CppStyleTestBase(unittest.TestCase):
return self.perform_lint(code, 'test.cpp', basic_error_rules)
# Only include what you use errors.
- def perform_include_what_you_use(self, code, filename='foo.h', io=codecs):
+ def perform_include_what_you_use(self, code, filename='foo.h', fs=None):
basic_error_rules = ('-',
'+build/include_what_you_use')
- unit_test_config = {cpp_style.INCLUDE_IO_INJECTION_KEY: io}
- return self.perform_lint(code, filename, basic_error_rules, unit_test_config)
+ return self.perform_lint(code, filename, basic_error_rules, fs)
+
+ def perform_avoid_static_cast_of_objects(self, code, filename='foo.cpp', fs=None):
+ basic_error_rules = ('-',
+ '+runtime/casting')
+ return self.perform_lint(code, filename, basic_error_rules, fs)
# Perform lint and compare the error message with "expected_message".
def assert_lint(self, code, expected_message, file_name='foo.cpp'):
@@ -764,13 +757,44 @@ class CppStyleTest(CppStyleTestBase):
self.assert_language_rules_check('foo.cpp', statement, error_message)
self.assert_language_rules_check('foo.h', statement, error_message)
- # Test for static_cast readability.
- def test_static_cast_readability(self):
- self.assert_lint(
- 'Text* x = static_cast<Text*>(foo);',
- 'Consider using toText helper function in WebCore/dom/Text.h '
- 'instead of static_cast<Text*>'
- ' [readability/check] [4]')
+ # Tests for static_cast readability.
+ def test_static_cast_on_objects_with_toFoo(self):
+ mock_header_contents = ['inline Foo* toFoo(Bar* bar)']
+ fs = FileSystem()
+ orig_read_text_file_fn = fs.read_text_file
+
+ def mock_read_text_file_fn(path):
+ return mock_header_contents
+
+ try:
+ fs.read_text_file = mock_read_text_file_fn
+ message = self.perform_avoid_static_cast_of_objects(
+ 'Foo* x = static_cast<Foo*>(bar);',
+ filename='casting.cpp',
+ fs=fs)
+ self.assertEqual(message, 'static_cast of class objects is not allowed. Use toFoo defined in Foo.h.'
+ ' [runtime/casting] [4]')
+ finally:
+ fs.read_text_file = orig_read_text_file_fn
+
+ def test_static_cast_on_objects_without_toFoo(self):
+ mock_header_contents = ['inline FooBar* toFooBar(Bar* bar)']
+ fs = FileSystem()
+ orig_read_text_file_fn = fs.read_text_file
+
+ def mock_read_text_file_fn(path):
+ return mock_header_contents
+
+ try:
+ fs.read_text_file = mock_read_text_file_fn
+ message = self.perform_avoid_static_cast_of_objects(
+ 'Foo* x = static_cast<Foo*>(bar);',
+ filename='casting.cpp',
+ fs=fs)
+ self.assertEqual(message, 'static_cast of class objects is not allowed. Add toFoo in Foo.h and use it instead.'
+ ' [runtime/casting] [4]')
+ finally:
+ fs.read_text_file = orig_read_text_file_fn
# We cannot test this functionality because of difference of
# function definitions. Anyway, we may never enable this.
@@ -997,43 +1021,53 @@ class CppStyleTest(CppStyleTestBase):
# 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.assertEqual(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.assertEqual(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.assertEqual(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%sa.h"
-
- std::set<int> foo;''' % (os.path.basename(os.getcwd()), os.path.sep),
- filename='a.cpp',
- io=MockIo(mock_header_contents))
- self.assertEqual(message, 'Add #include <set> for set<> '
- '[build/include_what_you_use] [4]')
+ fs = FileSystem()
+ orig_read_text_file_fn = fs.read_text_file
+
+ def mock_read_text_file_fn(path):
+ return mock_header_contents
+
+ try:
+ fs.read_text_file = mock_read_text_file_fn
+ message = self.perform_include_what_you_use(
+ '#include "config.h"\n'
+ '#include "blah/a.h"\n',
+ filename='blah/a.cpp',
+ fs=fs)
+ self.assertEqual(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',
+ fs=fs)
+ self.assertEqual(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.assertEqual(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%sa.h"
+
+ std::set<int> foo;''' % (os.path.basename(os.getcwd()), os.path.sep),
+ filename='a.cpp',
+ fs=fs)
+ self.assertEqual(message, 'Add #include <set> for set<> '
+ '[build/include_what_you_use] [4]')
+ finally:
+ fs.read_text_file = orig_read_text_file_fn
def test_files_belong_to_same_module(self):
f = cpp_style.files_belong_to_same_module
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/python.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/python.py
index 2aabb4ddcfb..f09638ca6a2 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/python.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/style/checkers/python.py
@@ -75,7 +75,9 @@ class PythonChecker(object):
wkf = WebKitFinder(FileSystem())
executive = Executive()
env = os.environ.copy()
- env['PYTHONPATH'] = ('%s%s%s' % (wkf.path_from_webkit_base('Tools', 'Scripts'),
+ env['PYTHONPATH'] = ('%s%s%s%s%s' % (wkf.path_from_webkit_base('Tools', 'Scripts'),
+ os.pathsep,
+ wkf.path_from_webkit_base('Source', 'build', 'scripts'),
os.pathsep,
wkf.path_from_webkit_base('Tools', 'Scripts', 'webkitpy', 'thirdparty')))
return executive.run_command([sys.executable, wkf.path_from_depot_tools_base('pylint.py'),
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py
index 6ba1544071b..bc493c8d92a 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py
@@ -33,6 +33,7 @@ from mod_pywebsocket import util
from mod_pywebsocket.http_header_util import quote_if_necessary
+# The list of available server side extension processor classes.
_available_processors = {}
_compression_extension_names = []
@@ -40,6 +41,8 @@ _compression_extension_names = []
class ExtensionProcessorInterface(object):
def __init__(self, request):
+ self._logger = util.get_class_logger(self)
+
self._request = request
self._active = True
@@ -62,12 +65,14 @@ class ExtensionProcessorInterface(object):
return None
def get_extension_response(self):
- if self._active:
- response = self._get_extension_response_internal()
- if response is None:
- self._active = False
- return response
- return None
+ if not self._active:
+ self._logger.debug('Extension %s is deactivated', self.name())
+ return None
+
+ response = self._get_extension_response_internal()
+ if response is None:
+ self._active = False
+ return response
def _setup_stream_options_internal(self, stream_options):
pass
@@ -450,10 +455,10 @@ class PerMessageDeflateExtensionProcessor(ExtensionProcessorInterface):
http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-08
"""
- _S2C_MAX_WINDOW_BITS_PARAM = 's2c_max_window_bits'
- _S2C_NO_CONTEXT_TAKEOVER_PARAM = 's2c_no_context_takeover'
- _C2S_MAX_WINDOW_BITS_PARAM = 'c2s_max_window_bits'
- _C2S_NO_CONTEXT_TAKEOVER_PARAM = 'c2s_no_context_takeover'
+ _SERVER_MAX_WINDOW_BITS_PARAM = 'server_max_window_bits'
+ _SERVER_NO_CONTEXT_TAKEOVER_PARAM = 'server_no_context_takeover'
+ _CLIENT_MAX_WINDOW_BITS_PARAM = 'client_max_window_bits'
+ _CLIENT_NO_CONTEXT_TAKEOVER_PARAM = 'client_no_context_takeover'
def __init__(self, request, draft08=True):
"""Construct PerMessageDeflateExtensionProcessor
@@ -468,8 +473,8 @@ class PerMessageDeflateExtensionProcessor(ExtensionProcessorInterface):
ExtensionProcessorInterface.__init__(self, request)
self._logger = util.get_class_logger(self)
- self._preferred_c2s_max_window_bits = None
- self._c2s_no_context_takeover = False
+ self._preferred_client_max_window_bits = None
+ self._client_no_context_takeover = False
self._draft08 = draft08
@@ -479,129 +484,133 @@ class PerMessageDeflateExtensionProcessor(ExtensionProcessorInterface):
def _get_extension_response_internal(self):
if self._draft08:
for name in self._request.get_parameter_names():
- if name not in [self._S2C_MAX_WINDOW_BITS_PARAM,
- self._S2C_NO_CONTEXT_TAKEOVER_PARAM,
- self._C2S_MAX_WINDOW_BITS_PARAM]:
+ if name not in [self._SERVER_MAX_WINDOW_BITS_PARAM,
+ self._SERVER_NO_CONTEXT_TAKEOVER_PARAM,
+ self._CLIENT_MAX_WINDOW_BITS_PARAM]:
self._logger.debug('Unknown parameter: %r', name)
return None
else:
# Any unknown parameter will be just ignored.
pass
- s2c_max_window_bits = None
- if self._request.has_parameter(self._S2C_MAX_WINDOW_BITS_PARAM):
- s2c_max_window_bits = self._request.get_parameter_value(
- self._S2C_MAX_WINDOW_BITS_PARAM)
+ server_max_window_bits = None
+ if self._request.has_parameter(self._SERVER_MAX_WINDOW_BITS_PARAM):
+ server_max_window_bits = self._request.get_parameter_value(
+ self._SERVER_MAX_WINDOW_BITS_PARAM)
try:
- s2c_max_window_bits = _parse_window_bits(s2c_max_window_bits)
+ server_max_window_bits = _parse_window_bits(
+ server_max_window_bits)
except ValueError, e:
self._logger.debug('Bad %s parameter: %r',
- self._S2C_MAX_WINDOW_BITS_PARAM,
+ self._SERVER_MAX_WINDOW_BITS_PARAM,
e)
return None
- s2c_no_context_takeover = self._request.has_parameter(
- self._S2C_NO_CONTEXT_TAKEOVER_PARAM)
- if (s2c_no_context_takeover and
+ server_no_context_takeover = self._request.has_parameter(
+ self._SERVER_NO_CONTEXT_TAKEOVER_PARAM)
+ if (server_no_context_takeover and
self._request.get_parameter_value(
- self._S2C_NO_CONTEXT_TAKEOVER_PARAM) is not None):
+ self._SERVER_NO_CONTEXT_TAKEOVER_PARAM) is not None):
self._logger.debug('%s parameter must not have a value: %r',
- self._S2C_NO_CONTEXT_TAKEOVER_PARAM,
- s2c_no_context_takeover)
+ self._SERVER_NO_CONTEXT_TAKEOVER_PARAM,
+ server_no_context_takeover)
return None
- # c2s_max_window_bits from a client indicates whether the client can
- # accept c2s_max_window_bits from a server or not.
- client_c2s_max_window_bits = self._request.has_parameter(
- self._C2S_MAX_WINDOW_BITS_PARAM)
+ # client_max_window_bits from a client indicates whether the client can
+ # accept client_max_window_bits from a server or not.
+ client_client_max_window_bits = self._request.has_parameter(
+ self._CLIENT_MAX_WINDOW_BITS_PARAM)
if (self._draft08 and
- client_c2s_max_window_bits and
+ client_client_max_window_bits and
self._request.get_parameter_value(
- self._C2S_MAX_WINDOW_BITS_PARAM) is not None):
+ self._CLIENT_MAX_WINDOW_BITS_PARAM) is not None):
self._logger.debug('%s parameter must not have a value in a '
'client\'s opening handshake: %r',
- self._C2S_MAX_WINDOW_BITS_PARAM,
- client_c2s_max_window_bits)
+ self._CLIENT_MAX_WINDOW_BITS_PARAM,
+ client_client_max_window_bits)
return None
self._rfc1979_deflater = util._RFC1979Deflater(
- s2c_max_window_bits, s2c_no_context_takeover)
+ server_max_window_bits, server_no_context_takeover)
# Note that we prepare for incoming messages compressed with window
- # bits upto 15 regardless of the c2s_max_window_bits value to be sent
- # to the client.
+ # bits upto 15 regardless of the client_max_window_bits value to be
+ # sent to the client.
self._rfc1979_inflater = util._RFC1979Inflater()
self._framer = _PerMessageDeflateFramer(
- s2c_max_window_bits, s2c_no_context_takeover)
+ server_max_window_bits, server_no_context_takeover)
self._framer.set_bfinal(False)
self._framer.set_compress_outgoing_enabled(True)
response = common.ExtensionParameter(self._request.name())
- if s2c_max_window_bits is not None:
+ if server_max_window_bits is not None:
response.add_parameter(
- self._S2C_MAX_WINDOW_BITS_PARAM, str(s2c_max_window_bits))
+ self._SERVER_MAX_WINDOW_BITS_PARAM,
+ str(server_max_window_bits))
- if s2c_no_context_takeover:
+ if server_no_context_takeover:
response.add_parameter(
- self._S2C_NO_CONTEXT_TAKEOVER_PARAM, None)
+ self._SERVER_NO_CONTEXT_TAKEOVER_PARAM, None)
- if self._preferred_c2s_max_window_bits is not None:
- if self._draft08 and not client_c2s_max_window_bits:
+ if self._preferred_client_max_window_bits is not None:
+ if self._draft08 and not client_client_max_window_bits:
self._logger.debug('Processor is configured to use %s but '
'the client cannot accept it',
- self._C2S_MAX_WINDOW_BITS_PARAM)
+ self._CLIENT_MAX_WINDOW_BITS_PARAM)
return None
response.add_parameter(
- self._C2S_MAX_WINDOW_BITS_PARAM,
- str(self._preferred_c2s_max_window_bits))
+ self._CLIENT_MAX_WINDOW_BITS_PARAM,
+ str(self._preferred_client_max_window_bits))
- if self._c2s_no_context_takeover:
+ if self._client_no_context_takeover:
response.add_parameter(
- self._C2S_NO_CONTEXT_TAKEOVER_PARAM, None)
+ self._CLIENT_NO_CONTEXT_TAKEOVER_PARAM, None)
self._logger.debug(
'Enable %s extension ('
- 'request: s2c_max_window_bits=%s; s2c_no_context_takeover=%r, '
- 'response: c2s_max_window_bits=%s; c2s_no_context_takeover=%r)' %
+ 'request: server_max_window_bits=%s; '
+ 'server_no_context_takeover=%r, '
+ 'response: client_max_window_bits=%s; '
+ 'client_no_context_takeover=%r)' %
(self._request.name(),
- s2c_max_window_bits,
- s2c_no_context_takeover,
- self._preferred_c2s_max_window_bits,
- self._c2s_no_context_takeover))
+ server_max_window_bits,
+ server_no_context_takeover,
+ self._preferred_client_max_window_bits,
+ self._client_no_context_takeover))
return response
def _setup_stream_options_internal(self, stream_options):
self._framer.setup_stream_options(stream_options)
- def set_c2s_max_window_bits(self, value):
- """If this option is specified, this class adds the c2s_max_window_bits
- extension parameter to the handshake response, but doesn't reduce the
- LZ77 sliding window size of its inflater. I.e., you can use this for
- testing client implementation but cannot reduce memory usage of this
- class.
+ def set_client_max_window_bits(self, value):
+ """If this option is specified, this class adds the
+ client_max_window_bits extension parameter to the handshake response,
+ but doesn't reduce the LZ77 sliding window size of its inflater.
+ I.e., you can use this for testing client implementation but cannot
+ reduce memory usage of this class.
If this method has been called with True and an offer without the
- c2s_max_window_bits extension parameter is received,
+ client_max_window_bits extension parameter is received,
- (When processing the permessage-deflate extension) this processor
declines the request.
- (When processing the permessage-compress extension) this processor
accepts the request.
"""
- self._preferred_c2s_max_window_bits = value
+ self._preferred_client_max_window_bits = value
- def set_c2s_no_context_takeover(self, value):
+ def set_client_no_context_takeover(self, value):
"""If this option is specified, this class adds the
- c2s_no_context_takeover extension parameter to the handshake response,
- but doesn't reset inflater for each message. I.e., you can use this for
- testing client implementation but cannot reduce memory usage of this
- class.
+ client_no_context_takeover extension parameter to the handshake
+ response, but doesn't reset inflater for each message. I.e., you can
+ use this for testing client implementation but cannot reduce memory
+ usage of this class.
"""
- self._c2s_no_context_takeover = value
+ self._client_no_context_takeover = value
def set_bfinal(self, value):
self._framer.set_bfinal(value)
@@ -886,6 +895,11 @@ _available_processors[common.MUX_EXTENSION] = MuxExtensionProcessor
def get_extension_processor(extension_request):
+ """Given an ExtensionParameter representing an extension offer received
+ from a client, configures and returns an instance of the corresponding
+ extension processor class.
+ """
+
processor_class = _available_processors.get(extension_request.name())
if processor_class is None:
return None
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/standalone.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/standalone.py
index 2bf3b0c286f..d270bf09347 100755
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/standalone.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/standalone.py
@@ -583,8 +583,9 @@ class WebSocketServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
self._logger.debug('%r', e)
raise socket.error(1, '%r' % e)
cert = accepted_socket.get_peer_certificate()
- self._logger.debug('Client cert subject: %r',
- cert.get_subject().get_components())
+ if cert is not None:
+ self._logger.debug('Client cert subject: %r',
+ cert.get_subject().get_components())
accepted_socket = _StandaloneSSLConnection(accepted_socket)
else:
raise ValueError('No TLS support module is available')
@@ -661,6 +662,99 @@ class WebSocketRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
CGIHTTPServer.CGIHTTPRequestHandler.__init__(
self, request, client_address, server)
+ def _xhr_send_benchmark_helper(self):
+ content_length = int(self.headers.getheader('Content-Length'))
+
+ self._logger.debug('Requested to receive %s bytes', content_length)
+
+ RECEIVE_BLOCK_SIZE = 1024 * 1024
+
+ bytes_to_receive = content_length
+ while bytes_to_receive > 0:
+ bytes_to_receive_in_this_loop = bytes_to_receive
+ if bytes_to_receive_in_this_loop > RECEIVE_BLOCK_SIZE:
+ bytes_to_receive_in_this_loop = RECEIVE_BLOCK_SIZE
+ received_data = self.rfile.read(bytes_to_receive_in_this_loop)
+ for c in received_data:
+ if c != 'a':
+ self._logger.debug('Request body verification failed')
+ return
+ bytes_to_receive -= len(received_data)
+ if bytes_to_receive < 0:
+ self._logger.debug('Received %d more bytes than expected' %
+ (-bytes_to_receive))
+ return
+
+ # Return the number of received bytes back to the client.
+ response_body = '%d' % content_length
+ self.wfile.write(
+ 'HTTP/1.1 200 OK\r\n'
+ 'Content-Type: text/html\r\n'
+ 'Content-Length: %d\r\n'
+ '\r\n%s' % (len(response_body), response_body))
+ self.wfile.flush()
+
+ def _xhr_receive_benchmark_helper(self):
+ content_length = self.headers.getheader('Content-Length')
+ request_body = self.rfile.read(int(content_length))
+
+ request_array = request_body.split(' ')
+ if len(request_array) < 2:
+ self._logger.debug('Malformed request body: %r', request_body)
+ return
+
+ # Parse the size parameter.
+ bytes_to_send = request_array[0]
+ try:
+ bytes_to_send = int(bytes_to_send)
+ except ValueError, e:
+ self._logger.debug('Malformed size parameter: %r', bytes_to_send)
+ return
+ self._logger.debug('Requested to send %s bytes', bytes_to_send)
+
+ # Parse the transfer encoding parameter.
+ chunked_mode = False
+ mode_parameter = request_array[1]
+ if mode_parameter == 'chunked':
+ self._logger.debug('Requested chunked transfer encoding')
+ chunked_mode = True
+ elif mode_parameter != 'none':
+ self._logger.debug('Invalid mode parameter: %r', mode_parameter)
+ return
+
+ # Write a header
+ response_header = (
+ 'HTTP/1.1 200 OK\r\n'
+ 'Content-Type: application/octet-stream\r\n')
+ if chunked_mode:
+ response_header += 'Transfer-Encoding: chunked\r\n\r\n'
+ else:
+ response_header += (
+ 'Content-Length: %d\r\n\r\n' % bytes_to_send)
+ self.wfile.write(response_header)
+ self.wfile.flush()
+
+ # Write a body
+ SEND_BLOCK_SIZE = 1024 * 1024
+
+ while bytes_to_send > 0:
+ bytes_to_send_in_this_loop = bytes_to_send
+ if bytes_to_send_in_this_loop > SEND_BLOCK_SIZE:
+ bytes_to_send_in_this_loop = SEND_BLOCK_SIZE
+
+ if chunked_mode:
+ self.wfile.write('%x\r\n' % bytes_to_send_in_this_loop)
+ self.wfile.write('a' * bytes_to_send_in_this_loop)
+ if chunked_mode:
+ self.wfile.write('\r\n')
+ self.wfile.flush()
+
+ bytes_to_send -= bytes_to_send_in_this_loop
+
+ if chunked_mode:
+ self.wfile.write('0\r\n\r\n')
+ self.wfile.flush()
+
def parse_request(self):
"""Override BaseHTTPServer.BaseHTTPRequestHandler.parse_request.
@@ -693,6 +787,16 @@ class WebSocketRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
return True
host, port, resource = http_header_util.parse_uri(self.path)
+
+ # Special paths for XMLHttpRequest benchmark
+ xhr_benchmark_helper_prefix = '/073be001e10950692ccbf3a2ad21c245'
+ if resource == (xhr_benchmark_helper_prefix + '_send'):
+ self._xhr_send_benchmark_helper()
+ return False
+ if resource == (xhr_benchmark_helper_prefix + '_receive'):
+ self._xhr_receive_benchmark_helper()
+ return False
+
if resource is None:
self._logger.info('Invalid URI: %r', self.path)
self._logger.info('Fallback to CGIHTTPRequestHandler')
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/bot/commitannouncer.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/bot/commitannouncer.py
index 657a2392339..684da06d003 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/bot/commitannouncer.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/bot/commitannouncer.py
@@ -182,7 +182,10 @@ class CommitAnnouncer(SingleServerIRCBot):
return '%s committed "%s" %s %s%s' % (email, subject, first_url, svn_url, red_flag_message)
def _post(self, message):
- self.connection.execute_delayed(0, lambda: self.connection.privmsg(channel, message))
+ self.connection.execute_delayed(0, lambda: self.connection.privmsg(channel, self._sanitize_string(message)))
+
+ def _sanitize_string(self, message):
+ return message.encode('ascii', 'backslashreplace')
class CommitAnnouncerThread(threading.Thread):
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/bot/commitannouncer_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/bot/commitannouncer_unittest.py
index c6adb0170ec..faf10848a2c 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/bot/commitannouncer_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/bot/commitannouncer_unittest.py
@@ -206,3 +206,8 @@ Review URL: https://codereview.chromium.org/123456
git-svn-id: svn://svn.chromium.org/blink/trunk@456789 bbb929c8-8fbe-4397-9dbb-9b2b20218538
"""))
+
+ def test_sanitize_string(self):
+ bot = CommitAnnouncer(MockTool(), "test_password")
+ self.assertEqual('normal ascii', bot._sanitize_string('normal ascii'))
+ self.assertEqual('uni\\u0441ode!', bot._sanitize_string(u'uni\u0441ode!'))
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/__init__.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/__init__.py
index 39f15361a1b..64720f53202 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/__init__.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/__init__.py
@@ -1,7 +1,6 @@
# Required for Python to search this directory for module files
from webkitpy.tool.commands.commitannouncer import CommitAnnouncerCommand
-from webkitpy.tool.commands.download import *
from webkitpy.tool.commands.flakytests import FlakyTests
from webkitpy.tool.commands.gardenomatic import GardenOMatic
from webkitpy.tool.commands.prettydiff import PrettyDiff
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/commitannouncer.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/commitannouncer.py
index 98c33d0ea32..3beb121d9d8 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/commitannouncer.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/commitannouncer.py
@@ -38,6 +38,7 @@ _log = logging.getLogger(__name__)
class CommitAnnouncerCommand(AbstractDeclarativeCommand):
name = "commit-announcer"
help_text = "Start an IRC bot for announcing new git commits."
+ show_in_main_help = True
def __init__(self):
options = [
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/download.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/download.py
deleted file mode 100644
index f26e09e482d..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/download.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright (c) 2009, 2011 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 logging
-
-from webkitpy.tool import steps
-
-from webkitpy.common.config import urls
-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
-
-_log = logging.getLogger(__name__)
-
-
-class Clean(AbstractSequencedCommand):
- name = "clean"
- help_text = "Clean the working copy"
- steps = [
- steps.DiscardLocalChanges,
- ]
-
- def _prepare_state(self, options, args, tool):
- options.force_clean = True
-
-
-class CheckStyleLocal(AbstractSequencedCommand):
- name = "check-style-local"
- help_text = "Run check-webkit-style on the current working directory diff"
- steps = [
- steps.CheckStyle,
- ]
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/download_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/download_unittest.py
deleted file mode 100644
index ce2c7f33a0f..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/download_unittest.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright (C) 2009, 2011 Google Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (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.thirdparty.unittest2 as 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
-from webkitpy.common.checkout.checkout_mock import MockCheckout
-
-
-class DownloadCommandsTest(CommandsTest):
- maxDiff = None
-
- def _default_options(self):
- options = MockOptions()
- options.build = True
- options.build_style = True
- options.check_style = True
- options.check_style_filter = None
- options.clean = True
- options.close_bug = True
- options.force_clean = False
- options.non_interactive = False
- options.parent_command = 'MOCK parent command'
- options.quiet = False
- options.test = True
- options.update = True
- return options
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/flakytests.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/flakytests.py
index cdd98b81124..3a97cd61cd7 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/flakytests.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/flakytests.py
@@ -34,6 +34,7 @@ from webkitpy.layout_tests.models.test_expectations import TestExpectationParser
class FlakyTests(AbstractDeclarativeCommand):
name = "flaky-tests"
help_text = "Generate FlakyTests file from the flakiness dashboard"
+ show_in_main_help = True
def execute(self, options, args, tool):
port = tool.port_factory.get()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/gardenomatic.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/gardenomatic.py
index e1ebe9b88dd..0a3a703db0e 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/gardenomatic.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/gardenomatic.py
@@ -30,6 +30,7 @@ from webkitpy.tool.servers.gardeningserver import GardeningHTTPServer
class GardenOMatic(AbstractRebaseliningCommand):
name = "garden-o-matic"
help_text = "Command for gardening the WebKit tree."
+ show_in_main_help = True
def __init__(self):
super(GardenOMatic, self).__init__(options=(self.platform_options + [
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queries.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queries.py
index 0c3d8cd2e89..7d2f3f56352 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queries.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queries.py
@@ -45,6 +45,7 @@ _log = logging.getLogger(__name__)
class CrashLog(AbstractDeclarativeCommand):
name = "crash-log"
help_text = "Print the newest crash log for the given process"
+ show_in_main_help = True
long_help = """Finds the newest crash log matching the given process name
and PID and prints it to stdout."""
argument_names = "PROCESS_NAME [PID]"
@@ -60,6 +61,7 @@ and PID and prints it to stdout."""
class PrintExpectations(AbstractDeclarativeCommand):
name = 'print-expectations'
help_text = 'Print the expected result for the given test(s) on the given port(s)'
+ show_in_main_help = True
def __init__(self):
options = [
@@ -151,6 +153,7 @@ class PrintExpectations(AbstractDeclarativeCommand):
class PrintBaselines(AbstractDeclarativeCommand):
name = 'print-baselines'
help_text = 'Prints the baseline locations for given test(s) on the given port(s)'
+ show_in_main_help = True
def __init__(self):
options = [
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queuestest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queuestest.py
deleted file mode 100644
index 9812eb70723..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/queuestest.py
+++ /dev/null
@@ -1,107 +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.thirdparty.unittest2 as 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, seconds_to_sleep):
- pass
-
- def run(self):
- pass
-
-
-class QueuesTest(unittest.TestCase):
- # This is _patch1 in mocktool.py
- mock_work_item = MockTool().bugs.fetch_attachment(10000)
-
- def assert_outputs(self, func, func_name, args, expected_stdout, expected_stderr, expected_exceptions, expected_logs):
- exception = None
- if expected_exceptions and func_name in expected_exceptions:
- exception = expected_exceptions[func_name]
-
- logs = None
- if expected_logs and func_name in expected_logs:
- logs = expected_logs[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,
- expected_logs=logs)
-
- def _default_begin_work_queue_stderr(self, name):
- string_replacements = {"name": name}
- return "MOCK: update_status: %(name)s Starting Queue\n" % string_replacements
-
- def _default_begin_work_queue_logs(self, name):
- checkout_dir = '/mock-checkout'
- 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, expected_logs=None, options=None, tool=None):
- if not tool:
- tool = MockTool()
- # This is a hack to make it easy for callers to not have to setup a custom MockFileSystem just to test the commit-queue
- # the cq tries to read the layout test results, and will hit a KeyError in MockFileSystem if we don't do this.
- tool.filesystem.write_text_file('/mock-results/full_results.json', "")
- 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, expected_logs)
- self.assert_outputs(queue.work_item_log_path, "work_item_log_path", [work_item], expected_stdout, expected_stderr, expected_exceptions, expected_logs)
- self.assert_outputs(queue.begin_work_queue, "begin_work_queue", [], expected_stdout, expected_stderr, expected_exceptions, expected_logs)
- self.assert_outputs(queue.should_continue_work_queue, "should_continue_work_queue", [], expected_stdout, expected_stderr, expected_exceptions, expected_logs)
- self.assert_outputs(queue.next_work_item, "next_work_item", [], expected_stdout, expected_stderr, expected_exceptions, expected_logs)
- self.assert_outputs(queue.process_work_item, "process_work_item", [work_item], expected_stdout, expected_stderr, expected_exceptions, expected_logs)
- self.assert_outputs(queue.handle_unexpected_error, "handle_unexpected_error", [work_item, "Mock error message"], expected_stdout, expected_stderr, expected_exceptions, expected_logs)
- # 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", output="MOCK output")], expected_stdout, expected_stderr, expected_exceptions, expected_logs)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py
index a4a952b6e60..70dfd28d13c 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py
@@ -227,6 +227,7 @@ class RebaselineTest(BaseInternalRebaselineCommand):
class OptimizeBaselines(AbstractRebaseliningCommand):
name = "optimize-baselines"
help_text = "Reshuffles the baselines for the given tests to use as litte space on disk as possible."
+ show_in_main_help = True
argument_names = "TEST_NAMES"
def __init__(self):
@@ -255,6 +256,7 @@ class OptimizeBaselines(AbstractRebaseliningCommand):
class AnalyzeBaselines(AbstractRebaseliningCommand):
name = "analyze-baselines"
help_text = "Analyzes the baselines for the given tests and prints results that are identical."
+ show_in_main_help = True
argument_names = "TEST_NAMES"
def __init__(self):
@@ -492,6 +494,7 @@ class RebaselineJson(AbstractParallelRebaselineCommand):
class RebaselineExpectations(AbstractParallelRebaselineCommand):
name = "rebaseline-expectations"
help_text = "Rebaselines the tests indicated in TestExpectations."
+ show_in_main_help = True
def __init__(self):
super(RebaselineExpectations, self).__init__(options=[
@@ -539,6 +542,7 @@ class RebaselineExpectations(AbstractParallelRebaselineCommand):
class Rebaseline(AbstractParallelRebaselineCommand):
name = "rebaseline"
help_text = "Rebaseline tests with results from the build bots. Shows the list of failing tests on the builders if no test names are provided."
+ show_in_main_help = True
argument_names = "[TEST_NAMES]"
def __init__(self):
@@ -677,14 +681,19 @@ class AutoRebaseline(AbstractParallelRebaselineCommand):
has_any_needs_rebaseline_lines = False
for line in tool.scm().blame(expectations_file_path).split("\n"):
- if "NeedsRebaseline" not in line:
+ comment_index = line.find("#")
+ if comment_index == -1:
+ comment_index = len(line)
+ line_without_comments = re.sub(r"\s+", " ", line[:comment_index].strip())
+
+ if "NeedsRebaseline" not in line_without_comments:
continue
if not has_any_needs_rebaseline_lines:
self._start_new_log_entry(log_server)
has_any_needs_rebaseline_lines = True
- parsed_line = re.match("^(\S*)[^(]*\((\S*).*?([^ ]*)\ \[[^[]*$", line)
+ parsed_line = re.match("^(\S*)[^(]*\((\S*).*?([^ ]*)\ \[[^[]*$", line_without_comments)
commit_hash = parsed_line.group(1)
svn_revision = tool.scm().svn_revision_from_git_commit(commit_hash)
@@ -703,7 +712,7 @@ class AutoRebaseline(AbstractParallelRebaselineCommand):
revision = svn_revision
author = parsed_line.group(2)
- bugs.update(re.findall("crbug\.com\/(\d+)", line))
+ bugs.update(re.findall("crbug\.com\/(\d+)", line_without_comments))
tests.add(test)
if len(tests) >= self.MAX_LINES_TO_REBASELINE:
@@ -841,6 +850,7 @@ class AutoRebaseline(AbstractParallelRebaselineCommand):
class RebaselineOMatic(AbstractDeclarativeCommand):
name = "rebaseline-o-matic"
help_text = "Calls webkit-patch auto-rebaseline in a loop."
+ show_in_main_help = True
SLEEP_TIME_IN_SECONDS = 30
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
index cf8aa8a4c3b..957ce59cd61 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py
@@ -224,8 +224,8 @@ class TestRebaselineTest(_BaseTestCase):
def test_baseline_directory(self):
command = self.command
- self.assertMultiLineEqual(command._baseline_directory("WebKit Mac10.7"), "/mock-checkout/LayoutTests/platform/mac-lion")
- self.assertMultiLineEqual(command._baseline_directory("WebKit Mac10.6"), "/mock-checkout/LayoutTests/platform/mac-snowleopard")
+ self.assertMultiLineEqual(command._baseline_directory("WebKit Mac10.7"), "/mock-checkout/third_party/WebKit/LayoutTests/platform/mac-lion")
+ self.assertMultiLineEqual(command._baseline_directory("WebKit Mac10.6"), "/mock-checkout/third_party/WebKit/LayoutTests/platform/mac-snowleopard")
def test_rebaseline_updates_expectations_file_noop(self):
self._zero_out_test_expectations()
@@ -273,7 +273,7 @@ Bug(A) [ Debug ] : fast/css/large-list-of-rules-crash.html [ Failure ]
self.command._rebaseline_test("WebKit Linux", "userscripts/another-test.html", "txt", None)
- self.assertDictEqual(self.command._scm_changes, {'add': ['/mock-checkout/LayoutTests/platform/linux/userscripts/another-test-expected.txt'], 'delete': []})
+ self.assertDictEqual(self.command._scm_changes, {'add': ['/mock-checkout/third_party/WebKit/LayoutTests/platform/linux/userscripts/another-test-expected.txt'], 'delete': []})
def test_rebaseline_test_internal_with_port_that_lacks_buildbot(self):
self.tool.executive = MockExecutive2()
@@ -443,7 +443,7 @@ class TestRebaselineJsonUpdatesExpectationsFiles(_BaseTestCase):
self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}})
new_expectations = self._read(self.lion_expectations_path)
- self.assertMultiLineEqual(new_expectations, "Bug(x) [ MountainLion Retina SnowLeopard ] userscripts/first-test.html [ ImageOnlyFailure ]\nbug(z) [ Linux ] userscripts/first-test.html [ ImageOnlyFailure ]\n")
+ self.assertMultiLineEqual(new_expectations, "Bug(x) [ Mavericks MountainLion Retina SnowLeopard ] userscripts/first-test.html [ ImageOnlyFailure ]\nbug(z) [ Linux ] userscripts/first-test.html [ ImageOnlyFailure ]\n")
def test_rebaseline_updates_expectations_file_all_platforms(self):
options = MockOptions(optimize=False, verbose=True, results_directory=None)
@@ -455,7 +455,7 @@ class TestRebaselineJsonUpdatesExpectationsFiles(_BaseTestCase):
self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}})
new_expectations = self._read(self.lion_expectations_path)
- self.assertMultiLineEqual(new_expectations, "Bug(x) [ Android Linux MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n")
+ self.assertMultiLineEqual(new_expectations, "Bug(x) [ Android Linux Mavericks MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n")
def test_rebaseline_handles_platform_skips(self):
# This test is just like test_rebaseline_updates_expectations_file_all_platforms(),
@@ -471,7 +471,7 @@ class TestRebaselineJsonUpdatesExpectationsFiles(_BaseTestCase):
self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}})
new_expectations = self._read(self.lion_expectations_path)
- self.assertMultiLineEqual(new_expectations, "Bug(x) [ Linux MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n")
+ self.assertMultiLineEqual(new_expectations, "Bug(x) [ Linux Mavericks MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n")
def test_rebaseline_handles_skips_in_file(self):
# This test is like test_Rebaseline_handles_platform_skips, except that the
@@ -491,7 +491,7 @@ class TestRebaselineJsonUpdatesExpectationsFiles(_BaseTestCase):
new_expectations = self._read(self.lion_expectations_path)
self.assertMultiLineEqual(new_expectations,
- ("Bug(x) [ Linux MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n"
+ ("Bug(x) [ Linux Mavericks MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n"
"Bug(y) [ Android ] userscripts/first-test.html [ Skip ]\n"))
def test_rebaseline_handles_smoke_tests(self):
@@ -509,7 +509,7 @@ class TestRebaselineJsonUpdatesExpectationsFiles(_BaseTestCase):
self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}})
new_expectations = self._read(self.lion_expectations_path)
- self.assertMultiLineEqual(new_expectations, "Bug(x) [ Linux MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n")
+ self.assertMultiLineEqual(new_expectations, "Bug(x) [ Linux Mavericks MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n")
@@ -784,6 +784,7 @@ TBR=foo@chromium.org
def test_execute(self):
def blame(path):
return """
+6469e754a1 path/to/TestExpectations (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000 11) # Test NeedsRebaseline being in a comment doesn't bork parsing.
6469e754a1 path/to/TestExpectations (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000 11) crbug.com/24182 [ Debug ] path/to/norebaseline.html [ ImageOnlyFailure ]
6469e754a1 path/to/TestExpectations (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000 13) Bug(foo) fast/dom/prototype-taco.html [ NeedsRebaseline ]
6469e754a1 path/to/TestExpectations (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000 11) crbug.com/24182 [ SnowLeopard ] fast/dom/prototype-strawberry.html [ NeedsRebaseline ]
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaselineserver.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaselineserver.py
index ffc03b858dd..2448f872400 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaselineserver.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaselineserver.py
@@ -50,6 +50,7 @@ class TestConfig(object):
class RebaselineServer(AbstractLocalServerCommand):
name = "rebaseline-server"
help_text = __doc__
+ show_in_main_help = True
argument_names = "/path/to/results/directory"
server = RebaselineHTTPServer
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/stepsequence.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/stepsequence.py
index 894771276e1..c48c2c4d493 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/stepsequence.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/stepsequence.py
@@ -31,7 +31,6 @@ import sys
from webkitpy.tool import steps
-from webkitpy.common.checkout.scm import CheckoutNeedsUpdate
from webkitpy.common.system.executive import ScriptError
_log = logging.getLogger(__name__)
@@ -79,12 +78,6 @@ class StepSequence(object):
state = {}
try:
self._run(tool, options, state)
- except CheckoutNeedsUpdate, e:
- _log.info("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)
- self.exit_after_handled_error(e)
except ScriptError, e:
if not options.quiet:
_log.error(e.message_with_output())
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/main.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/main.py
index 96eaf946ac3..dcd236eed12 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/main.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/main.py
@@ -33,7 +33,6 @@ from optparse import make_option
import os
import threading
-from webkitpy.common.config.ports import DeprecatedPort
from webkitpy.common.host import Host
from webkitpy.tool.multicommandtool import MultiCommandTool
from webkitpy.tool import commands
@@ -43,8 +42,6 @@ class WebKitPatch(MultiCommandTool, Host):
global_options = [
make_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="enable all logging"),
make_option("-d", "--directory", action="append", dest="patch_directories", default=[], help="Directory to look at for changed files"),
- make_option("--seconds-to-sleep", action="store", default=120, type="int", help="Number of seconds to sleep in the task queue."),
- make_option("--port", action="store", dest="port", default=None, help="Specify a port (e.g., mac, qt, gtk, ...)."),
]
def __init__(self, path):
@@ -52,10 +49,6 @@ class WebKitPatch(MultiCommandTool, Host):
Host.__init__(self)
self._path = path
self.wakeup_event = threading.Event()
- self._deprecated_port = None
-
- def deprecated_port(self):
- return self._deprecated_port
def path(self):
return self._path
@@ -70,8 +63,6 @@ class WebKitPatch(MultiCommandTool, Host):
# FIXME: This may be unnecessary since we pass global options to all commands during execute() as well.
def handle_global_options(self, options):
self.initialize_scm(options.patch_directories)
- # If options.port is None, we'll get the default port for this platform.
- self._deprecated_port = DeprecatedPort.port(options.port)
def should_execute_command(self, command):
if command.requires_local_commits and not self.scm().supports_local_commits():
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/servers/rebaselineserver_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/servers/rebaselineserver_unittest.py
index ffeef175105..defb9cd15b8 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/servers/rebaselineserver_unittest.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/servers/rebaselineserver_unittest.py
@@ -290,10 +290,11 @@ class GetBaselinesTest(unittest.TestCase):
def get_test_config(test_files=[], result_files=[]):
- # We could grab this from port.layout_tests_dir(), but instantiating a fully mocked port is a pain.
- layout_tests_directory = "/mock-checkout/LayoutTests"
- results_directory = '/WebKitBuild/Debug/layout-test-results'
host = MockHost()
+ port = host.port_factory.get()
+ layout_tests_directory = port.layout_tests_dir()
+ results_directory = port.results_directory()
+
for file in test_files:
host.filesystem.write_binary_file(host.filesystem.join(layout_tests_directory, file), '')
for file in result_files:
@@ -301,6 +302,7 @@ def get_test_config(test_files=[], result_files=[]):
class TestMacPort(Port):
port_name = "mac"
+ FALLBACK_PATHS = {'': ['mac']}
return TestConfig(
TestMacPort(host, 'mac'),
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/__init__.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/__init__.py
index bc35b8f476c..261606dcd0f 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/__init__.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/__init__.py
@@ -27,19 +27,5 @@
# 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.addsvnmimetypeforpng import AddSvnMimetypeForPng
-from webkitpy.tool.steps.checkstyle import CheckStyle
-from webkitpy.tool.steps.cleanworkingdirectory import CleanWorkingDirectory
-from webkitpy.tool.steps.commit import Commit
from webkitpy.tool.steps.confirmdiff import ConfirmDiff
-from webkitpy.tool.steps.discardlocalchanges import DiscardLocalChanges
-from webkitpy.tool.steps.ensurebugisopenandassigned import EnsureBugIsOpenAndAssigned
-from webkitpy.tool.steps.ensurelocalcommitifneeded import EnsureLocalCommitIfNeeded
-from webkitpy.tool.steps.haslanded import HasLanded
-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.promptforbugortitle import PromptForBugOrTitle
-from webkitpy.tool.steps.reopenbugafterrollout import ReopenBugAfterRollout
-from webkitpy.tool.steps.revertrevision import RevertRevision
-from webkitpy.tool.steps.runtests import RunTests
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/abstractstep.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/abstractstep.py
index ff8a752b889..9d7f2e07373 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/abstractstep.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/abstractstep.py
@@ -40,33 +40,6 @@ class AbstractStep(object):
def _exit(self, code):
sys.exit(code)
- def _changed_files(self, state):
- return self.cached_lookup(state, "changed_files")
-
- _well_known_keys = {
- # FIXME: Should this use state.get('bug_id') or state.get('patch').bug_id() like UpdateChangeLogsWithReviewer does?
- "bug": lambda self, state: self._tool.bugs.fetch_bug(state["bug_id"]),
- # bug_title can either be a new title given by the user, or one from an existing bug.
- "bug_title": lambda self, state: self.cached_lookup(state, 'bug').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)),
- # Absolute path to ChangeLog files.
- "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 [
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/addsvnmimetypeforpng.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/addsvnmimetypeforpng.py
deleted file mode 100644
index 0ef0fed4629..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/addsvnmimetypeforpng.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright (C) 2012 Balazs Ankes (bank@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:
-# 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (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.tool.steps.abstractstep import AbstractStep
-from webkitpy.common import checksvnconfigfile
-from webkitpy.common.checkout.scm.detection import SCMDetector
-from webkitpy.common.system.systemhost import SystemHost
-
-_log = logging.getLogger(__name__)
-
-
-class AddSvnMimetypeForPng(AbstractStep):
- def __init__(self, tool, options, host=None, scm=None):
- self._tool = tool
- self._options = options
- self._host = host or SystemHost()
- self._fs = self._host.filesystem
- self._detector = scm or SCMDetector(self._fs, self._host.executive).detect_scm_system(self._fs.getcwd())
-
- def run(self, state):
- png_files = self._check_pngs(self._changed_files(state))
-
- if png_files:
- detection = self._detector.display_name()
-
- if detection == "git":
- (file_missing, autoprop_missing, png_missing) = checksvnconfigfile.check(self._host, self._fs)
- config_file_path = checksvnconfigfile.config_file_path(self._host, self._fs)
-
- if file_missing:
- _log.info("There is no SVN config file. The svn:mime-type of pngs won't set.")
- if not self._tool.user.confirm("Are you sure you want to continue?", default="n"):
- self._exit(1)
- elif autoprop_missing and png_missing:
- _log.info(checksvnconfigfile.errorstr_autoprop(config_file_path) + checksvnconfigfile.errorstr_png(config_file_path))
- if not self._tool.user.confirm("Do you want to continue?", default="n"):
- self._exit(1)
- elif autoprop_missing:
- _log.info(checksvnconfigfile.errorstr_autoprop(config_file_path))
- if not self._tool.user.confirm("Do you want to continue?", default="n"):
- self._exit(1)
- elif png_missing:
- _log.info(checksvnconfigfile.errorstr_png(config_file_path))
- if not self._tool.user.confirm("Do you want to continue?", default="n"):
- self._exit(1)
-
- elif detection == "svn":
- for filename in png_files:
- if self._detector.exists(filename) and self._detector.propget('svn:mime-type', filename) != 'image/png':
- print "Adding image/png mime-type to %s" % filename
- self._detector.propset('svn:mime-type', 'image/png', filename)
-
- def _check_pngs(self, changed_files):
- png_files = []
- for filename in changed_files:
- if filename.endswith('.png'):
- png_files.append(filename)
- return png_files
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/addsvnmimetypeforpng_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/addsvnmimetypeforpng_unittest.py
deleted file mode 100644
index 991b95b877c..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/addsvnmimetypeforpng_unittest.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright (C) 2012 Balazs Ankes (bank@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:
-# 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (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.thirdparty.unittest2 as unittest
-
-from webkitpy.tool.steps.addsvnmimetypeforpng import AddSvnMimetypeForPng
-from webkitpy.common.system.filesystem_mock import MockFileSystem
-from webkitpy.tool.mocktool import MockOptions, MockTool
-from webkitpy.common.system.systemhost_mock import MockSystemHost
-from webkitpy.common.system.outputcapture import OutputCapture
-
-
-class MockSCMDetector(object):
-
- def __init__(self, scm):
- self._scm = scm
-
- def display_name(self):
- return self._scm
-
-
-class AddSvnMimetypeForPngTest(unittest.TestCase):
- def test_run(self):
- capture = OutputCapture()
- options = MockOptions(git_commit='MOCK git commit')
-
- files = {'/Users/mock/.subversion/config': 'enable-auto-props = yes\n*.png = svn:mime-type=image/png'}
- fs = MockFileSystem(files)
- scm = MockSCMDetector('git')
-
- step = AddSvnMimetypeForPng(MockTool(), options, MockSystemHost(os_name='linux', filesystem=fs), scm)
- state = {
- "changed_files": ["test.png", "test.txt"],
- }
- try:
- capture.assert_outputs(self, step.run, [state])
- except SystemExit, e:
- self.assertEqual(e.code, 1)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/applypatch.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/applypatch.py
deleted file mode 100644
index 6c80b355a97..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/applypatch.py
+++ /dev/null
@@ -1,46 +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
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-
-_log = logging.getLogger(__name__)
-
-
-class ApplyPatch(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.non_interactive,
- ]
-
- def run(self, state):
- _log.info("Processing patch %s from bug %s." % (state["patch"].id(), state["patch"].bug_id()))
- self._tool.checkout().apply_patch(state["patch"])
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/checkstyle.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/checkstyle.py
deleted file mode 100644
index c699ed542f7..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/checkstyle.py
+++ /dev/null
@@ -1,67 +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.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-
-class CheckStyle(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.non_interactive,
- Options.check_style,
- Options.check_style_filter,
- Options.git_commit,
- ]
-
- def run(self, state):
- if not self._options.check_style:
- return
-
- 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))
-
- if self._options.check_style_filter:
- args.append("--filter")
- args.append(self._options.check_style_filter)
-
- try:
- self._tool.executive.run_and_throw_if_fail(self._tool.deprecated_port().check_webkit_style_command() + args, cwd=self._tool.scm().checkout_root)
- 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?"):
- self._exit(1)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py
deleted file mode 100644
index ddf7bed022d..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.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
-from webkitpy.common.system.executive import ScriptError
-
-
-class CleanWorkingDirectory(AbstractStep):
-
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.force_clean,
- Options.clean,
- ]
-
- def run(self, state):
- if not self._options.clean:
- return
-
- if self._tool.scm().has_working_directory_changes() and not self._options.force_clean:
- raise ScriptError("Working directory has changes, pass --force-clean to continue.")
-
- self._tool.scm().discard_working_directory_changes()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory_unittest.py
deleted file mode 100644
index 1b8fd929339..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory_unittest.py
+++ /dev/null
@@ -1,68 +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.thirdparty.unittest2 as unittest
-
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.tool.mocktool import MockOptions, MockTool
-from webkitpy.tool.steps.cleanworkingdirectory import CleanWorkingDirectory
-from webkitpy.common.system.executive import ScriptError
-
-
-class CleanWorkingDirectoryTest(unittest.TestCase):
- def test_run_working_directory_changes_no_force(self):
- tool = MockTool()
- tool._scm = Mock()
- step = CleanWorkingDirectory(tool, MockOptions(clean=True, force_clean=False))
- tool._scm.has_working_directory_changes = lambda: True
- self.assertRaises(ScriptError, step.run, {})
- self.assertEqual(tool._scm.discard_working_directory_changes.call_count, 0)
-
- def test_run_working_directory_changes_force(self):
- tool = MockTool()
- tool._scm = Mock()
- step = CleanWorkingDirectory(tool, MockOptions(clean=True, force_clean=True))
- tool._scm.has_working_directory_changes = lambda: True
- step.run({})
- self.assertEqual(tool._scm.discard_working_directory_changes.call_count, 1)
-
- def test_run_no_local_changes(self):
- tool = MockTool()
- tool._scm = Mock()
- step = CleanWorkingDirectory(tool, MockOptions(clean=True, force_clean=False))
- tool._scm.has_working_directory_changes = lambda: False
- tool._scm.has_local_commits = lambda: False
- step.run({})
- self.assertEqual(tool._scm.discard_working_directory_changes.call_count, 1)
-
- def test_no_clean(self):
- tool = MockTool()
- tool._scm = Mock()
- step = CleanWorkingDirectory(tool, MockOptions(clean=False))
- step.run({})
- self.assertEqual(tool._scm.discard_working_directory_changes.call_count, 0)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/commit.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/commit.py
deleted file mode 100644
index c0e42fd9939..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/commit.py
+++ /dev/null
@@ -1,108 +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 sys
-
-from webkitpy.common.checkout.scm import AuthenticationError, AmbiguousCommitError
-from webkitpy.common.config import urls
-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
-
-_log = logging.getLogger(__name__)
-
-
-class Commit(AbstractStep):
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.non_interactive,
- ]
-
- def _commit_warning(self, error):
- return ('There are %s local commits (and possibly changes in the working directory. '
- '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))
-
- def _check_test_expectations(self, changed_files):
- test_expectations_files = [filename for filename in changed_files if filename.endswith('TestExpectations')]
- if not test_expectations_files:
- return
-
- args = ["--diff-files"]
- args.extend(test_expectations_files)
- try:
- self._tool.executive.run_and_throw_if_fail(self._tool.deprecated_port().check_webkit_style_command() + args, cwd=self._tool.scm().checkout_root)
- except ScriptError, e:
- if self._options.non_interactive:
- raise
- if not self._tool.user.confirm("Are you sure you want to continue?", default="n"):
- self._exit(1)
-
- 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) < 10:
- raise Exception("Attempted to commit with a commit message shorter than 10 characters. Either your patch is missing a ChangeLog or webkit-patch may have a bug.")
-
- self._check_test_expectations(self._changed_files(state))
- self._state = state
-
- username = None
- password = None
- force_squash = self._options.non_interactive
-
- 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, password=password, force_squash=force_squash, changed_files=self._changed_files(state))
- svn_revision = scm.svn_revision_from_commit_text(commit_text)
- _log.info("Committed r%s: <%s>" % (svn_revision, urls.view_revision_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:
- if self._options.non_interactive:
- raise ScriptError(message="Authentication required")
- 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." % e.server_host)
- if e.prompt_for_password:
- password = self._tool.user.prompt_password("%s password for %s: " % (e.server_host, username), repeat=5)
- if not password:
- raise ScriptError("You need to specify the password for %s on %s to perform the commit." % (username, e.server_host))
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/commit_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/commit_unittest.py
deleted file mode 100644
index 567e0bb9e44..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/commit_unittest.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Copyright (C) 2012 Google Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (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.thirdparty.unittest2 as unittest
-
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.common.system.executive import ScriptError
-from webkitpy.common.system.executive_mock import MockExecutive
-from webkitpy.tool.mocktool import MockOptions, MockTool
-from webkitpy.tool.steps.commit import Commit
-
-
-class CommitTest(unittest.TestCase):
- def _test_check_test_expectations(self, filename):
- capture = OutputCapture()
- options = MockOptions()
- options.git_commit = ""
- options.non_interactive = True
-
- tool = MockTool()
- tool.user = None # Will cause any access of tool.user to raise an exception.
- step = Commit(tool, options)
- state = {
- "changed_files": [filename + "XXX"],
- }
-
- tool.executive = MockExecutive(should_log=True, should_throw_when_run=False)
- expected_logs = "Committed r49824: <http://trac.webkit.org/changeset/49824>\n"
- capture.assert_outputs(self, step.run, [state], expected_logs=expected_logs)
-
- state = {
- "changed_files": ["platform/chromium/" + filename],
- }
- expected_logs = """MOCK run_and_throw_if_fail: ['mock-check-webkit-style', '--diff-files', 'platform/chromium/%s'], cwd=/mock-checkout
-Committed r49824: <http://trac.webkit.org/changeset/49824>
-""" % filename
- capture.assert_outputs(self, step.run, [state], expected_logs=expected_logs)
-
- tool.executive = MockExecutive(should_log=True, should_throw_when_run=set(["platform/chromium/" + filename]))
- self.assertRaises(ScriptError, capture.assert_outputs, self, step.run, [state])
-
- def test_check_test_expectations(self):
- self._test_check_test_expectations('TestExpectations')
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/confirmdiff.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/confirmdiff.py
index ba0478b1fe4..6cab6107dba 100644
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/confirmdiff.py
+++ b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/confirmdiff.py
@@ -45,13 +45,13 @@ class ConfirmDiff(AbstractStep):
Options.confirm,
]
- def _show_pretty_diff(self, diff):
+ def _show_pretty_diff(self):
if not self._tool.user.can_open_url():
return None
try:
pretty_patch = PrettyPatch(self._tool.executive)
- pretty_diff_file = pretty_patch.pretty_diff_file(diff)
+ pretty_diff_file = pretty_patch.pretty_diff_file(self.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
@@ -62,13 +62,15 @@ class ConfirmDiff(AbstractStep):
except OSError, e:
_log.warning("PrettyPatch unavailable.")
+ def diff(self):
+ changed_files = self._tool.scm().changed_files(self._options.git_commit)
+ return self._tool.scm().create_patch(self._options.git_commit,
+ changed_files=changed_files)
+
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)
+ pretty_diff_file = self._show_pretty_diff()
diff_correct = self._tool.user.confirm("Was that diff correct?")
if pretty_diff_file:
pretty_diff_file.close()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/discardlocalchanges.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/discardlocalchanges.py
deleted file mode 100644
index 8a84cc7024f..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/discardlocalchanges.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
-from webkitpy.common.system.executive import ScriptError
-
-
-class DiscardLocalChanges(AbstractStep):
-
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.clean,
- Options.force_clean,
- ]
-
- def run(self, state):
- if not self._options.clean:
- return
-
- if not self._options.force_clean:
- if self._tool.scm().has_working_directory_changes():
- raise ScriptError("Working directory has changes, pass --force-clean to continue.")
- if self._tool.scm().has_local_commits():
- raise ScriptError("Repository has local commits, pass --force-clean to continue.")
- self._tool.scm().discard_local_changes()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/discardlocalchanges_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/discardlocalchanges_unittest.py
deleted file mode 100644
index 35d5f24971e..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/discardlocalchanges_unittest.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.
-
-import webkitpy.thirdparty.unittest2 as unittest
-
-from webkitpy.thirdparty.mock import Mock
-from webkitpy.tool.mocktool import MockOptions, MockTool
-from webkitpy.tool.steps.discardlocalchanges import DiscardLocalChanges
-from webkitpy.common.system.executive import ScriptError
-
-
-class DiscardLocalChangesTest(unittest.TestCase):
- def test_skip_on_clean(self):
- tool = MockTool()
- tool._scm = Mock()
- step = DiscardLocalChanges(tool, MockOptions(clean=False))
- step.run({})
- self.assertEqual(tool._scm.discard_local_changes.call_count, 0)
-
- def test_working_changes_exist_with_force(self):
- tool = MockTool()
- tool._scm = Mock()
- tool._scm.has_working_directory_changes = lambda: True
- tool._scm.has_local_commits = lambda: False
- step = DiscardLocalChanges(tool, MockOptions(clean=True, force_clean=True))
- step.run({})
- self.assertEqual(tool._scm.discard_local_changes.call_count, 1)
-
- def test_local_commits_exist_with_force(self):
- tool = MockTool()
- tool._scm = Mock()
- tool._scm.has_working_directory_changes = lambda: False
- tool._scm.has_local_commits = lambda: True
- step = DiscardLocalChanges(tool, MockOptions(clean=True, force_clean=True))
- step.run({})
- self.assertEqual(tool._scm.discard_local_changes.call_count, 1)
-
- def test_local_commits_and_working_changes_exist_with_force(self):
- tool = MockTool()
- tool._scm = Mock()
- tool._scm.has_working_directory_changes = lambda: True
- tool._scm.has_local_commits = lambda: True
- step = DiscardLocalChanges(tool, MockOptions(clean=True, force_clean=True))
- step.run({})
- self.assertEqual(tool._scm.discard_local_changes.call_count, 1)
-
- def test_no_changes_exist_with_force(self):
- tool = MockTool()
- tool._scm = Mock()
- tool._scm.has_working_directory_changes = lambda: False
- tool._scm.has_local_commits = lambda: False
- step = DiscardLocalChanges(tool, MockOptions(clean=True, force_clean=True))
- step.run({})
- self.assertEqual(tool._scm.discard_local_changes.call_count, 1)
-
- def test_error_working_changes_exist_without_force(self):
- tool = MockTool()
- tool._scm = Mock()
- tool._scm.has_working_directory_changes = lambda: True
- tool._scm.has_local_commits = lambda: False
- step = DiscardLocalChanges(tool, MockOptions(clean=True, force_clean=False))
- self.assertRaises(ScriptError, step.run, {})
- self.assertEqual(tool._scm.discard_local_changes.call_count, 0)
-
- def test_error_local_commits_exist_without_force(self):
- tool = MockTool()
- tool._scm = Mock()
- tool._scm.has_working_directory_changes = lambda: False
- tool._scm.has_local_commits = lambda: True
- step = DiscardLocalChanges(tool, MockOptions(clean=True, force_clean=False))
- self.assertRaises(ScriptError, step.run, {})
- self.assertEqual(tool._scm.discard_local_changes.call_count, 0)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/ensurebugisopenandassigned.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/ensurebugisopenandassigned.py
deleted file mode 100644
index 54f90b6d7a2..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/ensurebugisopenandassigned.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.
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-
-
-class EnsureBugIsOpenAndAssigned(AbstractStep):
- def run(self, state):
- bug = self.cached_lookup(state, "bug")
- if bug.is_unassigned():
- self._tool.bugs.reassign_bug(bug.id())
-
- if bug.is_closed():
- # FIXME: We should probably pass this message in somehow?
- # Right now this step is only used before PostDiff steps, so this is OK.
- self._tool.bugs.reopen_bug(bug.id(), "Reopening to attach new patch.")
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/ensurelocalcommitifneeded.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/ensurelocalcommitifneeded.py
deleted file mode 100644
index 388d2a229b5..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/ensurelocalcommitifneeded.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.
-
-import logging
-import sys
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-
-_log = logging.getLogger(__name__)
-
-
-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():
- _log.error("--local-commit passed, but %s does not support local commits" % self._tool.scm().display_name())
- sys.exit(1)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/haslanded.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/haslanded.py
deleted file mode 100644
index b0692b32b27..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/haslanded.py
+++ /dev/null
@@ -1,120 +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 cStringIO as StringIO
-import logging
-import sys
-import re
-import tempfile
-
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.common.system.executive import Executive, ScriptError
-from webkitpy.common.checkout import diff_parser
-
-from webkitpy.tool.steps import confirmdiff
-
-_log = logging.getLogger(__name__)
-
-
-class HasLanded(confirmdiff.ConfirmDiff):
-
- @classmethod
- def convert_to_svn(cls, diff):
- lines = StringIO.StringIO(diff).readlines()
- convert = diff_parser.get_diff_converter(lines)
- return "".join(convert(x) for x in lines)
-
- @classmethod
- def strip_change_log(cls, diff):
- output = []
- skipping = False
- for line in StringIO.StringIO(diff).readlines():
- indexline = re.match("^Index: ([^\\n]*/)?([^/\\n]*)$", line)
- if skipping and indexline:
- skipping = False
- if indexline and indexline.group(2) == "ChangeLog":
- skipping = True
- if not skipping:
- output.append(line)
- return "".join(output)
-
- @classmethod
- def diff_diff(cls, diff1, diff2, diff1_suffix, diff2_suffix, executive=None):
- # Now this is where it gets complicated, we need to compare our diff to the diff at landed_revision.
- diff1_patch = tempfile.NamedTemporaryFile(suffix=diff1_suffix + '.patch')
- diff1_patch.write(diff1)
- diff1_patch.flush()
-
- # Check if there are any differences in the patch that don't happen
- diff2_patch = tempfile.NamedTemporaryFile(suffix=diff2_suffix + '.patch')
- diff2_patch.write(diff2)
- diff2_patch.flush()
-
- # Diff the two diff's together...
- if not executive:
- executive = Executive()
-
- try:
- return executive.run_command(
- ["interdiff", diff1_patch.name, diff2_patch.name], decode_output=False)
- except ScriptError, e:
- _log.warning("Unable to find interdiff util (part of GNU difftools package) which is required.")
- raise
-
- def run(self, state):
- # Check if there are changes first
- if not self._tool.scm().local_changes_exist():
- _log.warn("No local changes found, exiting.")
- return True
-
- # Check if there is a SVN revision in the bug from the commit queue
- landed_revision = self.cached_lookup(state, "bug").commit_revision()
- if not landed_revision:
- raise ScriptError("Unable to find landed message in associated bug.")
-
- # Now this is there it gets complicated, we need to compare our diff to the diff at landed_revision.
- landed_diff_bin = self._tool.scm().diff_for_revision(landed_revision)
- landed_diff_trimmed = self.strip_change_log(self.convert_to_svn(landed_diff_bin))
-
- # Check if there are any differences in the patch that don't happen
- local_diff_bin = self._tool.scm().create_patch()
- local_diff_trimmed = self.strip_change_log(self.convert_to_svn(local_diff_bin))
-
- # Diff the two diff's together...
- diff_diff = self.diff_diff(landed_diff_trimmed, local_diff_trimmed,
- '-landed', '-local',
- executive=self._tool.executive)
-
- with self._show_pretty_diff(diff_diff) as pretty_diff_file:
- if not pretty_diff_file:
- self._tool.user.page(diff_diff)
-
- if self._tool.user.confirm("May I discard local changes?"):
- # Discard changes if the user confirmed we should
- _log.warn("Discarding changes as requested.")
- self._tool.scm().discard_local_changes()
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/haslanded_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/haslanded_unittest.py
deleted file mode 100644
index e1779829df2..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/haslanded_unittest.py
+++ /dev/null
@@ -1,299 +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.thirdparty.unittest2 as unittest
-import subprocess
-
-from webkitpy.tool.steps.haslanded import HasLanded
-
-
-class HasLandedTest(unittest.TestCase):
- maxDiff = None
-
- @unittest.skipUnless(subprocess.call('which interdiff', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0, "requires interdiff")
- def test_run(self):
- # These patches require trailing whitespace to remain valid patches.
- diff1 = """\
-Index: a.py
-===================================================================
---- a.py
-+++ a.py
-@@ -1,3 +1,5 @@
- A
- B
- C
-+D
-+E
-Index: b.py
-===================================================================
---- b.py 2013-01-21 15:20:59.693887185 +1100
-+++ b.py 2013-01-21 15:22:24.382555711 +1100
-@@ -1,3 +1,5 @@
- 1
- 2
- 3
-+4
-+5
-"""
-
- diff1_add_line = """\
-Index: a.py
-===================================================================
---- a.py
-+++ a.py
-@@ -1,3 +1,6 @@
- A
- B
- C
-+D
-+E
-+F
-Index: b.py
-===================================================================
---- b.py
-+++ b.py
-@@ -1,3 +1,5 @@
- 1
- 2
- 3
-+4
-+5
-"""
-
- diff1_remove_line = """\
-Index: a.py
-===================================================================
---- a.py
-+++ a.py
-@@ -1,3 +1,4 @@
- A
- B
- C
-+D
-Index: b.py
-===================================================================
---- b.py
-+++ b.py
-@@ -1,3 +1,5 @@
- 1
- 2
- 3
-+4
-+5
-"""
-
- diff1_add_file = diff1 + """\
-Index: c.py
-===================================================================
---- c.py
-+++ c.py
-@@ -1,3 +1,5 @@
- 1
- 2
- 3
-+4
-+5
-"""
-
- diff1_remove_file = """\
-Index: a.py
-===================================================================
---- a.py
-+++ a.py
-@@ -1,3 +1,5 @@
- A
- B
- C
-+D
-+E
-"""
- self.assertMultiLineEqual(
- HasLanded.diff_diff(diff1, diff1_add_line, '', 'add-line'),
- """\
-diff -u a.py a.py
---- a.py
-+++ a.py
-@@ -5,0 +6 @@
-+F
-""")
-
- self.assertMultiLineEqual(
- HasLanded.diff_diff(diff1, diff1_remove_line, '', 'remove-line'),
- """\
-diff -u a.py a.py
---- a.py
-+++ a.py
-@@ -5 +4,0 @@
--E
-""")
- self.assertMultiLineEqual(
- HasLanded.diff_diff(diff1, diff1_add_file, '', 'add-file'),
- """\
-only in patch2:
-unchanged:
---- c.py
-+++ c.py
-@@ -1,3 +1,5 @@
- 1
- 2
- 3
-+4
-+5
-""")
- self.assertMultiLineEqual(
- HasLanded.diff_diff(diff1, diff1_remove_file, '', 'remove-file'),
- """\
-reverted:
---- b.py 2013-01-21 15:22:24.382555711 +1100
-+++ b.py 2013-01-21 15:20:59.693887185 +1100
-@@ -1,5 +1,3 @@
- 1
- 2
- 3
--4
--5
-""")
-
- def test_convert_to_svn_and_strip_change_log(self):
- # These patches require trailing whitespace to remain valid patches.
- testbefore1 = HasLanded.convert_to_svn("""\
-diff --git a/Tools/ChangeLog b/Tools/ChangeLog
-index 219ba72..0390b73 100644
---- a/Tools/ChangeLog
-+++ b/Tools/ChangeLog
-@@ -1,3 +1,32 @@
-+2013-01-17 Tim 'mithro' Ansell <mithro@mithis.com>
-+
-+ Adding "has-landed" command to webkit-patch which allows a person to
-+ Reviewed by NOBODY (OOPS!).
-+
- 2013-01-20 Tim 'mithro' Ansell <mithro@mithis.com>
-
- Extend diff_parser to support the --full-index output.
-diff --git a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-index 4bf8ec6..3a128cb 100644
---- a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-+++ b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-@@ -28,6 +28,8 @@
-+import re
-+
- from .attachment import Attachment
-
-""")
- testafter1 = HasLanded.convert_to_svn("""\
-diff --git a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-index 4bf8ec6..3a128cb 100644
---- a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-+++ b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-@@ -28,6 +28,8 @@
-+import re
-+
- from .attachment import Attachment
-
-diff --git a/Tools/ChangeLog b/Tools/ChangeLog
-index 219ba72..0390b73 100644
---- a/Tools/ChangeLog
-+++ b/Tools/ChangeLog
-@@ -1,3 +1,32 @@
-+2013-01-17 Tim 'mithro' Ansell <mithro@mithis.com>
-+
-+ Adding "has-landed" command to webkit-patch which allows a person to
-+ Reviewed by NOBODY (OOPS!).
-+
- 2013-01-20 Tim 'mithro' Ansell <mithro@mithis.com>
-
- Extend diff_parser to support the --full-index output.
-""")
- testexpected1 = """\
-Index: Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-===================================================================
---- Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-+++ Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-@@ -28,6 +28,8 @@
-+import re
-+
- from .attachment import Attachment
-
-"""
- testmiddle1 = HasLanded.convert_to_svn("""\
-diff --git a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-index 4bf8ec6..3a128cb 100644
---- a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-+++ b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-@@ -28,6 +28,8 @@
-+import re
-+
- from .attachment import Attachment
-
-diff --git a/ChangeLog b/ChangeLog
-index 219ba72..0390b73 100644
---- a/ChangeLog
-+++ b/ChangeLog
-@@ -1,3 +1,32 @@
-+2013-01-17 Tim 'mithro' Ansell <mithro@mithis.com>
-+
-+ Adding "has-landed" command to webkit-patch which allows a person to
-+ Reviewed by NOBODY (OOPS!).
-+
- 2013-01-20 Tim 'mithro' Ansell <mithro@mithis.com>
-
- Extend diff_parser to support the --full-index output.
-diff --git a/Tools/Scripts/webkitpy/common/other.py b/Tools/Scripts/webkitpy/common/other.py
-index 4bf8ec6..3a128cb 100644
---- a/Tools/Scripts/webkitpy/common/other.py
-+++ b/Tools/Scripts/webkitpy/common/other.py
-@@ -28,6 +28,8 @@
-+import re
-+
- from .attachment import Attachment
-
-""")
- testexpected2 = """\
-Index: Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-===================================================================
---- Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-+++ Tools/Scripts/webkitpy/common/net/bugzilla/bug.py
-@@ -28,6 +28,8 @@
-+import re
-+
- from .attachment import Attachment
-
-Index: Tools/Scripts/webkitpy/common/other.py
-===================================================================
---- Tools/Scripts/webkitpy/common/other.py
-+++ Tools/Scripts/webkitpy/common/other.py
-@@ -28,6 +28,8 @@
-+import re
-+
- from .attachment import Attachment
-
-"""
-
- self.assertMultiLineEqual(testexpected1, HasLanded.strip_change_log(testbefore1))
- self.assertMultiLineEqual(testexpected1, HasLanded.strip_change_log(testafter1))
- self.assertMultiLineEqual(testexpected2, HasLanded.strip_change_log(testmiddle1))
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/metastep.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/metastep.py
deleted file mode 100644
index 361a8a5e555..00000000000
--- a/chromium/third_party/WebKit/Tools/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/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/obsoletepatches.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/obsoletepatches.py
deleted file mode 100644
index f4894aea290..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/obsoletepatches.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 logging
-
-from webkitpy.tool.grammar import pluralize
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-
-_log = logging.getLogger(__name__)
-
-
-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.info("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/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/postdiff.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/postdiff.py
deleted file mode 100644
index fc5c443fd6f..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/postdiff.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 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
- bug_id = state["bug_id"]
-
- self._tool.bugs.add_patch_to_bug(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(bug_id))
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/promptforbugortitle.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/promptforbugortitle.py
deleted file mode 100644
index 3114891bcbe..00000000000
--- a/chromium/third_party/WebKit/Tools/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/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/reopenbugafterrollout.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/reopenbugafterrollout.py
deleted file mode 100644
index cf713a449af..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/reopenbugafterrollout.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.
-
-import logging
-
-from webkitpy.tool.comments import bug_comment_from_commit_text
-from webkitpy.tool.steps.abstractstep import AbstractStep
-
-_log = logging.getLogger(__name__)
-
-
-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.info(comment_text)
- _log.info("No bugs were updated.")
- return
- self._tool.bugs.reopen_bug(bug_id, comment_text)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/revertrevision.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/revertrevision.py
deleted file mode 100644
index a61037908aa..00000000000
--- a/chromium/third_party/WebKit/Tools/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_diffs(state["revision_list"])
- self.did_modify_checkout(state)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/runtests.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/runtests.py
deleted file mode 100644
index 937eb01440d..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/runtests.py
+++ /dev/null
@@ -1,108 +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
-import platform
-import sys
-from webkitpy.tool.steps.abstractstep import AbstractStep
-from webkitpy.tool.steps.options import Options
-from webkitpy.common.system.executive import ScriptError
-
-_log = logging.getLogger(__name__)
-
-class RunTests(AbstractStep):
- # FIXME: This knowledge really belongs in the commit-queue.
- NON_INTERACTIVE_FAILURE_LIMIT_COUNT = 30
-
- @classmethod
- def options(cls):
- return AbstractStep.options() + [
- Options.build_style,
- Options.test,
- Options.non_interactive,
- Options.quiet,
- ]
-
- def run(self, state):
- if not self._options.test:
- return
-
- if not self._options.non_interactive:
- # FIXME: We should teach the commit-queue and the EWS how to run these tests.
-
- python_unittests_command = self._tool.deprecated_port().run_python_unittests_command()
- if python_unittests_command:
- _log.info("Running Python unit tests")
- self._tool.executive.run_and_throw_if_fail(python_unittests_command, cwd=self._tool.scm().checkout_root)
-
- perl_unittests_command = self._tool.deprecated_port().run_perl_unittests_command()
- if perl_unittests_command:
- _log.info("Running Perl unit tests")
- self._tool.executive.run_and_throw_if_fail(perl_unittests_command, cwd=self._tool.scm().checkout_root)
-
- bindings_tests_command = self._tool.deprecated_port().run_bindings_tests_command()
- if bindings_tests_command:
- _log.info("Running bindings generation tests")
- args = bindings_tests_command
- try:
- self._tool.executive.run_and_throw_if_fail(args, cwd=self._tool.scm().checkout_root)
- except ScriptError, e:
- _log.info("Error running run-bindings-tests: %s" % e.message_with_output())
-
- webkit_unit_tests_command = self._tool.deprecated_port().run_webkit_unit_tests_command()
- if webkit_unit_tests_command:
- _log.info("Running WebKit unit tests")
- args = webkit_unit_tests_command
- try:
- self._tool.executive.run_and_throw_if_fail(args, cwd=self._tool.scm().checkout_root)
- except ScriptError, e:
- _log.info("Error running webkit_unit_tests: %s" % e.message_with_output())
-
-
- _log.info("Running run-webkit-tests")
- args = self._tool.deprecated_port().run_webkit_tests_command()
- if self._options.non_interactive:
- args.extend([
- "--no-new-test-results",
- "--no-show-results",
- "--exit-after-n-failures=%s" % self.NON_INTERACTIVE_FAILURE_LIMIT_COUNT,
- ])
-
- # old-run-webkit-tests does not support --skip-failing-tests
- # Using --quiet one Windows fails when we try to use /dev/null, disabling for now until we find a fix
- if sys.platform != "cygwin":
- args.append("--quiet")
- args.append("--skip-failing-tests")
- else:
- args.append("--no-build");
-
- if self._options.quiet:
- args.append("--quiet")
-
- self._tool.executive.run_and_throw_if_fail(args, cwd=self._tool.scm().checkout_root)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py
deleted file mode 100644
index 89ca932d885..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright (C) 2011 Google Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (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 platform
-import sys
-import webkitpy.thirdparty.unittest2 as unittest
-
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.tool.mocktool import MockOptions, MockTool
-from webkitpy.tool.steps.runtests import RunTests
-
-class RunTestsTest(unittest.TestCase):
- def test_webkit_run_unit_tests(self):
- tool = MockTool(log_executive=True)
- tool._deprecated_port.run_python_unittests_command = lambda: None
- tool._deprecated_port.run_perl_unittests_command = lambda: None
- step = RunTests(tool, MockOptions(test=True, non_interactive=True, quiet=False))
-
- if sys.platform != "cygwin":
- expected_logs = """Running bindings generation tests
-MOCK run_and_throw_if_fail: ['mock-run-bindings-tests'], cwd=/mock-checkout
-Running WebKit unit tests
-MOCK run_and_throw_if_fail: ['mock-run-webkit-unit-tests'], cwd=/mock-checkout
-Running run-webkit-tests
-MOCK run_and_throw_if_fail: ['mock-run-webkit-tests', '--no-new-test-results', '--no-show-results', '--exit-after-n-failures=30', '--quiet', '--skip-failing-tests'], cwd=/mock-checkout
-"""
- else:
- expected_logs = """Running bindings generation tests
-MOCK run_and_throw_if_fail: ['mock-run-bindings-tests'], cwd=/mock-checkout
-Running WebKit unit tests
-MOCK run_and_throw_if_fail: ['mock-run-webkit-unit-tests'], cwd=/mock-checkout
-Running run-webkit-tests
-MOCK run_and_throw_if_fail: ['mock-run-webkit-tests', '--no-new-test-results', '--no-show-results', '--exit-after-n-failures=30', '--no-build'], cwd=/mock-checkout
-"""
-
- OutputCapture().assert_outputs(self, step.run, [{}], expected_logs=expected_logs)
diff --git a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py b/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py
deleted file mode 100644
index e735d663325..00000000000
--- a/chromium/third_party/WebKit/Tools/Scripts/webkitpy/tool/steps/steps_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 webkitpy.thirdparty.unittest2 as unittest
-
-from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.common.config.ports import DeprecatedPort
-from webkitpy.tool.mocktool import MockOptions, MockTool
-
-from webkitpy.tool import steps
-
-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_prompt_for_bug_or_title_step(self):
- tool = MockTool()
- tool.user.prompt = lambda message: 50000
- self._run_step(steps.PromptForBugOrTitle, tool=tool)
-
- def _assert_step_output_with_bug(self, step, bug_id, expected_logs, options=None):
- state = {'bug_id': bug_id}
- OutputCapture().assert_outputs(self, self._run_step, [step, MockTool(), options, state], expected_logs=expected_logs)
-
- def test_runtests_args(self):
- mock_options = self._step_options()
- mock_options.non_interactive = False
- step = steps.RunTests(MockTool(log_executive=True), mock_options)
- tool = MockTool(log_executive=True)
- # FIXME: We shouldn't use a real port-object here, but there is too much to mock at the moment.
- tool._deprecated_port = DeprecatedPort()
- step = steps.RunTests(tool, mock_options)
- expected_logs = """Running Python unit tests
-MOCK run_and_throw_if_fail: ['Tools/Scripts/test-webkitpy'], cwd=/mock-checkout
-Running Perl unit tests
-MOCK run_and_throw_if_fail: ['Tools/Scripts/test-webkitperl'], cwd=/mock-checkout
-Running bindings generation tests
-MOCK run_and_throw_if_fail: ['Tools/Scripts/run-bindings-tests'], cwd=/mock-checkout
-Running run-webkit-tests
-MOCK run_and_throw_if_fail: ['Tools/Scripts/run-webkit-tests', '--quiet'], cwd=/mock-checkout
-"""
- OutputCapture().assert_outputs(self, step.run, [{}], expected_logs=expected_logs)