1# Copyright (C) 2010 Google Inc. All rights reserved. 2# 3# Redistribution and use in source and binary forms, with or without 4# modification, are permitted provided that the following conditions are 5# met: 6# 7# * Redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer. 9# * Redistributions in binary form must reproduce the above 10# copyright notice, this list of conditions and the following disclaimer 11# in the documentation and/or other materials provided with the 12# distribution. 13# * Neither the name of Google Inc. nor the names of its 14# contributors may be used to endorse or promote products derived from 15# this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29import webkitpy.thirdparty.unittest2 as unittest 30 31from webkitpy.common.checkout.baselineoptimizer import BaselineOptimizer 32from webkitpy.common.checkout.scm.scm_mock import MockSCM 33from webkitpy.common.host_mock import MockHost 34from webkitpy.common.net.buildbot.buildbot_mock import MockBuilder 35from webkitpy.common.net.layouttestresults import LayoutTestResults 36from webkitpy.common.system.executive_mock import MockExecutive 37from webkitpy.common.system.executive_mock import MockExecutive2 38from webkitpy.common.system.outputcapture import OutputCapture 39from webkitpy.thirdparty.mock import Mock 40from webkitpy.tool.commands.rebaseline import * 41from webkitpy.tool.mocktool import MockTool, MockOptions 42 43 44class _BaseTestCase(unittest.TestCase): 45 MOCK_WEB_RESULT = 'MOCK Web result, convert 404 to None=True' 46 WEB_PREFIX = 'http://example.com/f/builders/WebKit Mac10.7/results/layout-test-results' 47 48 command_constructor = None 49 50 def setUp(self): 51 self.tool = MockTool() 52 self.command = self.command_constructor() # lint warns that command_constructor might not be set, but this is intentional; pylint: disable=E1102 53 self.command.bind_to_tool(self.tool) 54 self.lion_port = self.tool.port_factory.get_from_builder_name("WebKit Mac10.7") 55 self.lion_expectations_path = self.lion_port.path_to_generic_test_expectations_file() 56 57 # FIXME: crbug.com/279494. We should override builders._exact_matches 58 # here to point to a set of test ports and restore the value in 59 # tearDown(), and that way the individual tests wouldn't have to worry 60 # about it. 61 62 def _expand(self, path): 63 if self.tool.filesystem.isabs(path): 64 return path 65 return self.tool.filesystem.join(self.lion_port.layout_tests_dir(), path) 66 67 def _read(self, path): 68 return self.tool.filesystem.read_text_file(self._expand(path)) 69 70 def _write(self, path, contents): 71 self.tool.filesystem.write_text_file(self._expand(path), contents) 72 73 def _zero_out_test_expectations(self): 74 for port_name in self.tool.port_factory.all_port_names(): 75 port = self.tool.port_factory.get(port_name) 76 for path in port.expectations_files(): 77 self._write(path, '') 78 self.tool.filesystem.written_files = {} 79 80 def _setup_mock_builder_data(self): 81 data = LayoutTestResults.results_from_string("""ADD_RESULTS({ 82 "tests": { 83 "userscripts": { 84 "first-test.html": { 85 "expected": "PASS", 86 "actual": "IMAGE+TEXT" 87 }, 88 "second-test.html": { 89 "expected": "FAIL", 90 "actual": "IMAGE+TEXT" 91 } 92 } 93 } 94});""") 95 # FIXME: crbug.com/279494 - we shouldn't be mixing mock and real builder names. 96 for builder in ['MOCK builder', 'MOCK builder (Debug)', 'WebKit Mac10.7']: 97 self.command._builder_data[builder] = data 98 99 100class TestCopyExistingBaselinesInternal(_BaseTestCase): 101 command_constructor = CopyExistingBaselinesInternal 102 103 def setUp(self): 104 super(TestCopyExistingBaselinesInternal, self).setUp() 105 106 def test_copying_overwritten_baseline(self): 107 self.tool.executive = MockExecutive2() 108 109 # FIXME: crbug.com/279494. it's confusing that this is the test- port, and not the regular lion port. Really all of the tests should be using the test ports. 110 port = self.tool.port_factory.get('test-mac-snowleopard') 111 self._write(port._filesystem.join(port.layout_tests_dir(), 'platform/test-mac-snowleopard/failures/expected/image-expected.txt'), 'original snowleopard result') 112 113 old_exact_matches = builders._exact_matches 114 oc = OutputCapture() 115 try: 116 builders._exact_matches = { 117 "MOCK Leopard": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])}, 118 "MOCK SnowLeopard": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])}, 119 } 120 121 options = MockOptions(builder="MOCK SnowLeopard", suffixes="txt", verbose=True, test="failures/expected/image.html", results_directory=None) 122 123 oc.capture_output() 124 self.command.execute(options, [], self.tool) 125 finally: 126 out, _, _ = oc.restore_output() 127 builders._exact_matches = old_exact_matches 128 129 self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-mac-leopard/failures/expected/image-expected.txt')), 'original snowleopard result') 130 self.assertMultiLineEqual(out, '{"add": [], "remove-lines": [], "delete": []}\n') 131 132 def test_copying_overwritten_baseline_to_multiple_locations(self): 133 self.tool.executive = MockExecutive2() 134 135 # FIXME: crbug.com/279494. it's confusing that this is the test- port, and not the regular win port. Really all of the tests should be using the test ports. 136 port = self.tool.port_factory.get('test-win-win7') 137 self._write(port._filesystem.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt'), 'original win7 result') 138 139 old_exact_matches = builders._exact_matches 140 oc = OutputCapture() 141 try: 142 builders._exact_matches = { 143 "MOCK Leopard": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])}, 144 "MOCK Linux": {"port_name": "test-linux-x86_64", "specifiers": set(["mock-specifier"])}, 145 "MOCK Win7": {"port_name": "test-win-win7", "specifiers": set(["mock-specifier"])}, 146 } 147 148 options = MockOptions(builder="MOCK Win7", suffixes="txt", verbose=True, test="failures/expected/image.html", results_directory=None) 149 150 oc.capture_output() 151 self.command.execute(options, [], self.tool) 152 finally: 153 out, _, _ = oc.restore_output() 154 builders._exact_matches = old_exact_matches 155 156 self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-linux-x86_64/failures/expected/image-expected.txt')), 'original win7 result') 157 self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/mac-leopard/userscripts/another-test-expected.txt'))) 158 self.assertMultiLineEqual(out, '{"add": [], "remove-lines": [], "delete": []}\n') 159 160 def test_no_copy_existing_baseline(self): 161 self.tool.executive = MockExecutive2() 162 163 # FIXME: it's confusing that this is the test- port, and not the regular win port. Really all of the tests should be using the test ports. 164 port = self.tool.port_factory.get('test-win-win7') 165 self._write(port._filesystem.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt'), 'original win7 result') 166 167 old_exact_matches = builders._exact_matches 168 oc = OutputCapture() 169 try: 170 builders._exact_matches = { 171 "MOCK Leopard": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])}, 172 "MOCK Linux": {"port_name": "test-linux-x86_64", "specifiers": set(["mock-specifier"])}, 173 "MOCK Win7": {"port_name": "test-win-win7", "specifiers": set(["mock-specifier"])}, 174 } 175 176 options = MockOptions(builder="MOCK Win7", suffixes="txt", verbose=True, test="failures/expected/image.html", results_directory=None) 177 178 oc.capture_output() 179 self.command.execute(options, [], self.tool) 180 finally: 181 out, _, _ = oc.restore_output() 182 builders._exact_matches = old_exact_matches 183 184 self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-linux-x86_64/failures/expected/image-expected.txt')), 'original win7 result') 185 self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt')), 'original win7 result') 186 self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/mac-leopard/userscripts/another-test-expected.txt'))) 187 self.assertMultiLineEqual(out, '{"add": [], "remove-lines": [], "delete": []}\n') 188 189 def test_no_copy_skipped_test(self): 190 self.tool.executive = MockExecutive2() 191 192 port = self.tool.port_factory.get('test-win-win7') 193 fs = self.tool.filesystem 194 self._write(fs.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt'), 'original win7 result') 195 expectations_path = fs.join(port.path_to_generic_test_expectations_file()) 196 self._write(expectations_path, ( 197 "[ Win ] failures/expected/image.html [ Failure ]\n" 198 "[ Linux ] failures/expected/image.html [ Skip ]\n")) 199 old_exact_matches = builders._exact_matches 200 oc = OutputCapture() 201 try: 202 builders._exact_matches = { 203 "MOCK Linux": {"port_name": "test-linux-x86_64", "specifiers": set(["mock-specifier"])}, 204 "MOCK Leopard": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])}, 205 "MOCK Win7": {"port_name": "test-win-win7", "specifiers": set(["mock-specifier"])}, 206 } 207 208 options = MockOptions(builder="MOCK Win7", suffixes="txt", verbose=True, test="failures/expected/image.html", results_directory=None) 209 210 oc.capture_output() 211 self.command.execute(options, [], self.tool) 212 finally: 213 out, _, _ = oc.restore_output() 214 builders._exact_matches = old_exact_matches 215 216 self.assertFalse(fs.exists(fs.join(port.layout_tests_dir(), 'platform/test-linux-x86_64/failures/expected/image-expected.txt'))) 217 self.assertEqual(self._read(fs.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt')), 218 'original win7 result') 219 220 221class TestRebaselineTest(_BaseTestCase): 222 command_constructor = RebaselineTest # AKA webkit-patch rebaseline-test-internal 223 224 def setUp(self): 225 super(TestRebaselineTest, self).setUp() 226 self.options = MockOptions(builder="WebKit Mac10.7", test="userscripts/another-test.html", suffixes="txt", results_directory=None) 227 228 def test_baseline_directory(self): 229 command = self.command 230 self.assertMultiLineEqual(command._baseline_directory("WebKit Mac10.7"), "/mock-checkout/third_party/WebKit/LayoutTests/platform/mac-lion") 231 self.assertMultiLineEqual(command._baseline_directory("WebKit Mac10.6"), "/mock-checkout/third_party/WebKit/LayoutTests/platform/mac-snowleopard") 232 233 def test_rebaseline_updates_expectations_file_noop(self): 234 self._zero_out_test_expectations() 235 self._write(self.lion_expectations_path, """Bug(B) [ Mac Linux XP Debug ] fast/dom/Window/window-postmessage-clone-really-deep-array.html [ Pass ] 236Bug(A) [ Debug ] : fast/css/large-list-of-rules-crash.html [ Failure ] 237""") 238 self._write("fast/dom/Window/window-postmessage-clone-really-deep-array.html", "Dummy test contents") 239 self._write("fast/css/large-list-of-rules-crash.html", "Dummy test contents") 240 self._write("userscripts/another-test.html", "Dummy test contents") 241 242 self.options.suffixes = "png,wav,txt" 243 self.command._rebaseline_test_and_update_expectations(self.options) 244 245 self.assertItemsEqual(self.tool.web.urls_fetched, 246 [self.WEB_PREFIX + '/userscripts/another-test-actual.png', 247 self.WEB_PREFIX + '/userscripts/another-test-actual.wav', 248 self.WEB_PREFIX + '/userscripts/another-test-actual.txt']) 249 new_expectations = self._read(self.lion_expectations_path) 250 self.assertMultiLineEqual(new_expectations, """Bug(B) [ Mac Linux XP Debug ] fast/dom/Window/window-postmessage-clone-really-deep-array.html [ Pass ] 251Bug(A) [ Debug ] : fast/css/large-list-of-rules-crash.html [ Failure ] 252""") 253 254 def test_rebaseline_test(self): 255 self.command._rebaseline_test("WebKit Linux", "userscripts/another-test.html", "txt", self.WEB_PREFIX) 256 self.assertItemsEqual(self.tool.web.urls_fetched, [self.WEB_PREFIX + '/userscripts/another-test-actual.txt']) 257 258 def test_rebaseline_test_with_results_directory(self): 259 self._write("userscripts/another-test.html", "test data") 260 self._write(self.lion_expectations_path, "Bug(x) [ Mac ] userscripts/another-test.html [ ImageOnlyFailure ]\nbug(z) [ Linux ] userscripts/another-test.html [ ImageOnlyFailure ]\n") 261 self.options.results_directory = '/tmp' 262 self.command._rebaseline_test_and_update_expectations(self.options) 263 self.assertItemsEqual(self.tool.web.urls_fetched, ['file:///tmp/userscripts/another-test-actual.txt']) 264 265 def test_rebaseline_reftest(self): 266 self._write("userscripts/another-test.html", "test data") 267 self._write("userscripts/another-test-expected.html", "generic result") 268 OutputCapture().assert_outputs(self, self.command._rebaseline_test_and_update_expectations, args=[self.options], 269 expected_logs="Cannot rebaseline reftest: userscripts/another-test.html\n") 270 self.assertDictEqual(self.command._scm_changes, {'add': [], 'remove-lines': [], "delete": []}) 271 272 def test_rebaseline_test_and_print_scm_changes(self): 273 self.command._print_scm_changes = True 274 self.command._scm_changes = {'add': [], 'delete': []} 275 self.tool._scm.exists = lambda x: False 276 277 self.command._rebaseline_test("WebKit Linux", "userscripts/another-test.html", "txt", None) 278 279 self.assertDictEqual(self.command._scm_changes, {'add': ['/mock-checkout/third_party/WebKit/LayoutTests/platform/linux/userscripts/another-test-expected.txt'], 'delete': []}) 280 281 def test_rebaseline_test_internal_with_port_that_lacks_buildbot(self): 282 self.tool.executive = MockExecutive2() 283 284 # FIXME: it's confusing that this is the test- port, and not the regular win port. Really all of the tests should be using the test ports. 285 port = self.tool.port_factory.get('test-win-win7') 286 self._write(port._filesystem.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt'), 'original win7 result') 287 288 old_exact_matches = builders._exact_matches 289 oc = OutputCapture() 290 try: 291 builders._exact_matches = { 292 "MOCK XP": {"port_name": "test-win-xp"}, 293 "MOCK Win7": {"port_name": "test-win-win7"}, 294 } 295 296 options = MockOptions(optimize=True, builder="MOCK Win7", suffixes="txt", 297 verbose=True, test="failures/expected/image.html", results_directory=None) 298 299 oc.capture_output() 300 self.command.execute(options, [], self.tool) 301 finally: 302 out, _, _ = oc.restore_output() 303 builders._exact_matches = old_exact_matches 304 305 self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt')), 'MOCK Web result, convert 404 to None=True') 306 self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-win-xp/failures/expected/image-expected.txt'))) 307 self.assertMultiLineEqual(out, '{"add": [], "remove-lines": [{"test": "failures/expected/image.html", "builder": "MOCK Win7"}], "delete": []}\n') 308 309 310class TestAbstractParallelRebaselineCommand(_BaseTestCase): 311 command_constructor = AbstractParallelRebaselineCommand 312 313 def test_builders_to_fetch_from(self): 314 old_exact_matches = builders._exact_matches 315 try: 316 builders._exact_matches = { 317 "MOCK XP": {"port_name": "test-win-xp"}, 318 "MOCK Win7": {"port_name": "test-win-win7"}, 319 "MOCK Win7 (dbg)(1)": {"port_name": "test-win-win7"}, 320 "MOCK Win7 (dbg)(2)": {"port_name": "test-win-win7"}, 321 } 322 323 builders_to_fetch = self.command._builders_to_fetch_from(["MOCK XP", "MOCK Win7 (dbg)(1)", "MOCK Win7 (dbg)(2)", "MOCK Win7"]) 324 self.assertEqual(builders_to_fetch, ["MOCK XP", "MOCK Win7"]) 325 finally: 326 builders._exact_matches = old_exact_matches 327 328 329class TestRebaselineJson(_BaseTestCase): 330 command_constructor = RebaselineJson 331 332 def setUp(self): 333 super(TestRebaselineJson, self).setUp() 334 self.tool.executive = MockExecutive2() 335 self.old_exact_matches = builders._exact_matches 336 builders._exact_matches = { 337 "MOCK builder": {"port_name": "test-mac-snowleopard"}, 338 "MOCK builder (Debug)": {"port_name": "test-mac-snowleopard"}, 339 } 340 341 def tearDown(self): 342 builders._exact_matches = self.old_exact_matches 343 super(TestRebaselineJson, self).tearDown() 344 345 def test_rebaseline_test_passes_on_all_builders(self): 346 self._setup_mock_builder_data() 347 348 def builder_data(): 349 self.command._builder_data['MOCK builder'] = LayoutTestResults.results_from_string("""ADD_RESULTS({ 350 "tests": { 351 "userscripts": { 352 "first-test.html": { 353 "expected": "NEEDSREBASELINE", 354 "actual": "PASS" 355 } 356 } 357 } 358});""") 359 return self.command._builder_data 360 361 self.command.builder_data = builder_data 362 363 options = MockOptions(optimize=True, verbose=True, results_directory=None) 364 365 self._write(self.lion_expectations_path, "Bug(x) userscripts/first-test.html [ ImageOnlyFailure ]\n") 366 self._write("userscripts/first-test.html", "Dummy test contents") 367 368 self.command._rebaseline(options, {"userscripts/first-test.html": {"MOCK builder": ["txt", "png"]}}) 369 370 # Note that we have one run_in_parallel() call followed by a run_command() 371 self.assertEqual(self.tool.executive.calls, 372 [[['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', '', 'userscripts/first-test.html', '--verbose']]]) 373 374 def test_rebaseline_all(self): 375 self._setup_mock_builder_data() 376 377 options = MockOptions(optimize=True, verbose=True, results_directory=None) 378 self._write("userscripts/first-test.html", "Dummy test contents") 379 self.command._rebaseline(options, {"userscripts/first-test.html": {"MOCK builder": ["txt", "png"]}}) 380 381 # Note that we have one run_in_parallel() call followed by a run_command() 382 self.assertEqual(self.tool.executive.calls, 383 [[['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/first-test.html', '--verbose']], 384 [['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/first-test.html', '--verbose']], 385 [['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', 'txt,png', 'userscripts/first-test.html', '--verbose']]]) 386 387 def test_rebaseline_debug(self): 388 self._setup_mock_builder_data() 389 390 options = MockOptions(optimize=True, verbose=True, results_directory=None) 391 self._write("userscripts/first-test.html", "Dummy test contents") 392 self.command._rebaseline(options, {"userscripts/first-test.html": {"MOCK builder (Debug)": ["txt", "png"]}}) 393 394 # Note that we have one run_in_parallel() call followed by a run_command() 395 self.assertEqual(self.tool.executive.calls, 396 [[['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder (Debug)', '--test', 'userscripts/first-test.html', '--verbose']], 397 [['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder (Debug)', '--test', 'userscripts/first-test.html', '--verbose']], 398 [['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', 'txt,png', 'userscripts/first-test.html', '--verbose']]]) 399 400 def test_no_optimize(self): 401 self._setup_mock_builder_data() 402 403 options = MockOptions(optimize=False, verbose=True, results_directory=None) 404 self._write("userscripts/first-test.html", "Dummy test contents") 405 self.command._rebaseline(options, {"userscripts/first-test.html": {"MOCK builder (Debug)": ["txt", "png"]}}) 406 407 # Note that we have only one run_in_parallel() call 408 self.assertEqual(self.tool.executive.calls, 409 [[['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder (Debug)', '--test', 'userscripts/first-test.html', '--verbose']], 410 [['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder (Debug)', '--test', 'userscripts/first-test.html', '--verbose']]]) 411 412 def test_results_directory(self): 413 self._setup_mock_builder_data() 414 415 options = MockOptions(optimize=False, verbose=True, results_directory='/tmp') 416 self._write("userscripts/first-test.html", "Dummy test contents") 417 self.command._rebaseline(options, {"userscripts/first-test.html": {"MOCK builder": ["txt", "png"]}}) 418 419 # Note that we have only one run_in_parallel() call 420 self.assertEqual(self.tool.executive.calls, 421 [[['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/first-test.html', '--results-directory', '/tmp', '--verbose']], 422 [['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/first-test.html', '--results-directory', '/tmp', '--verbose']]]) 423 424class TestRebaselineJsonUpdatesExpectationsFiles(_BaseTestCase): 425 command_constructor = RebaselineJson 426 427 def setUp(self): 428 super(TestRebaselineJsonUpdatesExpectationsFiles, self).setUp() 429 self.tool.executive = MockExecutive2() 430 431 def mock_run_command(args, 432 cwd=None, 433 input=None, 434 error_handler=None, 435 return_exit_code=False, 436 return_stderr=True, 437 decode_output=False, 438 env=None): 439 return '{"add": [], "remove-lines": [{"test": "userscripts/first-test.html", "builder": "WebKit Mac10.7"}]}\n' 440 self.tool.executive.run_command = mock_run_command 441 442 def test_rebaseline_updates_expectations_file(self): 443 options = MockOptions(optimize=False, verbose=True, results_directory=None) 444 445 self._write(self.lion_expectations_path, "Bug(x) [ Mac ] userscripts/first-test.html [ ImageOnlyFailure ]\nbug(z) [ Linux ] userscripts/first-test.html [ ImageOnlyFailure ]\n") 446 self._write("userscripts/first-test.html", "Dummy test contents") 447 self._setup_mock_builder_data() 448 449 self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}}) 450 451 new_expectations = self._read(self.lion_expectations_path) 452 self.assertMultiLineEqual(new_expectations, "Bug(x) [ Mavericks MountainLion Retina SnowLeopard ] userscripts/first-test.html [ ImageOnlyFailure ]\nbug(z) [ Linux ] userscripts/first-test.html [ ImageOnlyFailure ]\n") 453 454 def test_rebaseline_updates_expectations_file_all_platforms(self): 455 options = MockOptions(optimize=False, verbose=True, results_directory=None) 456 457 self._write(self.lion_expectations_path, "Bug(x) userscripts/first-test.html [ ImageOnlyFailure ]\n") 458 self._write("userscripts/first-test.html", "Dummy test contents") 459 self._setup_mock_builder_data() 460 461 self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}}) 462 463 new_expectations = self._read(self.lion_expectations_path) 464 self.assertMultiLineEqual(new_expectations, "Bug(x) [ Android Linux Mavericks MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n") 465 466 def test_rebaseline_handles_platform_skips(self): 467 # This test is just like test_rebaseline_updates_expectations_file_all_platforms(), 468 # except that if a particular port happens to SKIP a test in an overrides file, 469 # we count that as passing, and do not think that we still need to rebaseline it. 470 options = MockOptions(optimize=False, verbose=True, results_directory=None) 471 472 self._write(self.lion_expectations_path, "Bug(x) userscripts/first-test.html [ ImageOnlyFailure ]\n") 473 self._write("NeverFixTests", "Bug(y) [ Android ] userscripts [ Skip ]\n") 474 self._write("userscripts/first-test.html", "Dummy test contents") 475 self._setup_mock_builder_data() 476 477 self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}}) 478 479 new_expectations = self._read(self.lion_expectations_path) 480 self.assertMultiLineEqual(new_expectations, "Bug(x) [ Linux Mavericks MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n") 481 482 def test_rebaseline_handles_skips_in_file(self): 483 # This test is like test_Rebaseline_handles_platform_skips, except that the 484 # Skip is in the same (generic) file rather than a platform file. In this case, 485 # the Skip line should be left unmodified. Note that the first line is now 486 # qualified as "[Linux Mac Win]"; if it was unqualified, it would conflict with 487 # the second line. 488 options = MockOptions(optimize=False, verbose=True, results_directory=None) 489 490 self._write(self.lion_expectations_path, 491 ("Bug(x) [ Linux Mac Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n" 492 "Bug(y) [ Android ] userscripts/first-test.html [ Skip ]\n")) 493 self._write("userscripts/first-test.html", "Dummy test contents") 494 self._setup_mock_builder_data() 495 496 self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}}) 497 498 new_expectations = self._read(self.lion_expectations_path) 499 self.assertMultiLineEqual(new_expectations, 500 ("Bug(x) [ Linux Mavericks MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n" 501 "Bug(y) [ Android ] userscripts/first-test.html [ Skip ]\n")) 502 503 def test_rebaseline_handles_smoke_tests(self): 504 # This test is just like test_rebaseline_handles_platform_skips, except that we check for 505 # a test not being in the SmokeTests file, instead of using overrides files. 506 # If a test is not part of the smoke tests, we count that as passing on ports that only 507 # run smoke tests, and do not think that we still need to rebaseline it. 508 options = MockOptions(optimize=False, verbose=True, results_directory=None) 509 510 self._write(self.lion_expectations_path, "Bug(x) userscripts/first-test.html [ ImageOnlyFailure ]\n") 511 self._write("SmokeTests", "fast/html/article-element.html") 512 self._write("userscripts/first-test.html", "Dummy test contents") 513 self._setup_mock_builder_data() 514 515 self.command._rebaseline(options, {"userscripts/first-test.html": {"WebKit Mac10.7": ["txt", "png"]}}) 516 517 new_expectations = self._read(self.lion_expectations_path) 518 self.assertMultiLineEqual(new_expectations, "Bug(x) [ Linux Mavericks MountainLion Retina SnowLeopard Win ] userscripts/first-test.html [ ImageOnlyFailure ]\n") 519 520 521class TestRebaseline(_BaseTestCase): 522 # This command shares most of its logic with RebaselineJson, so these tests just test what is different. 523 524 command_constructor = Rebaseline # AKA webkit-patch rebaseline 525 526 def test_rebaseline(self): 527 self.command._builders_to_pull_from = lambda: [MockBuilder('MOCK builder')] 528 529 self._write("userscripts/first-test.html", "test data") 530 531 self._zero_out_test_expectations() 532 self._setup_mock_builder_data() 533 534 old_exact_matches = builders._exact_matches 535 try: 536 builders._exact_matches = { 537 "MOCK builder": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])}, 538 } 539 self.command.execute(MockOptions(results_directory=False, optimize=False, builders=None, suffixes="txt,png", verbose=True), ['userscripts/first-test.html'], self.tool) 540 finally: 541 builders._exact_matches = old_exact_matches 542 543 calls = filter(lambda x: x != ['qmake', '-v'] and x[0] != 'perl', self.tool.executive.calls) 544 self.assertEqual(calls, 545 [[['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/first-test.html', '--verbose']], 546 [['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/first-test.html', '--verbose']]]) 547 548 def test_rebaseline_directory(self): 549 self.command._builders_to_pull_from = lambda: [MockBuilder('MOCK builder')] 550 551 self._write("userscripts/first-test.html", "test data") 552 self._write("userscripts/second-test.html", "test data") 553 554 self._setup_mock_builder_data() 555 556 old_exact_matches = builders._exact_matches 557 try: 558 builders._exact_matches = { 559 "MOCK builder": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])}, 560 } 561 self.command.execute(MockOptions(results_directory=False, optimize=False, builders=None, suffixes="txt,png", verbose=True), ['userscripts'], self.tool) 562 finally: 563 builders._exact_matches = old_exact_matches 564 565 calls = filter(lambda x: x != ['qmake', '-v'] and x[0] != 'perl', self.tool.executive.calls) 566 self.assertEqual(calls, 567 [[['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/first-test.html', '--verbose'], 568 ['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/second-test.html', '--verbose']], 569 [['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/first-test.html', '--verbose'], 570 ['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/second-test.html', '--verbose']]]) 571 572 573class MockLineRemovingExecutive(MockExecutive): 574 def run_in_parallel(self, commands): 575 assert len(commands) 576 577 num_previous_calls = len(self.calls) 578 command_outputs = [] 579 for cmd_line, cwd in commands: 580 out = self.run_command(cmd_line, cwd=cwd) 581 if 'rebaseline-test-internal' in cmd_line: 582 out = '{"add": [], "remove-lines": [{"test": "%s", "builder": "%s"}], "delete": []}\n' % (cmd_line[7], cmd_line[5]) 583 command_outputs.append([0, out, '']) 584 585 new_calls = self.calls[num_previous_calls:] 586 self.calls = self.calls[:num_previous_calls] 587 self.calls.append(new_calls) 588 return command_outputs 589 590 591class TestRebaselineExpectations(_BaseTestCase): 592 command_constructor = RebaselineExpectations 593 594 def setUp(self): 595 super(TestRebaselineExpectations, self).setUp() 596 self.options = MockOptions(optimize=False, builders=None, suffixes=['txt'], verbose=False, platform=None, results_directory=None) 597 598 def _write_test_file(self, port, path, contents): 599 abs_path = self.tool.filesystem.join(port.layout_tests_dir(), path) 600 self.tool.filesystem.write_text_file(abs_path, contents) 601 602 def _setup_test_port(self): 603 test_port = self.tool.port_factory.get('test') 604 original_get = self.tool.port_factory.get 605 606 def get_test_port(port_name=None, options=None, **kwargs): 607 if not port_name: 608 return test_port 609 return original_get(port_name, options, **kwargs) 610 # Need to make sure all the ports grabbed use the test checkout path instead of the mock checkout path. 611 # FIXME: crbug.com/279494 - we shouldn't be doing this. 612 self.tool.port_factory.get = get_test_port 613 614 return test_port 615 616 def test_rebaseline_expectations(self): 617 self._zero_out_test_expectations() 618 619 self.tool.executive = MockExecutive2() 620 621 def builder_data(): 622 self.command._builder_data['MOCK SnowLeopard'] = self.command._builder_data['MOCK Leopard'] = LayoutTestResults.results_from_string("""ADD_RESULTS({ 623 "tests": { 624 "userscripts": { 625 "another-test.html": { 626 "expected": "PASS", 627 "actual": "PASS TEXT" 628 }, 629 "images.svg": { 630 "expected": "FAIL", 631 "actual": "IMAGE+TEXT" 632 } 633 } 634 } 635});""") 636 return self.command._builder_data 637 638 self.command.builder_data = builder_data 639 640 self._write("userscripts/another-test.html", "Dummy test contents") 641 self._write("userscripts/images.svg", "Dummy test contents") 642 self.command._tests_to_rebaseline = lambda port: { 643 'userscripts/another-test.html': set(['txt']), 644 'userscripts/images.svg': set(['png']), 645 'userscripts/not-actually-failing.html': set(['txt', 'png', 'wav']), 646 } 647 648 old_exact_matches = builders._exact_matches 649 try: 650 builders._exact_matches = { 651 "MOCK Leopard": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])}, 652 "MOCK SnowLeopard": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])}, 653 } 654 self.command.execute(self.options, [], self.tool) 655 finally: 656 builders._exact_matches = old_exact_matches 657 658 # FIXME: change this to use the test- ports. 659 calls = filter(lambda x: x != ['qmake', '-v'], self.tool.executive.calls) 660 self.assertEqual(self.tool.executive.calls, [ 661 [ 662 ['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt', '--builder', 'MOCK Leopard', '--test', 'userscripts/another-test.html'], 663 ['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt', '--builder', 'MOCK SnowLeopard', '--test', 'userscripts/another-test.html'], 664 ['echo', 'copy-existing-baselines-internal', '--suffixes', 'png', '--builder', 'MOCK Leopard', '--test', 'userscripts/images.svg'], 665 ['echo', 'copy-existing-baselines-internal', '--suffixes', 'png', '--builder', 'MOCK SnowLeopard', '--test', 'userscripts/images.svg'] 666 ], 667 [ 668 ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK Leopard', '--test', 'userscripts/another-test.html'], 669 ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK SnowLeopard', '--test', 'userscripts/another-test.html'], 670 ['echo', 'rebaseline-test-internal', '--suffixes', 'png', '--builder', 'MOCK Leopard', '--test', 'userscripts/images.svg'], 671 ['echo', 'rebaseline-test-internal', '--suffixes', 'png', '--builder', 'MOCK SnowLeopard', '--test', 'userscripts/images.svg'] 672 ] 673 ]) 674 675 def test_rebaseline_expectations_noop(self): 676 self._zero_out_test_expectations() 677 678 oc = OutputCapture() 679 try: 680 oc.capture_output() 681 self.command.execute(self.options, [], self.tool) 682 finally: 683 _, _, logs = oc.restore_output() 684 self.assertEqual(self.tool.filesystem.written_files, {}) 685 self.assertEqual(logs, 'Did not find any tests marked Rebaseline.\n') 686 687 def disabled_test_overrides_are_included_correctly(self): 688 # This tests that the any tests marked as REBASELINE in the overrides are found, but 689 # that the overrides do not get written into the main file. 690 self._zero_out_test_expectations() 691 692 self._write(self.lion_expectations_path, '') 693 self.lion_port.expectations_dict = lambda: { 694 self.lion_expectations_path: '', 695 'overrides': ('Bug(x) userscripts/another-test.html [ Failure Rebaseline ]\n' 696 'Bug(y) userscripts/test.html [ Crash ]\n')} 697 self._write('/userscripts/another-test.html', '') 698 699 self.assertDictEqual(self.command._tests_to_rebaseline(self.lion_port), {'userscripts/another-test.html': set(['png', 'txt', 'wav'])}) 700 self.assertEqual(self._read(self.lion_expectations_path), '') 701 702 def test_rebaseline_without_other_expectations(self): 703 self._write("userscripts/another-test.html", "Dummy test contents") 704 self._write(self.lion_expectations_path, "Bug(x) userscripts/another-test.html [ Rebaseline ]\n") 705 self.assertDictEqual(self.command._tests_to_rebaseline(self.lion_port), {'userscripts/another-test.html': ('png', 'wav', 'txt')}) 706 707 def test_rebaseline_test_passes_everywhere(self): 708 test_port = self._setup_test_port() 709 710 old_builder_data = self.command.builder_data 711 712 def builder_data(): 713 self.command._builder_data['MOCK Leopard'] = self.command._builder_data['MOCK SnowLeopard'] = LayoutTestResults.results_from_string("""ADD_RESULTS({ 714 "tests": { 715 "fast": { 716 "dom": { 717 "prototype-taco.html": { 718 "expected": "FAIL", 719 "actual": "PASS", 720 "is_unexpected": true 721 } 722 } 723 } 724 } 725});""") 726 return self.command._builder_data 727 728 self.command.builder_data = builder_data 729 730 self.tool.filesystem.write_text_file(test_port.path_to_generic_test_expectations_file(), """ 731Bug(foo) fast/dom/prototype-taco.html [ Rebaseline ] 732""") 733 734 self._write_test_file(test_port, 'fast/dom/prototype-taco.html', "Dummy test contents") 735 736 self.tool.executive = MockLineRemovingExecutive() 737 738 old_exact_matches = builders._exact_matches 739 try: 740 builders._exact_matches = { 741 "MOCK Leopard": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])}, 742 "MOCK SnowLeopard": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])}, 743 } 744 745 self.command.execute(self.options, [], self.tool) 746 self.assertEqual(self.tool.executive.calls, []) 747 748 # The mac ports should both be removed since they're the only ones in builders._exact_matches. 749 self.assertEqual(self.tool.filesystem.read_text_file(test_port.path_to_generic_test_expectations_file()), """ 750Bug(foo) [ Linux Win ] fast/dom/prototype-taco.html [ Rebaseline ] 751""") 752 finally: 753 builders._exact_matches = old_exact_matches 754 755 756class _FakeOptimizer(BaselineOptimizer): 757 def read_results_by_directory(self, baseline_name): 758 if baseline_name.endswith('txt'): 759 return {'LayoutTests/passes/text.html': '123456'} 760 return {} 761 762 763class TestOptimizeBaselines(_BaseTestCase): 764 command_constructor = OptimizeBaselines 765 766 def _write_test_file(self, port, path, contents): 767 abs_path = self.tool.filesystem.join(port.layout_tests_dir(), path) 768 self.tool.filesystem.write_text_file(abs_path, contents) 769 770 def setUp(self): 771 super(TestOptimizeBaselines, self).setUp() 772 773 def test_modify_scm(self): 774 # FIXME: This is a hack to get the unittest and the BaselineOptimize to both use /mock-checkout 775 # instead of one using /mock-checkout and one using /test-checkout. 776 default_port = self.tool.port_factory.get() 777 self.tool.port_factory.get = lambda port_name=None: default_port 778 779 test_port = self.tool.port_factory.get('test') 780 self._write_test_file(test_port, 'another/test.html', "Dummy test contents") 781 self._write_test_file(test_port, 'platform/mac-snowleopard/another/test-expected.txt', "result A") 782 self._write_test_file(test_port, 'another/test-expected.txt', "result A") 783 784 old_exact_matches = builders._exact_matches 785 try: 786 builders._exact_matches = { 787 "MOCK Leopard Debug": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])}, 788 } 789 OutputCapture().assert_outputs(self, self.command.execute, args=[ 790 MockOptions(suffixes='txt', no_modify_scm=False, platform='test-mac-snowleopard'), 791 ['another/test.html'], 792 self.tool, 793 ], expected_stdout='{"add": [], "remove-lines": [], "delete": []}\n') 794 finally: 795 builders._exact_matches = old_exact_matches 796 797 self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(test_port.layout_tests_dir(), 'platform/mac/another/test-expected.txt'))) 798 self.assertTrue(self.tool.filesystem.exists(self.tool.filesystem.join(test_port.layout_tests_dir(), 'another/test-expected.txt'))) 799 800 def test_no_modify_scm(self): 801 # FIXME: This is a hack to get the unittest and the BaselineOptimize to both use /mock-checkout 802 # instead of one using /mock-checkout and one using /test-checkout. 803 default_port = self.tool.port_factory.get() 804 self.tool.port_factory.get = lambda port_name=None: default_port 805 806 test_port = self.tool.port_factory.get('test') 807 self._write_test_file(test_port, 'another/test.html', "Dummy test contents") 808 self._write_test_file(test_port, 'platform/mac-snowleopard/another/test-expected.txt', "result A") 809 self._write_test_file(test_port, 'another/test-expected.txt', "result A") 810 811 old_exact_matches = builders._exact_matches 812 try: 813 builders._exact_matches = { 814 "MOCK Leopard Debug": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])}, 815 } 816 OutputCapture().assert_outputs(self, self.command.execute, args=[ 817 MockOptions(suffixes='txt', no_modify_scm=True, platform='test-mac-snowleopard'), 818 ['another/test.html'], 819 self.tool, 820 ], expected_stdout='{"add": [], "remove-lines": [], "delete": ["/mock-checkout/third_party/WebKit/LayoutTests/platform/mac-snowleopard/another/test-expected.txt"]}\n') 821 finally: 822 builders._exact_matches = old_exact_matches 823 824 self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(test_port.layout_tests_dir(), 'platform/mac/another/test-expected.txt'))) 825 self.assertTrue(self.tool.filesystem.exists(self.tool.filesystem.join(test_port.layout_tests_dir(), 'another/test-expected.txt'))) 826 827 828class TestAnalyzeBaselines(_BaseTestCase): 829 command_constructor = AnalyzeBaselines 830 831 def setUp(self): 832 super(TestAnalyzeBaselines, self).setUp() 833 self.port = self.tool.port_factory.get('test') 834 self.tool.port_factory.get = (lambda port_name=None, options=None: self.port) 835 self.lines = [] 836 self.command._optimizer_class = _FakeOptimizer 837 self.command._write = (lambda msg: self.lines.append(msg)) # pylint bug warning about unnecessary lambda? pylint: disable=W0108 838 839 def test_default(self): 840 self.command.execute(MockOptions(suffixes='txt', missing=False, platform=None), ['passes/text.html'], self.tool) 841 self.assertEqual(self.lines, 842 ['passes/text-expected.txt:', 843 ' (generic): 123456']) 844 845 def test_missing_baselines(self): 846 self.command.execute(MockOptions(suffixes='png,txt', missing=True, platform=None), ['passes/text.html'], self.tool) 847 self.assertEqual(self.lines, 848 ['passes/text-expected.png: (no baselines found)', 849 'passes/text-expected.txt:', 850 ' (generic): 123456']) 851 852 853class TestAutoRebaseline(_BaseTestCase): 854 command_constructor = AutoRebaseline 855 856 def _write_test_file(self, port, path, contents): 857 abs_path = self.tool.filesystem.join(port.layout_tests_dir(), path) 858 self.tool.filesystem.write_text_file(abs_path, contents) 859 860 def _setup_test_port(self): 861 test_port = self.tool.port_factory.get('test') 862 original_get = self.tool.port_factory.get 863 864 def get_test_port(port_name=None, options=None, **kwargs): 865 if not port_name: 866 return test_port 867 return original_get(port_name, options, **kwargs) 868 # Need to make sure all the ports grabbed use the test checkout path instead of the mock checkout path. 869 # FIXME: crbug.com/279494 - we shouldn't be doing this. 870 self.tool.port_factory.get = get_test_port 871 872 return test_port 873 874 def setUp(self): 875 super(TestAutoRebaseline, self).setUp() 876 self.command.latest_revision_processed_on_all_bots = lambda: 9000 877 self.command.bot_revision_data = lambda: [{"builder": "Mock builder", "revision": "9000"}] 878 879 def test_release_builders(self): 880 old_exact_matches = builders._exact_matches 881 try: 882 builders._exact_matches = { 883 "MOCK Leopard": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])}, 884 "MOCK Leopard Debug": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])}, 885 "MOCK Leopard ASAN": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])}, 886 } 887 self.assertEqual(self.command._release_builders(), ['MOCK Leopard']) 888 finally: 889 builders._exact_matches = old_exact_matches 890 891 def test_tests_to_rebaseline(self): 892 def blame(path): 893 return """ 894624c3081c0 path/to/TestExpectations (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000 11) crbug.com/24182 [ Debug ] path/to/norebaseline.html [ ImageOnlyFailure ] 895624c3081c0 path/to/TestExpectations (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000 13) Bug(foo) path/to/rebaseline-without-bug-number.html [ NeedsRebaseline ] 896624c3081c0 path/to/TestExpectations (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000 11) crbug.com/24182 [ Debug ] path/to/rebaseline-with-modifiers.html [ NeedsRebaseline ] 897624c3081c0 path/to/TestExpectations (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000 12) crbug.com/24182 crbug.com/234 path/to/rebaseline-without-modifiers.html [ NeedsRebaseline ] 8986469e754a1 path/to/TestExpectations (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000 12) crbug.com/24182 path/to/rebaseline-new-revision.html [ NeedsRebaseline ] 899624caaaaaa path/to/TestExpectations (foo@chromium.org 2013-04-28 04:52:41 +0000 12) crbug.com/24182 path/to/not-cycled-through-bots.html [ NeedsRebaseline ] 9000000000000 path/to/TestExpectations (foo@chromium.org 2013-04-28 04:52:41 +0000 12) crbug.com/24182 path/to/locally-changed-lined.html [ NeedsRebaseline ] 901""" 902 self.tool.scm().blame = blame 903 904 min_revision = 9000 905 self.assertEqual(self.command.tests_to_rebaseline(self.tool, min_revision, print_revisions=False), ( 906 set(['path/to/rebaseline-without-bug-number.html', 'path/to/rebaseline-with-modifiers.html', 'path/to/rebaseline-without-modifiers.html']), 907 5678, 908 'foobarbaz1@chromium.org', 909 set(['24182', '234']), 910 True)) 911 912 def test_tests_to_rebaseline_over_limit(self): 913 def blame(path): 914 result = "" 915 for i in range(0, self.command.MAX_LINES_TO_REBASELINE + 1): 916 result += "624c3081c0 path/to/TestExpectations (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000 13) crbug.com/24182 path/to/rebaseline-%s.html [ NeedsRebaseline ]\n" % i 917 return result 918 self.tool.scm().blame = blame 919 920 expected_list_of_tests = [] 921 for i in range(0, self.command.MAX_LINES_TO_REBASELINE): 922 expected_list_of_tests.append("path/to/rebaseline-%s.html" % i) 923 924 min_revision = 9000 925 self.assertEqual(self.command.tests_to_rebaseline(self.tool, min_revision, print_revisions=False), ( 926 set(expected_list_of_tests), 927 5678, 928 'foobarbaz1@chromium.org', 929 set(['24182']), 930 True)) 931 932 def test_commit_message(self): 933 author = "foo@chromium.org" 934 revision = 1234 935 bugs = set() 936 self.assertEqual(self.command.commit_message(author, revision, bugs), 937 """Auto-rebaseline for r1234 938 939http://src.chromium.org/viewvc/blink?view=revision&revision=1234 940 941TBR=foo@chromium.org 942""") 943 944 bugs = set(["234", "345"]) 945 self.assertEqual(self.command.commit_message(author, revision, bugs), 946 """Auto-rebaseline for r1234 947 948http://src.chromium.org/viewvc/blink?view=revision&revision=1234 949 950BUG=234,345 951TBR=foo@chromium.org 952""") 953 954 def test_no_needs_rebaseline_lines(self): 955 def blame(path): 956 return """ 9576469e754a1 path/to/TestExpectations (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000 11) crbug.com/24182 [ Debug ] path/to/norebaseline.html [ ImageOnlyFailure ] 958""" 959 self.tool.scm().blame = blame 960 961 self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False), [], self.tool) 962 self.assertEqual(self.tool.executive.calls, []) 963 964 def test_execute(self): 965 def blame(path): 966 return """ 9676469e754a1 path/to/TestExpectations (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000 11) # Test NeedsRebaseline being in a comment doesn't bork parsing. 9686469e754a1 path/to/TestExpectations (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000 11) crbug.com/24182 [ Debug ] path/to/norebaseline.html [ ImageOnlyFailure ] 9696469e754a1 path/to/TestExpectations (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000 13) Bug(foo) fast/dom/prototype-taco.html [ NeedsRebaseline ] 9706469e754a1 path/to/TestExpectations (foobarbaz1@chromium.org 2013-06-14 20:18:46 +0000 11) crbug.com/24182 [ SnowLeopard ] fast/dom/prototype-strawberry.html [ NeedsRebaseline ] 9716469e754a1 path/to/TestExpectations (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000 12) crbug.com/24182 fast/dom/prototype-chocolate.html [ NeedsRebaseline ] 972624caaaaaa path/to/TestExpectations (foo@chromium.org 2013-04-28 04:52:41 +0000 12) crbug.com/24182 path/to/not-cycled-through-bots.html [ NeedsRebaseline ] 9730000000000 path/to/TestExpectations (foo@chromium.org 2013-04-28 04:52:41 +0000 12) crbug.com/24182 path/to/locally-changed-lined.html [ NeedsRebaseline ] 974""" 975 self.tool.scm().blame = blame 976 977 test_port = self._setup_test_port() 978 979 old_builder_data = self.command.builder_data 980 981 def builder_data(): 982 old_builder_data() 983 # have prototype-chocolate only fail on "MOCK Leopard". 984 self.command._builder_data['MOCK SnowLeopard'] = LayoutTestResults.results_from_string("""ADD_RESULTS({ 985 "tests": { 986 "fast": { 987 "dom": { 988 "prototype-taco.html": { 989 "expected": "PASS", 990 "actual": "PASS TEXT", 991 "is_unexpected": true 992 }, 993 "prototype-chocolate.html": { 994 "expected": "FAIL", 995 "actual": "PASS" 996 }, 997 "prototype-strawberry.html": { 998 "expected": "PASS", 999 "actual": "IMAGE PASS", 1000 "is_unexpected": true 1001 } 1002 } 1003 } 1004 } 1005});""") 1006 return self.command._builder_data 1007 1008 self.command.builder_data = builder_data 1009 1010 self.tool.filesystem.write_text_file(test_port.path_to_generic_test_expectations_file(), """ 1011crbug.com/24182 [ Debug ] path/to/norebaseline.html [ Rebaseline ] 1012Bug(foo) fast/dom/prototype-taco.html [ NeedsRebaseline ] 1013crbug.com/24182 [ SnowLeopard ] fast/dom/prototype-strawberry.html [ NeedsRebaseline ] 1014crbug.com/24182 fast/dom/prototype-chocolate.html [ NeedsRebaseline ] 1015crbug.com/24182 path/to/not-cycled-through-bots.html [ NeedsRebaseline ] 1016crbug.com/24182 path/to/locally-changed-lined.html [ NeedsRebaseline ] 1017""") 1018 1019 self._write_test_file(test_port, 'fast/dom/prototype-taco.html', "Dummy test contents") 1020 self._write_test_file(test_port, 'fast/dom/prototype-strawberry.html', "Dummy test contents") 1021 self._write_test_file(test_port, 'fast/dom/prototype-chocolate.html', "Dummy test contents") 1022 1023 self.tool.executive = MockLineRemovingExecutive() 1024 1025 old_exact_matches = builders._exact_matches 1026 try: 1027 builders._exact_matches = { 1028 "MOCK Leopard": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])}, 1029 "MOCK SnowLeopard": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])}, 1030 } 1031 1032 self.command.tree_status = lambda: 'closed' 1033 self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False), [], self.tool) 1034 self.assertEqual(self.tool.executive.calls, []) 1035 1036 self.command.tree_status = lambda: 'open' 1037 self.tool.executive.calls = [] 1038 self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False), [], self.tool) 1039 1040 self.assertEqual(self.tool.executive.calls, [ 1041 [ 1042 ['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt,png', '--builder', 'MOCK Leopard', '--test', 'fast/dom/prototype-chocolate.html'], 1043 ['echo', 'copy-existing-baselines-internal', '--suffixes', 'png', '--builder', 'MOCK SnowLeopard', '--test', 'fast/dom/prototype-strawberry.html'], 1044 ['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt', '--builder', 'MOCK Leopard', '--test', 'fast/dom/prototype-taco.html'], 1045 ['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt', '--builder', 'MOCK SnowLeopard', '--test', 'fast/dom/prototype-taco.html'], 1046 ], 1047 [ 1048 ['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK Leopard', '--test', 'fast/dom/prototype-chocolate.html'], 1049 ['echo', 'rebaseline-test-internal', '--suffixes', 'png', '--builder', 'MOCK SnowLeopard', '--test', 'fast/dom/prototype-strawberry.html'], 1050 ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK Leopard', '--test', 'fast/dom/prototype-taco.html'], 1051 ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK SnowLeopard', '--test', 'fast/dom/prototype-taco.html'], 1052 ], 1053 [ 1054 ['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', 'txt,png', 'fast/dom/prototype-chocolate.html'], 1055 ['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', 'png', 'fast/dom/prototype-strawberry.html'], 1056 ['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', 'txt', 'fast/dom/prototype-taco.html'], 1057 ], 1058 ['git', 'cl', 'upload', '-f'], 1059 ['git', 'pull'], 1060 ['git', 'cl', 'dcommit', '-f'], 1061 ['git', 'cl', 'set_close'], 1062 ]) 1063 1064 # The mac ports should both be removed since they're the only ones in builders._exact_matches. 1065 self.assertEqual(self.tool.filesystem.read_text_file(test_port.path_to_generic_test_expectations_file()), """ 1066crbug.com/24182 [ Debug ] path/to/norebaseline.html [ Rebaseline ] 1067Bug(foo) [ Linux Win ] fast/dom/prototype-taco.html [ NeedsRebaseline ] 1068crbug.com/24182 [ Linux Win ] fast/dom/prototype-chocolate.html [ NeedsRebaseline ] 1069crbug.com/24182 path/to/not-cycled-through-bots.html [ NeedsRebaseline ] 1070crbug.com/24182 path/to/locally-changed-lined.html [ NeedsRebaseline ] 1071""") 1072 finally: 1073 builders._exact_matches = old_exact_matches 1074 1075 def test_execute_git_cl_hangs(self): 1076 def blame(path): 1077 return """ 10786469e754a1 path/to/TestExpectations (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000 13) Bug(foo) fast/dom/prototype-taco.html [ NeedsRebaseline ] 1079""" 1080 self.tool.scm().blame = blame 1081 1082 test_port = self._setup_test_port() 1083 1084 old_builder_data = self.command.builder_data 1085 1086 def builder_data(): 1087 old_builder_data() 1088 # have prototype-chocolate only fail on "MOCK Leopard". 1089 self.command._builder_data['MOCK SnowLeopard'] = LayoutTestResults.results_from_string("""ADD_RESULTS({ 1090 "tests": { 1091 "fast": { 1092 "dom": { 1093 "prototype-taco.html": { 1094 "expected": "PASS", 1095 "actual": "PASS TEXT", 1096 "is_unexpected": true 1097 } 1098 } 1099 } 1100 } 1101});""") 1102 return self.command._builder_data 1103 1104 self.command.builder_data = builder_data 1105 1106 self.tool.filesystem.write_text_file(test_port.path_to_generic_test_expectations_file(), """ 1107Bug(foo) fast/dom/prototype-taco.html [ NeedsRebaseline ] 1108""") 1109 1110 self._write_test_file(test_port, 'fast/dom/prototype-taco.html', "Dummy test contents") 1111 1112 old_exact_matches = builders._exact_matches 1113 try: 1114 builders._exact_matches = { 1115 "MOCK SnowLeopard": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])}, 1116 } 1117 1118 self.command.SECONDS_BEFORE_GIVING_UP = 0 1119 self.command.tree_status = lambda: 'open' 1120 self.tool.executive.calls = [] 1121 self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False), [], self.tool) 1122 1123 self.assertEqual(self.tool.executive.calls, [ 1124 [ 1125 ['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt', '--builder', 'MOCK SnowLeopard', '--test', 'fast/dom/prototype-taco.html'], 1126 ], 1127 [ 1128 ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK SnowLeopard', '--test', 'fast/dom/prototype-taco.html'], 1129 ], 1130 [['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', 'txt', 'fast/dom/prototype-taco.html']], 1131 ['git', 'cl', 'upload', '-f'], 1132 ['git', 'cl', 'set_close'], 1133 ]) 1134 finally: 1135 builders._exact_matches = old_exact_matches 1136 1137 def test_execute_test_passes_everywhere(self): 1138 def blame(path): 1139 return """ 11406469e754a1 path/to/TestExpectations (foobarbaz1@chromium.org 2013-04-28 04:52:41 +0000 13) Bug(foo) fast/dom/prototype-taco.html [ NeedsRebaseline ] 1141""" 1142 self.tool.scm().blame = blame 1143 1144 test_port = self._setup_test_port() 1145 1146 old_builder_data = self.command.builder_data 1147 1148 def builder_data(): 1149 self.command._builder_data['MOCK Leopard'] = self.command._builder_data['MOCK SnowLeopard'] = LayoutTestResults.results_from_string("""ADD_RESULTS({ 1150 "tests": { 1151 "fast": { 1152 "dom": { 1153 "prototype-taco.html": { 1154 "expected": "FAIL", 1155 "actual": "PASS", 1156 "is_unexpected": true 1157 } 1158 } 1159 } 1160 } 1161});""") 1162 return self.command._builder_data 1163 1164 self.command.builder_data = builder_data 1165 1166 self.tool.filesystem.write_text_file(test_port.path_to_generic_test_expectations_file(), """ 1167Bug(foo) fast/dom/prototype-taco.html [ NeedsRebaseline ] 1168""") 1169 1170 self._write_test_file(test_port, 'fast/dom/prototype-taco.html', "Dummy test contents") 1171 1172 self.tool.executive = MockLineRemovingExecutive() 1173 1174 old_exact_matches = builders._exact_matches 1175 try: 1176 builders._exact_matches = { 1177 "MOCK Leopard": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])}, 1178 "MOCK SnowLeopard": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])}, 1179 } 1180 1181 self.command.tree_status = lambda: 'open' 1182 self.command.execute(MockOptions(optimize=True, verbose=False, move_overwritten_baselines=False, results_directory=False), [], self.tool) 1183 self.assertEqual(self.tool.executive.calls, [ 1184 [['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', '', 'fast/dom/prototype-taco.html']], 1185 ['git', 'cl', 'upload', '-f'], 1186 ['git', 'pull'], 1187 ['git', 'cl', 'dcommit', '-f'], 1188 ['git', 'cl', 'set_close'], 1189 ]) 1190 1191 # The mac ports should both be removed since they're the only ones in builders._exact_matches. 1192 self.assertEqual(self.tool.filesystem.read_text_file(test_port.path_to_generic_test_expectations_file()), """ 1193Bug(foo) [ Linux Win ] fast/dom/prototype-taco.html [ NeedsRebaseline ] 1194""") 1195 finally: 1196 builders._exact_matches = old_exact_matches 1197 1198 1199class TestRebaselineOMatic(_BaseTestCase): 1200 command_constructor = RebaselineOMatic 1201 1202 def setUp(self): 1203 super(TestRebaselineOMatic, self).setUp() 1204 self._logs = [] 1205 1206 def _mock_log_to_server(self, log='', is_new_entry=False): 1207 self._logs.append({'log': log, 'newentry': is_new_entry}) 1208 1209 def test_run_logged_command(self): 1210 self.command._verbose = False 1211 self.command._log_to_server = self._mock_log_to_server 1212 self.command._run_logged_command(['echo', 'foo']) 1213 self.assertEqual(self.tool.executive.calls, [['echo', 'foo']]) 1214 self.assertEqual(self._logs, [{'log': 'MOCK STDOUT', 'newentry': False}]) 1215 1216 def test_do_one_rebaseline(self): 1217 self.command._verbose = False 1218 self.command._log_to_server = self._mock_log_to_server 1219 1220 oc = OutputCapture() 1221 oc.capture_output() 1222 self.command._do_one_rebaseline() 1223 out, _, _ = oc.restore_output() 1224 1225 self.assertEqual(out, '') 1226 self.assertEqual(self.tool.executive.calls, [ 1227 ['git', 'pull'], 1228 ['/mock-checkout/third_party/WebKit/Tools/Scripts/webkit-patch', 'auto-rebaseline'], 1229 ]) 1230 self.assertEqual(self._logs, [ 1231 {'log': '', 'newentry': True}, 1232 {'log': 'MOCK STDOUT', 'newentry': False}, 1233 ]) 1234 1235 def test_do_one_rebaseline_verbose(self): 1236 self.command._verbose = True 1237 self.command._log_to_server = self._mock_log_to_server 1238 1239 oc = OutputCapture() 1240 oc.capture_output() 1241 self.command._do_one_rebaseline() 1242 out, _, _ = oc.restore_output() 1243 1244 self.assertEqual(out, 'MOCK STDOUT\n') 1245 self.assertEqual(self.tool.executive.calls, [ 1246 ['git', 'pull'], 1247 ['/mock-checkout/third_party/WebKit/Tools/Scripts/webkit-patch', 'auto-rebaseline', '--verbose'], 1248 ]) 1249 self.assertEqual(self._logs, [ 1250 {'log': '', 'newentry': True}, 1251 {'log': 'MOCK STDOUT', 'newentry': False}, 1252 ]) 1253