diff options
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/layout_tests')
12 files changed, 466 insertions, 108 deletions
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py index c543d91..51dcac8 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py @@ -36,8 +36,10 @@ import os import subprocess import sys import re +import webkitpy.common.checkout.scm as scm import webkitpy.common.system.executive as executive import webkitpy.common.system.logutils as logutils +import webkitpy.common.system.ospath as ospath import webkitpy.layout_tests.port.factory as port_factory _log = logutils.get_logger(__file__) @@ -52,11 +54,14 @@ def port_fallbacks(): back on. All platforms fall back on 'base'. """ fallbacks = {_BASE_PLATFORM: []} - for port_name in os.listdir(os.path.join('LayoutTests', 'platform')): + platform_dir = os.path.join(scm.find_checkout_root(), 'LayoutTests', + 'platform') + for port_name in os.listdir(platform_dir): try: platforms = port_factory.get(port_name).baseline_search_path() except NotImplementedError: - _log.error("'%s' lacks baseline_search_path(), please fix." % port_name) + _log.error("'%s' lacks baseline_search_path(), please fix." + % port_name) fallbacks[port_name] = [_BASE_PLATFORM] continue fallbacks[port_name] = [os.path.basename(p) for p in platforms][1:] @@ -102,7 +107,8 @@ def cluster_file_hashes(glob_pattern): # Fill in the map. cmd = ('git', 'ls-tree', '-r', 'HEAD', 'LayoutTests') try: - git_output = executive.Executive().run_command(cmd) + git_output = executive.Executive().run_command(cmd, + cwd=scm.find_checkout_root()) except OSError, e: if e.errno == 2: # No such file or directory. _log.error("Error: 'No such file' when running git.") @@ -156,11 +162,28 @@ def has_intermediate_results(test, fallbacks, matching_platform, return False -def find_dups(hashes, port_fallbacks): +def get_relative_test_path(filename, relative_to, + checkout_root=scm.find_checkout_root()): + """Constructs a relative path to |filename| from |relative_to|. + Args: + filename: The test file we're trying to get a relative path to. + relative_to: The absolute path we're relative to. + Returns: + A relative path to filename or None if |filename| is not below + |relative_to|. + """ + layout_test_dir = os.path.join(checkout_root, 'LayoutTests') + abs_path = os.path.join(layout_test_dir, filename) + return ospath.relpath(abs_path, relative_to) + + +def find_dups(hashes, port_fallbacks, relative_to): """Yields info about redundant test expectations. Args: hashes: a list of hashes as returned by cluster_file_hashes. - port_fallbacks: a list of fallback information as returned by get_port_fallbacks. + port_fallbacks: a list of fallback information as returned by + get_port_fallbacks. + relative_to: the directory that we want the results relative to Returns: a tuple containing (test, platform, fallback, platforms) """ @@ -176,13 +199,24 @@ def find_dups(hashes, port_fallbacks): # See if any of the platforms are redundant with each other. for platform in platforms.keys(): for fallback in port_fallbacks[platform]: - if fallback in platforms.keys(): - # We have to verify that there isn't an intermediate result - # that causes this duplicate hash to exist. - if not has_intermediate_results(test, - port_fallbacks[platform], fallback): - path = os.path.join('LayoutTests', platforms[platform]) - yield test, platform, fallback, path + if fallback not in platforms.keys(): + continue + # We have to verify that there isn't an intermediate result + # that causes this duplicate hash to exist. + if has_intermediate_results(test, port_fallbacks[platform], + fallback): + continue + # We print the relative path so it's easy to pipe the results + # to xargs rm. + path = get_relative_test_path(platforms[platform], relative_to) + if not path: + continue + yield { + 'test': test, + 'platform': platform, + 'fallback': fallback, + 'path': path, + } def deduplicate(glob_pattern): @@ -194,5 +228,4 @@ def deduplicate(glob_pattern): """ fallbacks = port_fallbacks() hashes = cluster_file_hashes(glob_pattern) - return [{'test': test, 'path': path, 'platform': platform, 'fallback': fallback} - for test, platform, fallback, path in find_dups(hashes, fallbacks)] + return list(find_dups(hashes, fallbacks, os.getcwd())) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py index be2e381..bb9604f 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py @@ -186,3 +186,22 @@ class ListDuplicatesTest(unittest.TestCase): 'fallback': 'chromium-win', 'platform': 'chromium-linux'}, result[0]) + + def test_get_relative_test_path(self): + checkout_root = scm.find_checkout_root() + layout_test_dir = os.path.join(checkout_root, 'LayoutTests') + test_cases = ( + ('platform/mac/test.html', + ('platform/mac/test.html', layout_test_dir)), + ('LayoutTests/platform/mac/test.html', + ('platform/mac/test.html', checkout_root)), + (None, + ('platform/mac/test.html', os.path.join(checkout_root, 'WebCore'))), + ('test.html', + ('platform/mac/test.html', os.path.join(layout_test_dir, 'platform/mac'))), + (None, + ('platform/mac/test.html', os.path.join(layout_test_dir, 'platform/win'))), + ) + for expected, inputs in test_cases: + self.assertEquals(expected, + deduplicate_tests.get_relative_test_path(*inputs)) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py index 9b963ca..970de60 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py @@ -72,20 +72,19 @@ def log_stack(stack): _log.error(' %s' % line.strip()) -def _process_output(port, test_info, test_types, test_args, configuration, - output_dir, crash, timeout, test_run_time, actual_checksum, +def _process_output(port, options, test_info, test_types, test_args, + crash, timeout, test_run_time, actual_checksum, output, error): """Receives the output from a DumpRenderTree process, subjects it to a number of tests, and returns a list of failure types the test produced. Args: port: port-specific hooks + options: command line options argument from optparse proc: an active DumpRenderTree process test_info: Object containing the test filename, uri and timeout test_types: list of test types to subject the output to test_args: arguments to be passed to each test - configuration: Debug or Release - output_dir: directory to put crash stack traces into Returns: a TestResult object """ @@ -106,7 +105,8 @@ def _process_output(port, test_info, test_types, test_args, configuration, _log.debug("Stacktrace for %s:\n%s" % (test_info.filename, error)) # Strip off "file://" since RelativeTestFilename expects # filesystem paths. - filename = os.path.join(output_dir, port.relative_test_filename( + filename = os.path.join(options.results_directory, + port.relative_test_filename( test_info.filename)) filename = os.path.splitext(filename)[0] + "-stack.txt" port.maybe_make_directory(os.path.split(filename)[0]) @@ -122,7 +122,7 @@ def _process_output(port, test_info, test_types, test_args, configuration, start_diff_time = time.time() new_failures = test_type.compare_output(port, test_info.filename, output, local_test_args, - configuration) + options.configuration) # Don't add any more failures if we already have a crash, so we don't # double-report those tests. We do double-report for timeouts since # we still want to see the text and image output. @@ -166,25 +166,23 @@ class TestResult(object): class SingleTestThread(threading.Thread): """Thread wrapper for running a single test file.""" - def __init__(self, port, image_path, shell_args, test_info, - test_types, test_args, configuration, output_dir): + def __init__(self, port, options, test_info, test_types, test_args): """ Args: port: object implementing port-specific hooks + options: command line argument object from optparse test_info: Object containing the test filename, uri and timeout - output_dir: Directory to put crash stacks into. - See TestShellThread for documentation of the remaining arguments. + test_types: A list of TestType objects to run the test output + against. + test_args: A TestArguments object to pass to each TestType. """ threading.Thread.__init__(self) self._port = port - self._image_path = image_path - self._shell_args = shell_args + self._options = options self._test_info = test_info self._test_types = test_types self._test_args = test_args - self._configuration = configuration - self._output_dir = output_dir self._driver = None def run(self): @@ -194,17 +192,17 @@ class SingleTestThread(threading.Thread): # FIXME: this is a separate routine to work around a bug # in coverage: see http://bitbucket.org/ned/coveragepy/issue/85. test_info = self._test_info - self._driver = self._port.create_driver(self._image_path, - self._shell_args) + self._driver = self._port.create_driver(self._test_args.png_path, + self._options) self._driver.start() start = time.time() crash, timeout, actual_checksum, output, error = \ self._driver.run_test(test_info.uri.strip(), test_info.timeout, test_info.image_hash()) end = time.time() - self._test_result = _process_output(self._port, + self._test_result = _process_output(self._port, self._options, test_info, self._test_types, self._test_args, - self._configuration, self._output_dir, crash, timeout, end - start, + crash, timeout, end - start, actual_checksum, output, error) self._driver.stop() @@ -248,12 +246,13 @@ class WatchableThread(threading.Thread): class TestShellThread(WatchableThread): - def __init__(self, port, filename_list_queue, result_queue, - test_types, test_args, image_path, shell_args, options): + def __init__(self, port, options, filename_list_queue, result_queue, + test_types, test_args): """Initialize all the local state for this DumpRenderTree thread. Args: port: interface to port-specific hooks + options: command line options argument from optparse filename_list_queue: A thread safe Queue class that contains lists of tuples of (filename, uri) pairs. result_queue: A thread safe Queue class that will contain tuples of @@ -261,22 +260,17 @@ class TestShellThread(WatchableThread): test_types: A list of TestType objects to run the test output against. test_args: A TestArguments object to pass to each TestType. - shell_args: Any extra arguments to be passed to DumpRenderTree. - options: A property dictionary as produced by optparse. The - command-line options should match those expected by - run_webkit_tests; they are typically passed via the - run_webkit_tests.TestRunner class.""" + + """ WatchableThread.__init__(self) self._port = port + self._options = options self._filename_list_queue = filename_list_queue self._result_queue = result_queue self._filename_list = [] self._test_types = test_types self._test_args = test_args self._driver = None - self._image_path = image_path - self._shell_args = shell_args - self._options = options self._directory_timing_stats = {} self._test_results = [] self._num_tests = 0 @@ -433,13 +427,11 @@ class TestShellThread(WatchableThread): A TestResult """ - worker = SingleTestThread(self._port, self._image_path, - self._shell_args, + worker = SingleTestThread(self._port, + self._options, test_info, self._test_types, - self._test_args, - self._options.configuration, - self._options.results_directory) + self._test_args) worker.start() @@ -503,11 +495,11 @@ class TestShellThread(WatchableThread): self._driver.run_test(test_info.uri, test_info.timeout, image_hash) end = time.time() - result = _process_output(self._port, test_info, self._test_types, - self._test_args, self._options.configuration, - self._options.results_directory, crash, - timeout, end - start, actual_checksum, - output, error) + result = _process_output(self._port, self._options, + test_info, self._test_types, + self._test_args, crash, + timeout, end - start, actual_checksum, + output, error) self._test_results.append(result) return result @@ -521,7 +513,8 @@ class TestShellThread(WatchableThread): # poll() is not threadsafe and can throw OSError due to: # http://bugs.python.org/issue1731717 if (not self._driver or self._driver.poll() is not None): - self._driver = self._port.create_driver(self._image_path, self._shell_args) + self._driver = self._port.create_driver(self._test_args.png_path, + self._options) self._driver.start() def _kill_dump_render_tree(self): diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py index 9125f9e..70beac3 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py @@ -152,6 +152,16 @@ class Port(object): While this is a generic routine, we include it in the Port interface so that it can be overriden for testing purposes.""" + + # The filenames show up in the diff output, make sure they're + # raw bytes and not unicode, so that they don't trigger join() + # trying to decode the input. + def to_raw_bytes(str): + if isinstance(str, unicode): + return str.encode('utf-8') + return str + expected_filename = to_raw_bytes(expected_filename) + actual_filename = to_raw_bytes(actual_filename) diff = difflib.unified_diff(expected_text.splitlines(True), actual_text.splitlines(True), expected_filename, @@ -364,7 +374,7 @@ class Port(object): results_filename in a users' browser.""" raise NotImplementedError('Port.show_html_results_file') - def create_driver(self, png_path, options): + def create_driver(self, image_path, options): """Return a newly created base.Driver subclass for starting/stopping the test driver.""" raise NotImplementedError('Port.create_driver') @@ -678,7 +688,7 @@ class Port(object): class Driver: """Abstract interface for the DumpRenderTree interface.""" - def __init__(self, port, png_path, options): + def __init__(self, port, png_path, options, executive): """Initialize a Driver to subsequently run tests. Typically this routine will spawn DumpRenderTree in a config @@ -688,7 +698,10 @@ class Driver: png_path - an absolute path for the driver to write any image data for a test (as a PNG). If no path is provided, that indicates that pixel test results will not be checked. - options - any port-specific driver options.""" + options - command line options argument from optparse + executive - reference to the process-wide Executive object + + """ raise NotImplementedError('Driver.__init__') def run_test(self, uri, timeout, checksum): diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py index 1cc426f..780cd22 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py @@ -66,7 +66,7 @@ class UnitTestPort(base.Port): def _open_configuration_file(self): if self._configuration_contents: return NewStringIO(self._configuration_contents) - return base.Port._open_configuration_file(self) + raise IOError class PortTest(unittest.TestCase): @@ -163,6 +163,33 @@ class PortTest(unittest.TestCase): self.assertFalse(base._wdiff_available) base._wdiff_available = True + def test_diff_text(self): + port = base.Port() + # Make sure that we don't run into decoding exceptions when the + # filenames are unicode, with regular or malformed input (expected or + # actual input is always raw bytes, not unicode). + port.diff_text('exp', 'act', 'exp.txt', 'act.txt') + port.diff_text('exp', 'act', u'exp.txt', 'act.txt') + port.diff_text('exp', 'act', u'a\xac\u1234\u20ac\U00008000', 'act.txt') + + port.diff_text('exp' + chr(255), 'act', 'exp.txt', 'act.txt') + port.diff_text('exp' + chr(255), 'act', u'exp.txt', 'act.txt') + + # Though expected and actual files should always be read in with no + # encoding (and be stored as str objects), test unicode inputs just to + # be safe. + port.diff_text(u'exp', 'act', 'exp.txt', 'act.txt') + port.diff_text( + u'a\xac\u1234\u20ac\U00008000', 'act', 'exp.txt', 'act.txt') + + # And make sure we actually get diff output. + diff = port.diff_text('foo', 'bar', 'exp.txt', 'act.txt') + self.assertTrue('foo' in diff) + self.assertTrue('bar' in diff) + self.assertTrue('exp.txt' in diff) + self.assertTrue('act.txt' in diff) + self.assertFalse('nosuchthing' in diff) + def test_default_configuration_notfound(self): port = UnitTestPort() self.assertEqual(port.default_configuration(), "Release") @@ -223,8 +250,8 @@ class VirtualTest(unittest.TestCase): self.assertVirtual(port._shut_down_http_server, None) def test_virtual_driver_method(self): - self.assertRaises(NotImplementedError, base.Driver, base.Port, "", None) - self.assertVirtual(base.Driver, base.Port, "", None) + self.assertRaises(NotImplementedError, base.Driver, base.Port(), + "", None, None) def test_virtual_driver_methods(self): class VirtualDriver(base.Driver): diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py index 896eab1..3fc4613 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py @@ -194,13 +194,11 @@ class ChromiumPort(base.Port): def create_driver(self, image_path, options): """Starts a new Driver and returns a handle to it.""" - if self._options.use_drt and sys.platform == 'darwin': - return webkit.WebKitDriver(self, image_path, options, executive=self._executive) - if self._options.use_drt: - options += ['--test-shell'] - else: - options += ['--layout-tests'] - return ChromiumDriver(self, image_path, options, executive=self._executive) + if options.use_drt and sys.platform == 'darwin': + return webkit.WebKitDriver(self, image_path, options, + executive=self._executive) + return ChromiumDriver(self, image_path, options, + executive=self._executive) def start_helper(self): helper_path = self._path_to_helper() @@ -337,22 +335,32 @@ class ChromiumDriver(base.Driver): def __init__(self, port, image_path, options, executive=Executive()): self._port = port - self._configuration = port._options.configuration - # FIXME: _options is very confusing, because it's not an Options() element. - # FIXME: These don't need to be passed into the constructor, but could rather - # be passed into .start() self._options = options self._image_path = image_path self._executive = executive + def _driver_args(self): + driver_args = [] + if self._image_path: + driver_args.append("--pixel-tests=" + self._image_path) + + if self._options.use_drt: + driver_args.append('--test-shell') + else: + driver_args.append('--layout-tests') + + if self._options.startup_dialog: + driver_args.append('--testshell-startup-dialog') + + if self._options.gp_fault_error_box: + driver_args.append('--gp-fault-error-box') + return driver_args + def start(self): # FIXME: Should be an error to call this method twice. - cmd = [] - # FIXME: We should not be grabbing at self._port._options.wrapper directly. - cmd += self._command_wrapper(self._port._options.wrapper) - cmd += [self._port._path_to_driver()] - if self._options: - cmd += self._options + cmd = self._command_wrapper(self._options.wrapper) + cmd.append(self._port._path_to_driver()) + cmd += self._driver_args() # We need to pass close_fds=True to work around Python bug #2320 # (otherwise we can hang when we kill DumpRenderTree when we are running diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py index 1af01ad..4940e4c 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py @@ -86,6 +86,7 @@ class DryRunPort(object): port_name = port_name[len(pfx):] else: port_name = None + self._options = options self.__delegate = factory.get(port_name, options) def __getattr__(self, name): @@ -116,16 +117,17 @@ class DryRunPort(object): pass def create_driver(self, image_path, options): - return DryrunDriver(self, image_path, options) + return DryrunDriver(self, image_path, options, executive=None) class DryrunDriver(base.Driver): """Dryrun implementation of the DumpRenderTree / Driver interface.""" - def __init__(self, port, image_path, test_driver_options): + def __init__(self, port, image_path, options, executive): self._port = port - self._driver_options = test_driver_options + self._options = options self._image_path = image_path + self._executive = executive self._layout_tests_dir = None def poll(self): diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py index a3a16c3..2ccddb0 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py @@ -97,7 +97,7 @@ class TestPort(base.Port): pass def create_driver(self, image_path, options): - return TestDriver(image_path, options, self) + return TestDriver(self, image_path, options, executive=None) def start_http_server(self): pass @@ -139,10 +139,11 @@ class TestPort(base.Port): class TestDriver(base.Driver): """Test/Dummy implementation of the DumpRenderTree interface.""" - def __init__(self, image_path, test_driver_options, port): - self._driver_options = test_driver_options - self._image_path = image_path + def __init__(self, port, image_path, options, executive): self._port = port + self._image_path = image_path + self._options = options + self._executive = executive self._image_written = False def poll(self): @@ -204,7 +205,7 @@ class TestDriver(base.Driver): crash = False timeout = False output = basename + '-txt\n' - if self._port.options().pixel_tests and ( + if self._options.pixel_tests and ( 'image' in basename or 'check' in basename): checksum = basename + '-checksum\n' with open(self._image_path, "w") as f: diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py index b085ceb..88c9bdf 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py @@ -199,7 +199,8 @@ class WebKitPort(base.Port): webbrowser.open(uri, new=1) def create_driver(self, image_path, options): - return WebKitDriver(self, image_path, options, executive=self._executive) + return WebKitDriver(self, image_path, options, + executive=self._executive) def test_base_platform_names(self): # At the moment we don't use test platform names, but we have @@ -405,22 +406,31 @@ class WebKitPort(base.Port): class WebKitDriver(base.Driver): """WebKit implementation of the DumpRenderTree interface.""" - def __init__(self, port, image_path, driver_options, executive=Executive()): + def __init__(self, port, image_path, options, executive=Executive()): self._port = port - # FIXME: driver_options is never used. self._image_path = image_path + self._options = options + self._executive = executive self._driver_tempdir = tempfile.mkdtemp(prefix='DumpRenderTree-') def __del__(self): shutil.rmtree(self._driver_tempdir) + def _driver_args(self): + driver_args = [] + if self._image_path: + driver_args.append('--pixel-tests') + + # These are used by the Chromium DRT port + if self._options.use_drt: + driver_args.append('--test-shell') + return driver_args + def start(self): - command = [] - # FIXME: We should not be grabbing at self._port._options.wrapper directly. - command += self._command_wrapper(self._port._options.wrapper) + command = self._command_wrapper(self._options.wrapper) command += [self._port._path_to_driver(), '-'] - if self._image_path: - command.append('--pixel-tests') + command += self._driver_args() + environment = self._port.setup_environ_for_server() environment['DYLD_FRAMEWORK_PATH'] = self._port._build_path() environment['DUMPRENDERTREE_TEMP'] = self._driver_tempdir diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py index 2e2da6d..14d4f0e 100755 --- a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py @@ -564,27 +564,18 @@ class TestRunner: filename_queue.put(item) return filename_queue - def _get_dump_render_tree_args(self, index): + def _get_test_args(self, index): """Returns the tuple of arguments for tests and for DumpRenderTree.""" - shell_args = [] test_args = test_type_base.TestArguments() - png_path = None + test_args.png_path = None if self._options.pixel_tests: png_path = os.path.join(self._options.results_directory, "png_result%s.png" % index) - shell_args.append("--pixel-tests=" + png_path) test_args.png_path = png_path - test_args.new_baseline = self._options.new_baseline test_args.reset_results = self._options.reset_results - if self._options.startup_dialog: - shell_args.append('--testshell-startup-dialog') - - if self._options.gp_fault_error_box: - shell_args.append('--gp-fault-error-box') - - return test_args, png_path, shell_args + return test_args def _contains_tests(self, subdir): for test_file in self._test_files: @@ -610,11 +601,10 @@ class TestRunner: test_types.append(test_type(self._port, self._options.results_directory)) - test_args, png_path, shell_args = \ - self._get_dump_render_tree_args(i) + test_args = self._get_test_args(i) thread = dump_render_tree_thread.TestShellThread(self._port, - filename_queue, self._result_queue, test_types, test_args, - png_path, shell_args, self._options) + self._options, filename_queue, self._result_queue, + test_types, test_args) if self._is_single_threaded(): thread.run_in_main_thread(self, result_summary) else: diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests.py new file mode 100755 index 0000000..f4c8098 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python + +# Copyright (C) 2010 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from __future__ import with_statement + +import glob +import logging +import optparse +import os +import re +import sys +import webkitpy.common.checkout.scm as scm + +_log = logging.getLogger("webkitpy.layout_tests." + "update-webgl-conformance-tests") + + +def remove_first_line_comment(text): + return re.compile(r'^<!--.*?-->\s*', re.DOTALL).sub('', text) + + +def translate_includes(text): + # Mapping of single filename to relative path under WebKit root. + # Assumption: these filenames are globally unique. + include_mapping = { + "js-test-style.css": "../../js/resources", + "js-test-pre.js": "../../js/resources", + "js-test-post.js": "../../js/resources", + "desktop-gl-constants.js": "resources", + } + + for filename, path in include_mapping.items(): + search = r'(?:[^"\'= ]*/)?' + re.escape(filename) + replace = os.path.join(path, filename) + text = re.sub(search, replace, text) + + return text + + +def translate_khronos_test(text): + """ + This method translates the contents of a Khronos test to a WebKit test. + """ + + translateFuncs = [ + remove_first_line_comment, + translate_includes, + ] + + for f in translateFuncs: + text = f(text) + + return text + + +def update_file(in_filename, out_dir): + # check in_filename exists + # check out_dir exists + out_filename = os.path.join(out_dir, os.path.basename(in_filename)) + + _log.debug("Processing " + in_filename) + with open(in_filename, 'r') as in_file: + with open(out_filename, 'w') as out_file: + out_file.write(translate_khronos_test(in_file.read())) + + +def update_directory(in_dir, out_dir): + for filename in glob.glob(os.path.join(in_dir, '*.html')): + update_file(os.path.join(in_dir, filename), out_dir) + + +def default_out_dir(): + current_scm = scm.detect_scm_system(os.path.dirname(sys.argv[0])) + if not current_scm: + return os.getcwd() + root_dir = current_scm.checkout_root + if not root_dir: + return os.getcwd() + out_dir = os.path.join(root_dir, "LayoutTests/fast/canvas/webgl") + if os.path.isdir(out_dir): + return out_dir + return os.getcwd() + + +def configure_logging(options): + """Configures the logging system.""" + log_fmt = '%(levelname)s: %(message)s' + log_datefmt = '%y%m%d %H:%M:%S' + log_level = logging.INFO + if options.verbose: + log_fmt = ('%(asctime)s %(filename)s:%(lineno)-4d %(levelname)s ' + '%(message)s') + log_level = logging.DEBUG + logging.basicConfig(level=log_level, format=log_fmt, + datefmt=log_datefmt) + + +def option_parser(): + usage = "usage: %prog [options] (input file or directory)" + parser = optparse.OptionParser(usage=usage) + parser.add_option('-v', '--verbose', + action='store_true', + default=False, + help='include debug-level logging') + parser.add_option('-o', '--output', + action='store', + type='string', + default=default_out_dir(), + metavar='DIR', + help='specify an output directory to place files ' + 'in [default: %default]') + return parser + + +def main(): + parser = option_parser() + (options, args) = parser.parse_args() + configure_logging(options) + + if len(args) == 0: + _log.error("Must specify an input directory or filename.") + parser.print_help() + return 1 + + in_name = args[0] + if os.path.isfile(in_name): + update_file(in_name, options.output) + elif os.path.isdir(in_name): + update_directory(in_name, options.output) + else: + _log.error("'%s' is not a directory or a file.", in_name) + return 2 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests_unittest.py new file mode 100644 index 0000000..7393b70 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests_unittest.py @@ -0,0 +1,102 @@ +#!/usr/bin/python +# Copyright (C) 2010 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Unit tests for update_webgl_conformance_tests.""" + +import unittest +from webkitpy.layout_tests import update_webgl_conformance_tests as webgl + + +def construct_script(name): + return "<script src=\"" + name + "\"></script>\n" + + +def construct_style(name): + return "<link rel=\"stylesheet\" href=\"" + name + "\">" + + +class TestTranslation(unittest.TestCase): + def assert_unchanged(self, text): + self.assertEqual(text, webgl.translate_khronos_test(text)) + + def assert_translate(self, input, output): + self.assertEqual(output, webgl.translate_khronos_test(input)) + + def test_simple_unchanged(self): + self.assert_unchanged("") + self.assert_unchanged("<html></html>") + + def test_header_strip(self): + single_line_header = "<!-- single line header. -->" + multi_line_header = """<!-- this is a multi-line + header. it should all be removed too. + -->""" + text = "<html></html>" + self.assert_translate(single_line_header, "") + self.assert_translate(single_line_header + text, text) + self.assert_translate(multi_line_header + text, text) + + def dont_strip_other_headers(self): + self.assert_unchanged("<html>\n<!-- don't remove comments on other lines. -->\n</html>") + + def test_include_rewriting(self): + # Mappings to None are unchanged + styles = { + "../resources/js-test-style.css": "../../js/resources/js-test-style.css", + "fail.css": None, + "resources/stylesheet.css": None, + "../resources/style.css": None, + } + scripts = { + "../resources/js-test-pre.js": "../../js/resources/js-test-pre.js", + "../resources/js-test-post.js": "../../js/resources/js-test-post.js", + "../resources/desktop-gl-constants.js": "resources/desktop-gl-constants.js", + + "resources/shadow-offset.js": None, + "../resources/js-test-post-async.js": None, + } + + input_text = "" + output_text = "" + for input, output in styles.items(): + input_text += construct_style(input) + output_text += construct_style(output if output else input) + for input, output in scripts.items(): + input_text += construct_script(input) + output_text += construct_script(output if output else input) + + head = '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">\n<html>\n<head>\n' + foot = '</head>\n<body>\n</body>\n</html>' + input_text = head + input_text + foot + output_text = head + output_text + foot + self.assert_translate(input_text, output_text) + + +if __name__ == '__main__': + unittest.main() |