diff options
Diffstat (limited to 'Tools/Scripts/webkitpy/layout_tests')
30 files changed, 381 insertions, 192 deletions
diff --git a/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_finder.py b/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_finder.py index a7b49831c..6447c8fb4 100644 --- a/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_finder.py +++ b/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_finder.py @@ -106,11 +106,9 @@ class LayoutTestFinder(object): tests_to_skip = all_tests - tests_to_skip elif self._options.skipped == 'ignore': tests_to_skip = set() - elif self._options.skipped == 'default': - pass # listed for completeness - - # make sure we're explicitly running any tests passed on the command line. - tests_to_skip -= paths + elif self._options.skipped != 'always': + # make sure we're explicitly running any tests passed on the command line; equivalent to 'default'. + tests_to_skip -= paths # unless of course we don't want to run the HTTP tests :) if not self._options.http: diff --git a/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py b/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py index d06ed7153..c0a70e615 100644 --- a/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py +++ b/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py @@ -239,8 +239,9 @@ def summarize_results(port_obj, expectations, result_summary, retry_summary, tes try: # We only use the svn revision for using trac links in the results.html file, # Don't do this by default since it takes >100ms. + # FIXME: Do we really need to populate this both here and in the json_results_generator? if use_trac_links_in_results_html(port_obj): - port_obj.host._initialize_scm() + port_obj.host.initialize_scm() results['revision'] = port_obj.host.scm().head_svn_revision() except Exception, e: _log.warn("Failed to determine svn revision for checkout (cwd: %s, webkit_base: %s), leaving 'revision' key blank in full_results.json.\n%s" % (port_obj._filesystem.getcwd(), port_obj.path_from_webkit_base(), e)) diff --git a/Tools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py b/Tools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py index c7d7c3eb7..73834f0ad 100644 --- a/Tools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py +++ b/Tools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py @@ -281,7 +281,8 @@ class JSONResultsGeneratorBase(object): results_for_builder = results_json[builder_name] - self._insert_generic_metadata(results_for_builder) + if builder_name: + self._insert_generic_metadata(results_for_builder) self._insert_failure_summaries(results_for_builder) @@ -377,15 +378,17 @@ class JSONResultsGeneratorBase(object): return self.__class__.PASS_RESULT - # FIXME: Callers should use scm.py instead. - # FIXME: Identify and fix the run-time errors that were observed on Windows - # chromium buildbot when we had updated this code to use scm.py once before. def _get_svn_revision(self, in_directory): """Returns the svn revision for the given directory. Args: in_directory: The directory where svn is to be run. """ + + # FIXME: We initialize this here in order to engage the stupid windows hacks :). + # We can't reuse an existing scm object because the specific directories may + # be part of other checkouts. + self._port.host.initialize_scm() scm = SCMDetector(self._filesystem, self._executive).detect_scm_system(in_directory) if scm: return scm.svn_revision(in_directory) diff --git a/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py b/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py index 42b518f7f..b48c5b933 100644 --- a/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py +++ b/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py @@ -155,7 +155,7 @@ class TestExpectationParser(object): else: parsed_specifiers.add(modifier) - if not expectation_line.parsed_bug_modifiers and not has_wontfix and not has_bugid: + if not expectation_line.parsed_bug_modifiers and not has_wontfix and not has_bugid and self._port.warn_if_bug_missing_in_test_expectations(): expectation_line.warnings.append(self.MISSING_BUG_WARNING) if self._allow_rebaseline_modifier and self.REBASELINE_MODIFIER in modifiers: @@ -869,7 +869,7 @@ class TestExpectations(object): return self._model def get_rebaselining_failures(self): - return self._model.get_test_set(REBASELINE, IMAGE) | self._model.get_test_set(REBASELINE, FAIL) + return self._model.get_test_set(REBASELINE) # FIXME: Change the callsites to use TestExpectationsModel and remove. def get_expectations(self, test): @@ -970,7 +970,7 @@ class TestExpectations(object): expectation.name in except_these_tests and 'rebaseline' in expectation.parsed_modifiers)) - return self.list_to_string(filter(without_rebaseline_modifier, self._expectations)) + return self.list_to_string(filter(without_rebaseline_modifier, self._expectations), reconstitute_only_these=[]) def _add_expectations(self, expectation_list): for expectation_line in expectation_list: diff --git a/Tools/Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py b/Tools/Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py index c5606071d..c3fc02658 100644 --- a/Tools/Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py +++ b/Tools/Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py @@ -179,8 +179,7 @@ class MiscTests(Base): "Bug(rniwa) disabled-test.html-disabled [ ImageOnlyFailure ]", is_lint_mode=True) self.assertFalse(True, "ParseError wasn't raised") except ParseError, e: - warnings = ("expectations:1 Test lacks BUG modifier. failures/expected/text.html\n" - "expectations:1 Unrecognized modifier 'foo' failures/expected/text.html\n" + warnings = ("expectations:1 Unrecognized modifier 'foo' failures/expected/text.html\n" "expectations:2 Path does not exist. non-existent-test.html") self.assertEqual(str(e), warnings) @@ -372,7 +371,9 @@ class SemanticTests(Base): def test_missing_bugid(self): self.parse_exp('failures/expected/text.html [ Failure ]') - self.assertTrue(self._exp.has_warnings()) + self.assertFalse(self._exp.has_warnings()) + + self._port.warn_if_bug_missing_in_test_expectations = lambda: True self.parse_exp('failures/expected/text.html [ Failure ]') line = self._exp._model.get_expectation_line('failures/expected/text.html') @@ -512,8 +513,22 @@ class RebaseliningTest(Base): 'Bug(z) failures/expected/crash.html [ Crash ]\n', 'Bug(x0) failures/expected/image.html [ Crash ]\n') + # Ensure that we don't modify unrelated lines, even if we could rewrite them. + # i.e., the second line doesn't get rewritten to "Bug(y) failures/expected/skip.html" + self.assertRemove('Bug(x) failures/expected/text.html [ Failure Rebaseline ]\n' + 'Bug(Y) failures/expected/image.html [ Skip ]\n' + 'Bug(z) failures/expected/crash.html\n', + '', + ['failures/expected/text.html'], + 'Bug(Y) failures/expected/image.html [ Skip ]\n' + 'Bug(z) failures/expected/crash.html\n', + '') + + def test_get_rebaselining_failures(self): + # Make sure we find a test as needing a rebaseline even if it is not marked as a failure. + self.parse_exp('Bug(x) failures/expected/text.html [ Rebaseline ]\n') + self.assertEqual(len(self._exp.get_rebaselining_failures()), 1) - def test_no_get_rebaselining_failures(self): self.parse_exp(self.get_basic_expectations()) self.assertEqual(len(self._exp.get_rebaselining_failures()), 0) diff --git a/Tools/Scripts/webkitpy/layout_tests/port/base.py b/Tools/Scripts/webkitpy/layout_tests/port/base.py index cd57032e9..ae55c684d 100755 --- a/Tools/Scripts/webkitpy/layout_tests/port/base.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/base.py @@ -182,6 +182,10 @@ class Port(object): """Return the number of DumpRenderTree instances to use for this port.""" return self._executive.cpu_count() + 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 @@ -870,6 +874,7 @@ class Port(object): # Most ports (?): 'WEBKIT_TESTFONTS', + 'WEBKITOUTPUTDIR', ] for variable in variables_to_copy: self._copy_value_from_environ_if_set(clean_env, variable) @@ -997,6 +1002,9 @@ class Port(object): # some ports have Skipped files which are returned as part of test_expectations(). return self._filesystem.exists(self.path_to_test_expectations_file()) + def warn_if_bug_missing_in_test_expectations(self): + return False + def expectations_dict(self): """Returns an OrderedDict of name -> expectations strings. The names are expected to be (but not required to be) paths in the filesystem. @@ -1162,9 +1170,18 @@ class Port(object): return "apache2-httpd.conf" def _path_to_apache_config_file(self): - """Returns the full path to the apache binary. + """Returns the full path to the apache configuration file. + + If the WEBKIT_HTTP_SERVER_CONF_PATH environment variable is set, its + contents will be used instead. This is needed only by ports that use the apache_http_server module.""" + config_file_from_env = os.environ.get('WEBKIT_HTTP_SERVER_CONF_PATH') + if config_file_from_env: + if not self._filesystem.exists(config_file_from_env): + raise IOError('%s was not found on the system' % config_file_from_env) + return config_file_from_env + 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) @@ -1223,11 +1240,15 @@ class Port(object): This is needed only by ports that use the http_server.py module.""" raise NotImplementedError('Port._path_to_lighttpd_php') + @memoized def _path_to_wdiff(self): """Returns the full path to the wdiff binary, or None if it is not available. This is likely used only by wdiff_text()""" - return 'wdiff' + for path in ("/usr/bin/wdiff", "/usr/bin/dwdiff"): + if self._filesystem.exists(path): + return path + return None def _webkit_baseline_path(self, platform): """Return the full path to the top of the baseline tree for a @@ -1275,7 +1296,7 @@ class Port(object): base_tests = self._real_tests([suite.base]) suite.tests = {} for test in base_tests: - suite.tests[test.replace(suite.base, suite.name)] = test + suite.tests[test.replace(suite.base, suite.name, 1)] = test return suites def _virtual_tests(self, paths, suites): @@ -1292,7 +1313,7 @@ class Port(object): def lookup_virtual_test_base(self, test_name): for suite in self.populated_virtual_test_suites(): if test_name.startswith(suite.name): - return test_name.replace(suite.name, suite.base) + return test_name.replace(suite.name, suite.base, 1) return None def lookup_virtual_test_args(self, test_name): diff --git a/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py index f6a6eb01f..e9b2f060d 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py @@ -437,11 +437,15 @@ class PortTest(unittest.TestCase): tests = port.tests(['passes']) self.assertTrue('passes/text.html' in tests) + self.assertTrue('passes/passes/test-virtual-passes.html' in tests) self.assertFalse('virtual/passes/text.html' in tests) tests = port.tests(['virtual/passes']) self.assertFalse('passes/text.html' in tests) - self.assertTrue('virtual/passes/text.html' in tests) + self.assertTrue('virtual/passes/test-virtual-passes.html' in tests) + self.assertTrue('virtual/passes/passes/test-virtual-passes.html' in tests) + self.assertFalse('virtual/passes/test-virtual-virtual/passes.html' in tests) + self.assertFalse('virtual/passes/virtual/passes/test-virtual-passes.html' in tests) def test_build_path(self): port = self.make_port(options=optparse.Values({'build_directory': '/my-build-directory/'})) diff --git a/Tools/Scripts/webkitpy/layout_tests/port/builders.py b/Tools/Scripts/webkitpy/layout_tests/port/builders.py index 3d03fc7af..605b8cccc 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/builders.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/builders.py @@ -37,16 +37,16 @@ from webkitpy.common.memoized import memoized # * specifiers -- a set of specifiers, representing configurations covered by this builder. _exact_matches = { # These builders are on build.chromium.org. - "Webkit Win": {"port_name": "chromium-win-xp", "specifiers": set(["xp", "release"])}, - "Webkit Win7": {"port_name": "chromium-win-win7", "specifiers": set(["win7"])}, - "Webkit Win (dbg)(1)": {"port_name": "chromium-win-xp", "specifiers": set(["win", "debug"])}, - "Webkit Win (dbg)(2)": {"port_name": "chromium-win-xp", "specifiers": set(["win", "debug"])}, - "Webkit Linux": {"port_name": "chromium-linux-x86_64", "specifiers": set(["linux", "x86_64", "release"])}, - "Webkit Linux 32": {"port_name": "chromium-linux-x86", "specifiers": set(["linux", "x86"])}, - "Webkit Linux (dbg)": {"port_name": "chromium-linux-x86_64", "specifiers": set(["linux", "debug"])}, - "Webkit Mac10.6": {"port_name": "chromium-mac-snowleopard", "specifiers": set(["snowleopard"])}, - "Webkit Mac10.6 (dbg)": {"port_name": "chromium-mac-snowleopard", "specifiers": set(["snowleopard", "debug"])}, - "Webkit Mac10.7": {"port_name": "chromium-mac-lion", "specifiers": set(["lion"])}, + "WebKit XP": {"port_name": "chromium-win-xp", "specifiers": set(["xp", "release"])}, + "WebKit Win7": {"port_name": "chromium-win-win7", "specifiers": set(["win7"])}, + "WebKit Win7 (dbg)(1)": {"port_name": "chromium-win-xp", "specifiers": set(["win", "debug"])}, + "WebKit Win7 (dbg)(2)": {"port_name": "chromium-win-xp", "specifiers": set(["win", "debug"])}, + "WebKit Linux": {"port_name": "chromium-linux-x86_64", "specifiers": set(["linux", "x86_64", "release"])}, + "WebKit Linux 32": {"port_name": "chromium-linux-x86", "specifiers": set(["linux", "x86"])}, + "WebKit Linux (dbg)": {"port_name": "chromium-linux-x86_64", "specifiers": set(["linux", "debug"])}, + "WebKit Mac10.6": {"port_name": "chromium-mac-snowleopard", "specifiers": set(["snowleopard"])}, + "WebKit Mac10.6 (dbg)": {"port_name": "chromium-mac-snowleopard", "specifiers": set(["snowleopard", "debug"])}, + "WebKit Mac10.7": {"port_name": "chromium-mac-lion", "specifiers": set(["lion"])}, # These builders are on build.webkit.org. "Apple MountainLion Release WK1 (Tests)": {"port_name": "mac-mountainlion", "specifiers": set(["mountainlion"]), "rebaseline_override_dir": "mac"}, @@ -92,6 +92,8 @@ _ports_without_builders = [ "qt-mac", "qt-win", "qt-wk2", + # FIXME: Move to _extact_matches. + "chromium-android", ] diff --git a/Tools/Scripts/webkitpy/layout_tests/port/chromium.py b/Tools/Scripts/webkitpy/layout_tests/port/chromium.py index 44c98a383..c310eb15b 100755 --- a/Tools/Scripts/webkitpy/layout_tests/port/chromium.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/chromium.py @@ -53,6 +53,7 @@ class ChromiumPort(Port): ALL_SYSTEMS = ( ('snowleopard', 'x86'), ('lion', 'x86'), + ('mountainlion', 'x86'), ('xp', 'x86'), ('win7', 'x86'), ('lucid', 'x86'), @@ -62,13 +63,13 @@ class ChromiumPort(Port): ('icecreamsandwich', 'x86')) ALL_BASELINE_VARIANTS = [ - 'chromium-mac-lion', 'chromium-mac-snowleopard', 'chromium-mac-leopard', + 'chromium-mac-mountainlion', 'chromium-mac-lion', 'chromium-mac-snowleopard', 'chromium-win-win7', 'chromium-win-xp', 'chromium-linux-x86_64', 'chromium-linux-x86', ] CONFIGURATION_SPECIFIER_MACROS = { - 'mac': ['snowleopard', 'lion'], + 'mac': ['snowleopard', 'lion', 'mountainlion'], 'win': ['xp', 'win7'], 'linux': ['lucid'], 'android': ['icecreamsandwich'], @@ -111,6 +112,13 @@ class ChromiumPort(Port): 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 @@ -332,13 +340,16 @@ class ChromiumPort(Port): 'win_layout_rel', ]) + def warn_if_bug_missing_in_test_expectations(self): + return True + def expectations_files(self): paths = [self.path_to_test_expectations_file()] skia_expectations_path = self.path_from_chromium_base('skia', 'skia_test_expectations.txt') + # FIXME: we should probably warn if this file is missing in some situations. + # See the discussion in webkit.org/b/97699. if self._filesystem.exists(skia_expectations_path): paths.append(skia_expectations_path) - else: - _log.warning("Using the chromium port without having the downstream skia_test_expectations.txt file checked out. Expectations related things might be wonky.") 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: @@ -376,6 +387,9 @@ class ChromiumPort(Port): VirtualTestSuite('platform/chromium/virtual/gpu/fast/hidpi', 'fast/hidpi', ['--force-compositing-mode']), + VirtualTestSuite('platform/chromium/virtual/softwarecompositing', + 'compositing', + ['--enable-software-compositing']), ] # diff --git a/Tools/Scripts/webkitpy/layout_tests/port/chromium_android.py b/Tools/Scripts/webkitpy/layout_tests/port/chromium_android.py index 3dfeab7a3..e246b8870 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/chromium_android.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/chromium_android.py @@ -77,45 +77,51 @@ TEST_PATH_PREFIX = '/all-tests' FORWARD_PORTS = '8000 8080 8443 8880 9323' MS_TRUETYPE_FONTS_DIR = '/usr/share/fonts/truetype/msttcorefonts/' +MS_TRUETYPE_FONTS_PACKAGE = 'ttf-mscorefonts-installer' # Timeout in seconds to wait for start/stop of DumpRenderTree. DRT_START_STOP_TIMEOUT_SECS = 10 -# List of fonts that layout tests expect, copied from DumpRenderTree/gtk/TestShellX11.cpp. +# List of fonts that layout tests expect, copied from DumpRenderTree/chromium/TestShellX11.cpp. HOST_FONT_FILES = [ - [MS_TRUETYPE_FONTS_DIR, 'Arial.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Arial_Bold.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Arial_Bold_Italic.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Arial_Italic.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Comic_Sans_MS.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Comic_Sans_MS_Bold.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Courier_New.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Courier_New_Bold.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Courier_New_Bold_Italic.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Courier_New_Italic.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Georgia.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Georgia_Bold.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Georgia_Bold_Italic.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Georgia_Italic.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Impact.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS_Bold.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS_Bold_Italic.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS_Italic.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman_Bold.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman_Bold_Italic.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman_Italic.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Verdana.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Verdana_Bold.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Verdana_Bold_Italic.ttf'], - [MS_TRUETYPE_FONTS_DIR, 'Verdana_Italic.ttf'], + [[MS_TRUETYPE_FONTS_DIR], 'Arial.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Arial_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Arial_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Arial_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Comic_Sans_MS.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Comic_Sans_MS_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Courier_New.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Courier_New_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Courier_New_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Courier_New_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Georgia.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Georgia_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Georgia_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Georgia_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Impact.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Verdana.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Verdana_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Verdana_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE], + [[MS_TRUETYPE_FONTS_DIR], 'Verdana_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE], # The Microsoft font EULA - ['/usr/share/doc/ttf-mscorefonts-installer/', 'READ_ME!.gz'], - ['/usr/share/fonts/truetype/ttf-dejavu/', 'DejaVuSans.ttf'], + [['/usr/share/doc/ttf-mscorefonts-installer/'], 'READ_ME!.gz', MS_TRUETYPE_FONTS_PACKAGE], + # Other fonts: Arabic, CJK, Indic, Thai, etc. + [['/usr/share/fonts/truetype/ttf-dejavu/'], 'DejaVuSans.ttf', 'ttf-dejavu'], + [['/usr/share/fonts/truetype/kochi/'], 'kochi-mincho.ttf', 'ttf-kochi-mincho'], + [['/usr/share/fonts/truetype/ttf-indic-fonts-core/'], 'lohit_hi.ttf', 'ttf-indic-fonts-core'], + [['/usr/share/fonts/truetype/ttf-indic-fonts-core/'], 'lohit_ta.ttf', 'ttf-indic-fonts-core'], + [['/usr/share/fonts/truetype/ttf-indic-fonts-core/'], 'MuktiNarrow.ttf', 'ttf-indic-fonts-core'], + [['/usr/share/fonts/truetype/thai/', '/usr/share/fonts/truetype/tlwg/'], 'Garuda.ttf', 'fonts-tlwg-garuda'], + [['/usr/share/fonts/truetype/ttf-indic-fonts-core/', '/usr/share/fonts/truetype/ttf-punjabi-fonts/'], 'lohit_pa.ttf', 'ttf-indic-fonts-core'], ] -# Should increase this version after changing HOST_FONT_FILES. -FONT_FILES_VERSION = 2 DEVICE_FONTS_DIR = DEVICE_DRT_DIR + 'fonts/' @@ -198,7 +204,8 @@ class ChromiumAndroidPort(chromium.ChromiumPort): def check_build(self, needs_http): result = super(ChromiumAndroidPort, self).check_build(needs_http) - result = self.check_wdiff() and result + result = self._check_file_exists(self._path_to_md5sum(), 'md5sum utility') and result + result = self._check_file_exists(self._path_to_forwarder(), 'forwarder utility') and result if not result: _log.error('For complete Android build requirements, please see:') _log.error('') @@ -207,11 +214,15 @@ class ChromiumAndroidPort(chromium.ChromiumPort): return result def check_sys_deps(self, needs_http): - for (font_dir, font_file) in HOST_FONT_FILES: - font_path = font_dir + font_file - if not self._check_file_exists(font_path, 'font file'): - _log.error('You are missing %s. Try installing msttcorefonts. ' - 'See build instructions.' % font_path) + for (font_dirs, font_file, package) in HOST_FONT_FILES: + exists = False + for font_dir in font_dirs: + font_path = font_dir + font_file + if self._check_file_exists(font_path, '', logging=False): + exists = True + 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 @@ -271,6 +282,9 @@ class ChromiumAndroidPort(chromium.ChromiumPort): def _path_to_forwarder(self): return self._build_path('forwarder') + def _path_to_md5sum(self): + return self._build_path(MD5SUM_DEVICE_FILE_NAME) + def _path_to_image_diff(self): return self._host_port._path_to_image_diff() @@ -330,12 +344,10 @@ class ChromiumAndroidDriver(driver.Driver): super(ChromiumAndroidDriver, self).__del__() def _setup_md5sum_and_push_data_if_needed(self): - self._md5sum_path = self._port._build_path_with_configuration(self._port.get_option('configuration'), MD5SUM_DEVICE_FILE_NAME) - assert os.path.exists(self._md5sum_path) - + self._md5sum_path = self._port._path_to_md5sum() if not self._file_exists_on_device(MD5SUM_DEVICE_PATH): if not self._push_to_device(self._md5sum_path, MD5SUM_DEVICE_PATH): - _log.error('Could not push md5sum to device') + raise AssertionError('Could not push md5sum to device') self._push_executable() self._push_fonts() @@ -401,31 +413,20 @@ class ChromiumAndroidDriver(driver.Driver): self._abort('Failed to install %s onto device: %s' % (drt_host_path, install_result)) def _push_fonts(self): - if not self._check_version(DEVICE_FONTS_DIR, FONT_FILES_VERSION): - self._log_debug('Pushing fonts') - path_to_ahem_font = self._port._build_path('AHEM____.TTF') - self._push_to_device(path_to_ahem_font, DEVICE_FONTS_DIR + 'AHEM____.TTF') - for (host_dir, font_file) in HOST_FONT_FILES: - self._push_to_device(host_dir + font_file, DEVICE_FONTS_DIR + font_file) - self._link_device_file('/system/fonts/DroidSansFallback.ttf', DEVICE_FONTS_DIR + 'DroidSansFallback.ttf') - self._update_version(DEVICE_FONTS_DIR, FONT_FILES_VERSION) + self._log_debug('Pushing fonts') + path_to_ahem_font = self._port._build_path('AHEM____.TTF') + self._push_file_if_needed(path_to_ahem_font, DEVICE_FONTS_DIR + 'AHEM____.TTF') + for (host_dirs, font_file, package) in HOST_FONT_FILES: + for host_dir in host_dirs: + host_font_path = host_dir + font_file + if self._port._check_file_exists(host_font_path, '', logging=False): + self._push_file_if_needed(host_font_path, DEVICE_FONTS_DIR + font_file) def _push_test_resources(self): self._log_debug('Pushing test resources') for resource in TEST_RESOURCES_TO_PUSH: self._push_file_if_needed(self._port.layout_tests_dir() + '/' + resource, DEVICE_LAYOUT_TESTS_DIR + resource) - def _check_version(self, dir, version): - assert(dir.endswith('/')) - try: - device_version = int(self._run_adb_command(['shell', 'cat %sVERSION || echo 0' % dir])) - return device_version == version - except: - return False - - def _update_version(self, dir, version): - self._run_adb_command(['shell', 'echo %d > %sVERSION' % (version, dir)]) - def _run_adb_command(self, cmd, ignore_error=False): self._log_debug('Run adb command: ' + str(cmd)) if ignore_error: diff --git a/Tools/Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py index 2ffb77979..bb4229e65 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py @@ -30,6 +30,7 @@ import optparse import StringIO import time import unittest +import sys from webkitpy.common.system import executive_mock from webkitpy.common.system.executive_mock import MockExecutive2 @@ -244,7 +245,8 @@ class ChromiumAndroidDriverTest(unittest.TestCase): def test_command_from_driver_input(self): driver_input = driver.DriverInput('foo/bar/test.html', 10, 'checksum', True) expected_command = "/data/local/tmp/third_party/WebKit/LayoutTests/foo/bar/test.html'--pixel-test'checksum\n" - self.assertEquals(self.driver._command_from_driver_input(driver_input), expected_command) + if (sys.platform != "cygwin"): + self.assertEquals(self.driver._command_from_driver_input(driver_input), expected_command) driver_input = driver.DriverInput('http/tests/foo/bar/test.html', 10, 'checksum', True) expected_command = "http://127.0.0.1:8000/foo/bar/test.html'--pixel-test'checksum\n" diff --git a/Tools/Scripts/webkitpy/layout_tests/port/chromium_linux.py b/Tools/Scripts/webkitpy/layout_tests/port/chromium_linux.py index fa8c274ea..a2252c1b3 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/chromium_linux.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/chromium_linux.py @@ -155,14 +155,6 @@ class ChromiumLinuxPort(chromium.ChromiumPort): else: return '/usr/sbin/apache2' - def _path_to_apache_config_file(self): - if self._is_redhat_based(): - config_name = 'fedora-httpd.conf' - else: - config_name = 'apache2-debian-httpd.conf' - - return self._filesystem.join(self.layout_tests_dir(), 'http', 'conf', config_name) - def _path_to_lighttpd(self): return "/usr/sbin/lighttpd" @@ -178,12 +170,3 @@ class ChromiumLinuxPort(chromium.ChromiumPort): def _path_to_helper(self): return None - - def _path_to_wdiff(self): - if self._is_redhat_based(): - return '/usr/bin/dwdiff' - else: - return '/usr/bin/wdiff' - - def _is_redhat_based(self): - return self._filesystem.exists(self._filesystem.join('/etc', 'redhat-release')) diff --git a/Tools/Scripts/webkitpy/layout_tests/port/chromium_mac.py b/Tools/Scripts/webkitpy/layout_tests/port/chromium_mac.py index db82d5c79..08c1ede0f 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/chromium_mac.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/chromium_mac.py @@ -94,6 +94,14 @@ class ChromiumMacPort(chromium.ChromiumPort): def operating_system(self): return 'mac' + def expectations_files(self): + # FIXME: This is a temporary hack while getting the 10.8 baselines up to date. + # See https://bugs.webkit.org/show_bug.cgi?id=99505 + files = super(ChromiumMacPort, self).expectations_files() + if self.name() == 'chromium-mac-mountainlion': + files.append(self._filesystem.join(self._webkit_baseline_path(self.name()), 'TestExpectations')) + return files + # # PROTECTED METHODS # diff --git a/Tools/Scripts/webkitpy/layout_tests/port/chromium_mac_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/chromium_mac_unittest.py index edf92ea20..14e5a2b84 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/chromium_mac_unittest.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/chromium_mac_unittest.py @@ -95,6 +95,9 @@ class ChromiumMacPortTest(chromium_port_testcase.ChromiumPortTestCase): def test_path_to_image_diff(self): self.assertEquals(self.make_port()._path_to_image_diff(), '/mock-checkout/out/Release/ImageDiff') + def test_ml_expectations(self): + self.assertTrue(self.make_port(port_name='chromium-mac-mountainlion').expectations_files()[-1].endswith('-mountainlion/TestExpectations')) + self.assertFalse(self.make_port(port_name='chromium-mac-lion').expectations_files()[-1].endswith('-mountainlion/TestExpectations')) if __name__ == '__main__': port_testcase.main() diff --git a/Tools/Scripts/webkitpy/layout_tests/port/chromium_port_testcase.py b/Tools/Scripts/webkitpy/layout_tests/port/chromium_port_testcase.py index fb90d1b9b..a082a131c 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/chromium_port_testcase.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/chromium_port_testcase.py @@ -48,6 +48,13 @@ class ChromiumPortTestCase(port_testcase.PortTestCase): port = self.make_port() port.check_build(needs_http=True) + def test_default_max_locked_shards(self): + port = self.make_port() + port.default_child_processes = lambda: 16 + self.assertEquals(port.default_max_locked_shards(), 4) + port.default_child_processes = lambda: 2 + self.assertEquals(port.default_max_locked_shards(), 1) + def test_default_timeout_ms(self): self.assertEquals(self.make_port(options=MockOptions(configuration='Release')).default_timeout_ms(), 6000) self.assertEquals(self.make_port(options=MockOptions(configuration='Debug')).default_timeout_ms(), 12000) @@ -72,6 +79,8 @@ class ChromiumPortTestCase(port_testcase.PortTestCase): TestConfiguration('snowleopard', 'x86', 'release'), TestConfiguration('lion', 'x86', 'debug'), TestConfiguration('lion', 'x86', 'release'), + TestConfiguration('mountainlion', 'x86', 'debug'), + TestConfiguration('mountainlion', 'x86', 'release'), TestConfiguration('xp', 'x86', 'debug'), TestConfiguration('xp', 'x86', 'release'), TestConfiguration('win7', 'x86', 'debug'), diff --git a/Tools/Scripts/webkitpy/layout_tests/port/driver.py b/Tools/Scripts/webkitpy/layout_tests/port/driver.py index 4bf53d46a..7993d0577 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/driver.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/driver.py @@ -76,7 +76,7 @@ class DriverOutput(object): strip_patterns.append((re.compile('scrolled to [0-9]+,[0-9]+'), 'scrolled')) def __init__(self, text, image, image_hash, audio, crash=False, - test_time=0, timeout=False, error='', crashed_process_name='??', + test_time=0, measurements=None, timeout=False, error='', crashed_process_name='??', crashed_pid=None, crash_log=None): # FIXME: Args could be renamed to better clarify what they do. self.text = text @@ -89,6 +89,7 @@ class DriverOutput(object): self.crashed_pid = crashed_pid self.crash_log = crash_log self.test_time = test_time + self.measurements = measurements self.timeout = timeout self.error = error # stderr output @@ -138,6 +139,8 @@ class Driver(object): self.err_seen_eof = False self._server_process = None + self._measurements = {} + def __del__(self): self.stop() @@ -193,7 +196,7 @@ class Driver(object): crash_log += '\nstdout:\n%s\nstderr:\n%s\n' % (text, self.error_from_test) return DriverOutput(text, image, actual_image_hash, audio, - crash=crashed, test_time=time.time() - test_begin_time, + crash=crashed, test_time=time.time() - test_begin_time, measurements=self._measurements, timeout=timed_out, error=self.error_from_test, crashed_process_name=self._crashed_process_name, crashed_pid=self._crashed_pid, crash_log=crash_log) @@ -360,6 +363,10 @@ class Driver(object): def _read_first_block(self, deadline): # returns (text_content, audio_content) block = self._read_block(deadline) + if block.malloc: + self._measurements['Malloc'] = float(block.malloc) + if block.js_heap: + self._measurements['JSHeap'] = float(block.js_heap) if block.content_type == 'audio/wav': return (None, block.decoded_content) return (block.decoded_content, None) @@ -384,7 +391,9 @@ class Driver(object): if (self._read_header(block, line, 'Content-Type: ', 'content_type') or self._read_header(block, line, 'Content-Transfer-Encoding: ', 'encoding') or self._read_header(block, line, 'Content-Length: ', '_content_length', int) - or self._read_header(block, line, 'ActualHash: ', 'content_hash')): + or self._read_header(block, line, 'ActualHash: ', 'content_hash') + or self._read_header(block, line, 'DumpMalloc: ', 'malloc') + or self._read_header(block, line, 'DumpJSHeap: ', 'js_heap')): return # Note, we're not reading ExpectedHash: here, but we could. # If the line wasn't a header, we just append it to the content. @@ -450,6 +459,8 @@ class ContentBlock(object): # Content is treated as binary data even though the text output is usually UTF-8. self.content = str() # FIXME: Should be bytearray() once we require Python 2.6. self.decoded_content = None + self.malloc = None + self.js_heap = None def decode_content(self): if self.encoding == 'base64' and self.content is not None: diff --git a/Tools/Scripts/webkitpy/layout_tests/port/efl.py b/Tools/Scripts/webkitpy/layout_tests/port/efl.py index 269146aed..1022cd7b7 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/efl.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/efl.py @@ -32,7 +32,7 @@ import os from webkitpy.layout_tests.models.test_configuration import TestConfiguration from webkitpy.layout_tests.port.base import Port from webkitpy.layout_tests.port.pulseaudio_sanitizer import PulseAudioSanitizer - +from webkitpy.layout_tests.port.xvfbdriver import XvfbDriver class EflPort(Port, PulseAudioSanitizer): port_name = 'efl' @@ -80,6 +80,9 @@ class EflPort(Port, PulseAudioSanitizer): def _generate_all_test_configurations(self): return [TestConfiguration(version=self._version, architecture='x86', build_type=build_type) for build_type in self.ALL_BUILD_TYPES] + def _driver_class(self): + return XvfbDriver + def _path_to_driver(self): return self._build_path('bin', self.driver_name()) @@ -98,18 +101,22 @@ class EflPort(Port, PulseAudioSanitizer): search_paths = [] if self.get_option('webkit_test_runner'): search_paths.append(self.port_name + '-wk2') + search_paths.append('wk2') else: search_paths.append(self.port_name + '-wk1') search_paths.append(self.port_name) return search_paths def expectations_files(self): + # FIXME: We should be able to use the default algorithm here. return list(reversed([self._filesystem.join(self._webkit_baseline_path(p), 'TestExpectations') for p in self._search_paths()])) def show_results_html_file(self, results_filename): # FIXME: We should find a way to share this implmentation with Gtk, # or teach run-launcher how to call run-safari and move this down to WebKitPort. run_launcher_args = ["file://%s" % results_filename] + if self.get_option('webkit_test_runner'): + run_launcher_args.append('-2') # FIXME: old-run-webkit-tests also added ["-graphicssystem", "raster", "-style", "windows"] # FIXME: old-run-webkit-tests converted results_filename path for cygwin. self._run_script("run-launcher", run_launcher_args) diff --git a/Tools/Scripts/webkitpy/layout_tests/port/factory_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/factory_unittest.py index bb077c40a..2980c2d23 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/factory_unittest.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/factory_unittest.py @@ -105,7 +105,7 @@ class FactoryTest(unittest.TestCase): self.assertRaises(NotImplementedError, factory.PortFactory(MockSystemHost(os_name='vms')).get) def test_get_from_builder_name(self): - self.assertEquals(factory.PortFactory(MockSystemHost()).get_from_builder_name('Webkit Mac10.7').name(), + self.assertEquals(factory.PortFactory(MockSystemHost()).get_from_builder_name('WebKit Mac10.7').name(), 'chromium-mac-lion') diff --git a/Tools/Scripts/webkitpy/layout_tests/port/gtk.py b/Tools/Scripts/webkitpy/layout_tests/port/gtk.py index f02d14819..6c57b5363 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/gtk.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/gtk.py @@ -41,6 +41,9 @@ class GtkPort(Port, PulseAudioSanitizer): def expectations_files(self): return [self._filesystem.join(self._webkit_baseline_path(d), 'TestExpectations') for d in self._skipped_file_search_paths()] + def warn_if_bug_missing_in_test_expectations(self): + return True + def _port_flag_for_scripts(self): return "--gtk" @@ -89,18 +92,6 @@ class GtkPort(Port, PulseAudioSanitizer): def _path_to_image_diff(self): return self._build_path('Programs', 'ImageDiff') - def _path_to_apache(self): - if self._is_redhat_based(): - return '/usr/sbin/httpd' - else: - return '/usr/sbin/apache2' - - def _path_to_wdiff(self): - if self._is_redhat_based(): - return '/usr/bin/dwdiff' - else: - return '/usr/bin/wdiff' - def _path_to_webcore_library(self): gtk_library_names = [ "libwebkitgtk-1.0.so", diff --git a/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py b/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py index 985241ede..f704a7a13 100755 --- a/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py @@ -30,6 +30,7 @@ import errno import logging +import os import socket import sys import time @@ -89,6 +90,13 @@ class PortTestCase(unittest.TestCase): port_name = self.port_maker.determine_full_port_name(host, options, port_name) return self.port_maker(host, port_name, options=options, config=config, **kwargs) + def test_default_max_locked_shards(self): + port = self.make_port() + port.default_child_processes = lambda: 16 + self.assertEquals(port.default_max_locked_shards(), 1) + port.default_child_processes = lambda: 2 + self.assertEquals(port.default_max_locked_shards(), 1) + def test_default_timeout_ms(self): self.assertEquals(self.make_port(options=MockOptions(configuration='Release')).default_timeout_ms(), 35000) self.assertEquals(self.make_port(options=MockOptions(configuration='Debug')).default_timeout_ms(), 35000) @@ -582,10 +590,29 @@ class PortTestCase(unittest.TestCase): def test_path_to_apache_config_file(self): port = TestWebKitPort() + + saved_environ = os.environ.copy() + try: + os.environ['WEBKIT_HTTP_SERVER_CONF_PATH'] = '/path/to/httpd.conf' + self.assertRaises(IOError, port._path_to_apache_config_file) + port._filesystem.write_text_file('/existing/httpd.conf', 'Hello, world!') + os.environ['WEBKIT_HTTP_SERVER_CONF_PATH'] = '/existing/httpd.conf' + self.assertEquals(port._path_to_apache_config_file(), '/existing/httpd.conf') + finally: + os.environ = saved_environ.copy() + # 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.assertEquals(port._path_to_apache_config_file(), '/mock-checkout/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() + try: + os.environ['WEBKIT_HTTP_SERVER_CONF_PATH'] = '/existing/httpd.conf' + self.assertEquals(port._path_to_apache_config_file(), '/existing/httpd.conf') + finally: + os.environ = saved_environ.copy() + def test_check_build(self): port = self.make_port(options=MockOptions(build=True)) self.build_called = False diff --git a/Tools/Scripts/webkitpy/layout_tests/port/qt.py b/Tools/Scripts/webkitpy/layout_tests/port/qt.py index 828a80d64..5b8342d9c 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/qt.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/qt.py @@ -88,9 +88,9 @@ class QtPort(Port): def _path_to_webcore_library(self): if self.operating_system() == 'mac': - return self._build_path('lib/QtWebKit.framework/QtWebKit') + return self._build_path('lib/QtWebKitWidgets.framework/QtWebKitWidgets') else: - return self._build_path('lib/libQtWebKit.so') + return self._build_path('lib/libQtWebKitWidgets.so') def _modules_to_search_for_symbols(self): # We search in every library to be reliable in the case of building with CONFIG+=force_static_libs_as_shared. @@ -135,23 +135,21 @@ class QtPort(Port): search_paths.append('qt-4.8') elif version: search_paths.append('qt-5.0') - search_paths.append(self.port_name + '-' + self.host.platform.os_name) + search_paths.append(self.port_name + '-' + self.operating_system()) search_paths.append(self.port_name) return search_paths def default_baseline_search_path(self): return map(self._webkit_baseline_path, self._search_paths()) - def _skipped_file_search_paths(self): - skipped_path = self._search_paths() - if self.get_option('webkit_test_runner') and '5.0' in self.qt_version(): - skipped_path.append('wk2') - return skipped_path - def expectations_files(self): + paths = self._search_paths() + if self.get_option('webkit_test_runner'): + paths.append('wk2') + # expectations_files() uses the directories listed in _search_paths reversed. # e.g. qt -> qt-linux -> qt-4.8 - return list(reversed([self._filesystem.join(self._webkit_baseline_path(p), 'TestExpectations') for p in self._search_paths()])) + return list(reversed([self._filesystem.join(self._webkit_baseline_path(p), 'TestExpectations') for p in paths])) def setup_environ_for_server(self, server_name=None): clean_env = super(QtPort, self).setup_environ_for_server(server_name) diff --git a/Tools/Scripts/webkitpy/layout_tests/port/qt_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/qt_unittest.py index 374b10270..cf09bd8e0 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/qt_unittest.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/qt_unittest.py @@ -71,14 +71,6 @@ class QtPortTest(port_testcase.PortTestCase): absolute_search_paths = map(port._webkit_baseline_path, search_paths) self.assertEquals(port.baseline_search_path(), absolute_search_paths) - def _assert_skipped_path(self, search_paths, os_name, use_webkit2=False, qt_version='4.8'): - host = MockSystemHost(os_name=os_name) - host.executive = MockExecutive2(self._qt_version(qt_version)) - port_name = 'qt-' + os_name - port = self.make_port(host=host, qt_version=qt_version, port_name=port_name, - options=MockOptions(webkit_test_runner=use_webkit2, platform='qt')) - self.assertEquals(port._skipped_file_search_paths(), search_paths) - def _assert_expectations_files(self, search_paths, os_name, use_webkit2=False, qt_version='4.8'): # FIXME: Port constructors should not "parse" the port name, but # rather be passed components (directly or via setters). Once @@ -100,19 +92,13 @@ class QtPortTest(port_testcase.PortTestCase): for case in self.search_paths_cases: self._assert_search_path(**case) - def test_skipped_file_search_path(self): - caselist = self.search_paths_cases[:] - for case in caselist: - if case['use_webkit2'] and case['qt_version'] == '5.0': - case['search_paths'].append("wk2") - self._assert_skipped_path(**case) - def test_expectations_files(self): for case in self.search_paths_cases: expectations_case = deepcopy(case) - expectations_case['search_paths'] = [] - for path in reversed(case['search_paths']): - expectations_case['search_paths'].append('/mock-checkout/LayoutTests/platform/%s/TestExpectations' % (path)) + if expectations_case['use_webkit2']: + expectations_case['search_paths'].append("wk2") + expectations_case['search_paths'].reverse() + expectations_case['search_paths'] = map(lambda path: '/mock-checkout/LayoutTests/platform/%s/TestExpectations' % (path), expectations_case['search_paths']) self._assert_expectations_files(**expectations_case) def test_show_results_html_file(self): diff --git a/Tools/Scripts/webkitpy/layout_tests/port/server_process.py b/Tools/Scripts/webkitpy/layout_tests/port/server_process.py index bfa6aab80..bfdf8301b 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/server_process.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/server_process.py @@ -84,6 +84,17 @@ class ServerProcess(object): return self._pid def _reset(self): + if getattr(self, '_proc', None): + if self._proc.stdin: + self._proc.stdin.close() + self._proc.stdin = None + if self._proc.stdout: + self._proc.stdout.close() + self._proc.stdout = None + if self._proc.stderr: + self._proc.stderr.close() + self._proc.stderr = None + self._proc = None self._output = str() # bytesarray() once we require Python 2.6 self._error = str() # bytesarray() once we require Python 2.6 @@ -321,8 +332,11 @@ class ServerProcess(object): now = time.time() self._proc.stdin.close() + self._proc.stdin = None + killed = False if not timeout_secs: self._kill() + killed = True elif not self._host.platform.is_win(): # FIXME: Why aren't we calling this on win? deadline = now + timeout_secs @@ -331,13 +345,15 @@ class ServerProcess(object): if self._proc.poll() is None: _log.warning('stopping %s timed out, killing it' % self._name) self._kill() + killed = True _log.warning('killed') # read any remaining data on the pipes and return it. - if self._use_win32_apis: - self._wait_for_data_and_update_buffers_using_win32_apis(now) - else: - self._wait_for_data_and_update_buffers_using_select(now, stopping=True) + if not killed: + if self._use_win32_apis: + self._wait_for_data_and_update_buffers_using_win32_apis(now) + else: + self._wait_for_data_and_update_buffers_using_select(now, stopping=True) out, err = self._output, self._error self._reset() return (out, err) diff --git a/Tools/Scripts/webkitpy/layout_tests/port/server_process_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/server_process_unittest.py index 48c41e6f2..7a5ac45d4 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/server_process_unittest.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/server_process_unittest.py @@ -57,6 +57,7 @@ class TrivialMockPort(object): class MockFile(object): def __init__(self, server_process): self._server_process = server_process + self.closed = False def fileno(self): return 1 @@ -66,7 +67,7 @@ class MockFile(object): raise IOError def close(self): - pass + self.closed = True class MockProc(object): @@ -87,6 +88,8 @@ class FakeServerProcess(server_process.ServerProcess): def _start(self): self._proc = MockProc(self) self.stdin = self._proc.stdin + self.stdout = self._proc.stdout + self.stderr = self._proc.stderr self._pid = self._proc.pid self.broken_pipes = [] @@ -121,6 +124,15 @@ class TestServerProcess(unittest.TestCase): proc.stop(0) + def test_cleanup(self): + port_obj = TrivialMockPort() + server_process = FakeServerProcess(port_obj=port_obj, name="test", cmd=["test"]) + server_process._start() + server_process.stop() + self.assertTrue(server_process.stdin.closed) + self.assertTrue(server_process.stdout.closed) + self.assertTrue(server_process.stderr.closed) + def test_broken_pipe(self): port_obj = TrivialMockPort() diff --git a/Tools/Scripts/webkitpy/layout_tests/port/test.py b/Tools/Scripts/webkitpy/layout_tests/port/test.py index d008d995d..726575614 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/test.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/test.py @@ -239,6 +239,11 @@ layer at (0,0) size 800x34 actual_image='image_not_in_pixeldir-pngtEXtchecksum\x00checksum_fail', expected_image='image_not_in_pixeldir-pngtEXtchecksum\x00checksum-png') + # For testing that virtual test suites don't expand names containing themselves + # See webkit.org/b/97925 and base_unittest.PortTest.test_tests(). + tests.add('passes/test-virtual-passes.html') + tests.add('passes/passes/test-virtual-passes.html') + return tests diff --git a/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver.py b/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver.py index 7e386a108..b927720db 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver.py @@ -28,41 +28,60 @@ import logging import os +import re +import time from webkitpy.layout_tests.port.server_process import ServerProcess from webkitpy.layout_tests.port.driver import Driver +from webkitpy.common.system.file_lock import FileLock _log = logging.getLogger(__name__) class XvfbDriver(Driver): - def _start(self, pixel_tests, per_test_args): - - # Collect the number of X servers running already and make - # sure our Xvfb process doesn't clash with any of them. - def x_filter(process_name): - return process_name.find("Xorg") > -1 + def __init__(self, *args, **kwargs): + Driver.__init__(self, *args, **kwargs) + self._guard_lock = None + self._startup_delay_secs = 1.0 - running_displays = len(self._port.host.executive.running_pids(x_filter)) + def _next_free_display(self): + running_pids = self._port.host.executive.run_command(['ps', '-eo', 'comm,command']) + reserved_screens = set() + for pid in running_pids.split('\n'): + match = re.match('(X|Xvfb|Xorg)\s+.*\s:(?P<screen_number>\d+)', pid) + if match: + reserved_screens.add(int(match.group('screen_number'))) + for i in range(99): + if i not in reserved_screens: + _guard_lock_file = self._port.host.filesystem.join('/tmp', 'WebKitXvfb.lock.%i' % i) + self._guard_lock = FileLock(_guard_lock_file) + if self._guard_lock.acquire_lock(): + return i + def _start(self, pixel_tests, per_test_args): # Use even displays for pixel tests and odd ones otherwise. When pixel tests are disabled, # DriverProxy creates two drivers, one for normal and the other for ref tests. Both have # the same worker number, so this prevents them from using the same Xvfb instance. - display_id = self._worker_number * 2 + running_displays - if pixel_tests: - display_id += 1 + display_id = self._next_free_display() self._lock_file = "/tmp/.X%d-lock" % display_id run_xvfb = ["Xvfb", ":%d" % display_id, "-screen", "0", "800x600x24", "-nolisten", "tcp"] with open(os.devnull, 'w') as devnull: self._xvfb_process = self._port.host.executive.popen(run_xvfb, stderr=devnull) + # Crashes intend to occur occasionally in the first few tests that are run through each + # worker because the Xvfb display isn't ready yet. Halting execution a bit should avoid that. + time.sleep(self._startup_delay_secs) + server_name = self._port.driver_name() environment = self._port.setup_environ_for_server(server_name) # We must do this here because the DISPLAY number depends on _worker_number environment['DISPLAY'] = ":%d" % display_id # Drivers should use separate application cache locations environment['XDG_CACHE_HOME'] = self._port.host.filesystem.join(self._port.results_directory(), '%s-appcache-%d' % (server_name, self._worker_number)) + self._driver_tempdir = self._port._filesystem.mkdtemp(prefix='%s-' % self._port.driver_name()) + environment['DUMPRENDERTREE_TEMP'] = str(self._driver_tempdir) + environment['LOCAL_RESOURCE_ROOT'] = self._port.layout_tests_dir() self._crashed_process_name = None self._crashed_pid = None @@ -71,6 +90,9 @@ class XvfbDriver(Driver): def stop(self): super(XvfbDriver, self).stop() + if self._guard_lock: + self._guard_lock.release_lock() + self._guard_lock = None if getattr(self, '_xvfb_process', None): self._port.host.executive.kill_process(self._xvfb_process.pid) self._xvfb_process = None diff --git a/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver_unittest.py index 37a2fbd4d..220dd3517 100644 --- a/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver_unittest.py +++ b/Tools/Scripts/webkitpy/layout_tests/port/xvfbdriver_unittest.py @@ -30,6 +30,7 @@ import unittest from webkitpy.common.system.deprecated_logging import log from webkitpy.common.system.filesystem_mock import MockFileSystem +from webkitpy.common.system.executive_mock import MockExecutive2 from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.common.system.systemhost_mock import MockSystemHost from webkitpy.layout_tests.port import Port @@ -39,13 +40,14 @@ from webkitpy.layout_tests.port.xvfbdriver import XvfbDriver class XvfbDriverTest(unittest.TestCase): - def make_driver(self, worker_number=0, xorg_running=False): - port = Port(host=MockSystemHost(log_executive=True), config=MockConfig()) + def make_driver(self, worker_number=0, xorg_running=False, executive=None): + port = Port(host=MockSystemHost(log_executive=True, executive=executive), config=MockConfig()) port._server_process_constructor = MockServerProcess if xorg_running: port._executive._running_pids['Xorg'] = 108 driver = XvfbDriver(port, worker_number=worker_number, pixel_tests=True) + driver._startup_delay_secs = 0 return driver def cleanup_driver(self, driver): @@ -61,26 +63,54 @@ class XvfbDriverTest(unittest.TestCase): def test_start_no_pixel_tests(self): driver = self.make_driver() - expected_stderr = "MOCK running_pids: []\nMOCK popen: ['Xvfb', ':0', '-screen', '0', '800x600x24', '-nolisten', 'tcp']\n" + expected_stderr = "MOCK run_command: ['ps', '-eo', 'comm,command'], cwd=None\nMOCK popen: ['Xvfb', ':0', '-screen', '0', '800x600x24', '-nolisten', 'tcp']\n" self.assertDriverStartSuccessful(driver, expected_stderr=expected_stderr, expected_display=":0") self.cleanup_driver(driver) def test_start_pixel_tests(self): driver = self.make_driver() - expected_stderr = "MOCK running_pids: []\nMOCK popen: ['Xvfb', ':1', '-screen', '0', '800x600x24', '-nolisten', 'tcp']\n" - self.assertDriverStartSuccessful(driver, expected_stderr=expected_stderr, expected_display=":1", pixel_tests=True) + expected_stderr = "MOCK run_command: ['ps', '-eo', 'comm,command'], cwd=None\nMOCK popen: ['Xvfb', ':0', '-screen', '0', '800x600x24', '-nolisten', 'tcp']\n" + self.assertDriverStartSuccessful(driver, expected_stderr=expected_stderr, expected_display=":0", pixel_tests=True) self.cleanup_driver(driver) def test_start_arbitrary_worker_number(self): driver = self.make_driver(worker_number=17) - expected_stderr = "MOCK running_pids: []\nMOCK popen: ['Xvfb', ':35', '-screen', '0', '800x600x24', '-nolisten', 'tcp']\n" - self.assertDriverStartSuccessful(driver, expected_stderr=expected_stderr, expected_display=":35", pixel_tests=True) + expected_stderr = "MOCK run_command: ['ps', '-eo', 'comm,command'], cwd=None\nMOCK popen: ['Xvfb', ':0', '-screen', '0', '800x600x24', '-nolisten', 'tcp']\n" + self.assertDriverStartSuccessful(driver, expected_stderr=expected_stderr, expected_display=":0", pixel_tests=True) self.cleanup_driver(driver) - def test_start_existing_xorg_process(self): - driver = self.make_driver(xorg_running=True) - expected_stderr = "MOCK running_pids: [108]\nMOCK popen: ['Xvfb', ':1', '-screen', '0', '800x600x24', '-nolisten', 'tcp']\n" - self.assertDriverStartSuccessful(driver, expected_stderr=expected_stderr, expected_display=":1") + def disabled_test_next_free_display(self): + output = "Xorg /usr/bin/X :0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch -background none\nXvfb Xvfb :1 -screen 0 800x600x24 -nolisten tcp" + executive = MockExecutive2(output) + driver = self.make_driver(executive=executive) + self.assertEqual(driver._next_free_display(), 2) + self.cleanup_driver(driver) + output = "X /usr/bin/X :0 vt7 -nolisten tcp -auth /var/run/xauth/A:0-8p7Ybb" + executive = MockExecutive2(output) + driver = self.make_driver(executive=executive) + self.assertEqual(driver._next_free_display(), 1) + self.cleanup_driver(driver) + output = "Xvfb Xvfb :0 -screen 0 800x600x24 -nolisten tcp" + executive = MockExecutive2(output) + driver = self.make_driver(executive=executive) + self.assertEqual(driver._next_free_display(), 1) + self.cleanup_driver(driver) + output = "Xvfb Xvfb :1 -screen 0 800x600x24 -nolisten tcp\nXvfb Xvfb :0 -screen 0 800x600x24 -nolisten tcp\nXvfb Xvfb :3 -screen 0 800x600x24 -nolisten tcp" + executive = MockExecutive2(output) + driver = self.make_driver(executive=executive) + self.assertEqual(driver._next_free_display(), 2) + self.cleanup_driver(driver) + + def test_start_next_worker(self): + driver = self.make_driver() + driver._next_free_display = lambda: 0 + expected_stderr = "MOCK popen: ['Xvfb', ':0', '-screen', '0', '800x600x24', '-nolisten', 'tcp']\n" + self.assertDriverStartSuccessful(driver, expected_stderr=expected_stderr, expected_display=":0", pixel_tests=True) + self.cleanup_driver(driver) + driver = self.make_driver() + driver._next_free_display = lambda: 3 + expected_stderr = "MOCK popen: ['Xvfb', ':3', '-screen', '0', '800x600x24', '-nolisten', 'tcp']\n" + self.assertDriverStartSuccessful(driver, expected_stderr=expected_stderr, expected_display=":3", pixel_tests=True) self.cleanup_driver(driver) def test_stop(self): diff --git a/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py b/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py index e784cb61d..89522079c 100755 --- a/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py +++ b/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py @@ -129,6 +129,9 @@ def _set_up_derived_options(port, options): if not options.child_processes: options.child_processes = os.environ.get("WEBKIT_TEST_CHILD_PROCESSES", str(port.default_child_processes())) + if not options.max_locked_shards: + options.max_locked_shards = int(os.environ.get("WEBKIT_TEST_MAX_LOCKED_SHARDS", + str(port.default_max_locked_shards()))) if not options.configuration: options.configuration = port.default_configuration() @@ -375,7 +378,11 @@ def parse_args(args=None): optparse.make_option("--test-list", action="append", help="read list of tests to run from file", metavar="FILE"), optparse.make_option("--skipped", action="store", default="default", - help="control how tests marked SKIP are run. 'default' == Skip, 'ignore' == Run them anyway, 'only' == only run the SKIP tests."), + help=("control how tests marked SKIP are run. " + "'default' == Skip tests unless explicitly listed on the command line, " + "'ignore' == Run them anyway, " + "'only' == only run the SKIP tests, " + "'always' == always skip, even if listed on the command line.")), optparse.make_option("--force", dest="skipped", action="store_const", const='ignore', help="Run all tests, even those marked SKIP in the test list (same as --skipped=ignore)"), optparse.make_option("--time-out-ms", @@ -412,7 +419,7 @@ def parse_args(args=None): optparse.make_option("--no-retry-failures", action="store_false", dest="retry_failures", help="Don't re-try any tests that produce unexpected results."), - optparse.make_option("--max-locked-shards", type="int", default=1, + optparse.make_option("--max-locked-shards", type="int", default=0, help="Set the maximum number of locked shards"), optparse.make_option("--additional-env-var", type="string", action="append", default=[], help="Passes that environment variable to the tests (--additional-env-var=NAME=VALUE)"), diff --git a/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py b/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py index 4afcc1466..85437449b 100755 --- a/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py +++ b/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py @@ -301,6 +301,12 @@ class MainTest(unittest.TestCase, StreamTestingMixin): for batch in batch_tests_run: self.assertTrue(len(batch) <= 2, '%s had too many tests' % ', '.join(batch)) + def test_max_locked_shards(self): + if not self.should_test_processes: + return + _, _, regular_output, _ = logging_run(['--debug-rwt-logging', '--child-processes', '2'], shared_port=False) + self.assertTrue(any(['(1 locked)' in line for line in regular_output.buflist])) + def test_child_processes_2(self): if self.should_test_processes: _, _, regular_output, _ = logging_run( @@ -310,7 +316,7 @@ class MainTest(unittest.TestCase, StreamTestingMixin): def test_child_processes_min(self): if self.should_test_processes: _, _, regular_output, _ = logging_run( - ['--debug-rwt-logging', '--child-processes', '2', 'passes'], + ['--debug-rwt-logging', '--child-processes', '2', '-i', 'passes/passes', 'passes'], tests_included=True, shared_port=False) self.assertTrue(any(['Running 1 ' in line for line in regular_output.buflist])) @@ -418,6 +424,10 @@ class MainTest(unittest.TestCase, StreamTestingMixin): self.assertEquals(get_tests_run(['--skipped=only', 'passes'], tests_included=True, flatten_batches=True), ['passes/skipped/skip.html']) + # Now check that we don't run anything. + self.assertEquals(get_tests_run(['--skipped=always', 'passes/skipped/skip.html'], tests_included=True, flatten_batches=True), + []) + def test_iterations(self): tests_to_run = ['passes/image.html', 'passes/text.html'] tests_run = get_tests_run(['--iterations', '2'] + tests_to_run, tests_included=True, flatten_batches=True) @@ -775,7 +785,7 @@ class MainTest(unittest.TestCase, StreamTestingMixin): # These next tests test that we run the tests in ascending alphabetical # order per directory. HTTP tests are sharded separately from other tests, # so we have to test both. - tests_run = get_tests_run(['passes'], tests_included=True, flatten_batches=True) + tests_run = get_tests_run(['-i', 'passes/passes', 'passes'], tests_included=True, flatten_batches=True) self.assertEquals(tests_run, sorted(tests_run)) tests_run = get_tests_run(['http/tests/passes'], tests_included=True, flatten_batches=True) @@ -922,6 +932,11 @@ class MainTest(unittest.TestCase, StreamTestingMixin): # child process (e.g., on win32) and we need to make sure that works and we still # see the verbose log output. However, we can't use logging_run() because using # outputcapture to capture stdout and stderr latter results in a nonpicklable host. + + # Test is flaky on Windows: https://bugs.webkit.org/show_bug.cgi?id=98559 + if not self.should_test_processes: + return + options, parsed_args = parse_args(['--verbose', '--fully-parallel', '--child-processes', '2', 'passes/text.html', 'passes/image.html'], tests_included=True, print_nothing=False) host = MockHost() port_obj = host.port_factory.get(port_name=options.platform, options=options) diff --git a/Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py b/Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py index 7cd4af538..a616fab5b 100644 --- a/Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py +++ b/Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py @@ -54,7 +54,6 @@ class LayoutTestApacheHttpd(http_server_base.HttpServerBase): self._name = 'httpd' self._mappings = [{'port': 8000}, {'port': 8080}, - {'port': 8081}, {'port': 8443, 'sslcert': True}] self._output_dir = output_dir self._filesystem.maybe_make_directory(output_dir) @@ -79,7 +78,6 @@ class LayoutTestApacheHttpd(http_server_base.HttpServerBase): '-c', "\'Alias /js-test-resources \"%s\"'" % js_test_resources_dir, '-c', "\'Alias /media-resources \"%s\"'" % media_resources_dir, '-C', "\'Listen %s\'" % "127.0.0.1:8000", - '-C', "\'Listen %s\'" % "127.0.0.1:8081", '-c', "\'TypesConfig \"%s\"\'" % mime_types_path, '-c', "\'CustomLog \"%s\" common\'" % access_log, '-c', "\'ErrorLog \"%s\"\'" % error_log, |
