1 #!/usr/bin/python 2 # Copyright (C) 2010 Google Inc. All rights reserved. 3 # 4 # Redistribution and use in source and binary forms, with or without 5 # modification, are permitted provided that the following conditions are 6 # met: 7 # 8 # * Redistributions of source code must retain the above copyright 9 # notice, this list of conditions and the following disclaimer. 10 # * Redistributions in binary form must reproduce the above 11 # copyright notice, this list of conditions and the following disclaimer 12 # in the documentation and/or other materials provided with the 13 # distribution. 14 # * Neither the name of Google Inc. nor the names of its 15 # contributors may be used to endorse or promote products derived from 16 # this software without specific prior written permission. 17 # 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30 """Unit tests for printing.py.""" 31 32 import optparse 33 import unittest 34 import logging 35 36 from webkitpy.common import array_stream 37 from webkitpy.common.system import logtesting 38 from webkitpy.layout_tests import port 39 40 from webkitpy.layout_tests.layout_package import printing 41 from webkitpy.layout_tests.layout_package import result_summary 42 from webkitpy.layout_tests.layout_package import test_expectations 43 from webkitpy.layout_tests.layout_package import test_failures 44 from webkitpy.layout_tests.layout_package import test_results 45 from webkitpy.layout_tests.layout_package import test_runner 46 47 48 def get_options(args): 49 print_options = printing.print_options() 50 option_parser = optparse.OptionParser(option_list=print_options) 51 return option_parser.parse_args(args) 52 53 54 class TestUtilityFunctions(unittest.TestCase): 55 def test_configure_logging(self): 56 options, args = get_options([]) 57 stream = array_stream.ArrayStream() 58 handler = printing._configure_logging(stream, options.verbose) 59 logging.info("this should be logged") 60 self.assertFalse(stream.empty()) 61 62 stream.reset() 63 logging.debug("this should not be logged") 64 self.assertTrue(stream.empty()) 65 66 printing._restore_logging(handler) 67 68 stream.reset() 69 options, args = get_options(['--verbose']) 70 handler = printing._configure_logging(stream, options.verbose) 71 logging.debug("this should be logged") 72 self.assertFalse(stream.empty()) 73 printing._restore_logging(handler) 74 75 def test_print_options(self): 76 options, args = get_options([]) 77 self.assertTrue(options is not None) 78 79 def test_parse_print_options(self): 80 def test_switches(args, expected_switches_str, 81 verbose=False, child_processes=1, 82 is_fully_parallel=False): 83 options, args = get_options(args) 84 if expected_switches_str: 85 expected_switches = set(expected_switches_str.split(',')) 86 else: 87 expected_switches = set() 88 switches = printing.parse_print_options(options.print_options, 89 verbose, 90 child_processes, 91 is_fully_parallel) 92 self.assertEqual(expected_switches, switches) 93 94 # test that we default to the default set of switches 95 test_switches([], printing.PRINT_DEFAULT) 96 97 # test that verbose defaults to everything 98 test_switches([], printing.PRINT_EVERYTHING, verbose=True) 99 100 # test that --print default does what it's supposed to 101 test_switches(['--print', 'default'], printing.PRINT_DEFAULT) 102 103 # test that --print nothing does what it's supposed to 104 test_switches(['--print', 'nothing'], None) 105 106 # test that --print everything does what it's supposed to 107 test_switches(['--print', 'everything'], printing.PRINT_EVERYTHING) 108 109 # this tests that '--print X' overrides '--verbose' 110 test_switches(['--print', 'actual'], 'actual', verbose=True) 111 112 113 114 class Testprinter(unittest.TestCase): 115 def get_printer(self, args=None, single_threaded=False, 116 is_fully_parallel=False): 117 args = args or [] 118 printing_options = printing.print_options() 119 option_parser = optparse.OptionParser(option_list=printing_options) 120 options, args = option_parser.parse_args(args) 121 self._port = port.get('test', options) 122 nproc = 2 123 if single_threaded: 124 nproc = 1 125 126 regular_output = array_stream.ArrayStream() 127 buildbot_output = array_stream.ArrayStream() 128 printer = printing.Printer(self._port, options, regular_output, 129 buildbot_output, single_threaded, 130 is_fully_parallel) 131 return printer, regular_output, buildbot_output 132 133 def get_result(self, test, result_type=test_expectations.PASS, run_time=0): 134 failures = [] 135 if result_type == test_expectations.TIMEOUT: 136 failures = [test_failures.FailureTimeout()] 137 elif result_type == test_expectations.CRASH: 138 failures = [test_failures.FailureCrash()] 139 path = self._port._filesystem.join(self._port.layout_tests_dir(), test) 140 return test_results.TestResult(path, failures=failures, test_run_time=run_time) 141 142 def get_result_summary(self, tests, expectations_str): 143 test_paths = [self._port._filesystem.join(self._port.layout_tests_dir(), test) for 144 test in tests] 145 expectations = test_expectations.TestExpectations( 146 self._port, test_paths, expectations_str, 147 self._port.test_configuration(), 148 is_lint_mode=False) 149 150 rs = result_summary.ResultSummary(expectations, test_paths) 151 return test_paths, rs, expectations 152 153 def test_help_printer(self): 154 # Here and below we'll call the "regular" printer err and the 155 # buildbot printer out; this corresponds to how things run on the 156 # bots with stderr and stdout. 157 printer, err, out = self.get_printer() 158 159 # This routine should print something to stdout. testing what it is 160 # is kind of pointless. 161 printer.help_printing() 162 self.assertFalse(err.empty()) 163 self.assertTrue(out.empty()) 164 165 def do_switch_tests(self, method_name, switch, to_buildbot, 166 message='hello', exp_err=None, exp_bot=None): 167 def do_helper(method_name, switch, message, exp_err, exp_bot): 168 printer, err, bot = self.get_printer(['--print', switch]) 169 getattr(printer, method_name)(message) 170 self.assertEqual(err.get(), exp_err) 171 self.assertEqual(bot.get(), exp_bot) 172 173 if to_buildbot: 174 if exp_err is None: 175 exp_err = [] 176 if exp_bot is None: 177 exp_bot = [message + "\n"] 178 else: 179 if exp_err is None: 180 exp_err = [message + "\n"] 181 if exp_bot is None: 182 exp_bot = [] 183 do_helper(method_name, 'nothing', 'hello', [], []) 184 do_helper(method_name, switch, 'hello', exp_err, exp_bot) 185 do_helper(method_name, 'everything', 'hello', exp_err, exp_bot) 186 187 def test_configure_and_cleanup(self): 188 # This test verifies that calling cleanup repeatedly and deleting 189 # the object is safe. 190 printer, err, out = self.get_printer(['--print', 'everything']) 191 printer.cleanup() 192 printer.cleanup() 193 printer = None 194 195 def test_print_actual(self): 196 # Actual results need to be logged to the buildbot's stream. 197 self.do_switch_tests('print_actual', 'actual', to_buildbot=True) 198 199 def test_print_actual_buildbot(self): 200 # FIXME: Test that the format of the actual results matches what the 201 # buildbot is expecting. 202 pass 203 204 def test_print_config(self): 205 self.do_switch_tests('print_config', 'config', to_buildbot=False) 206 207 def test_print_expected(self): 208 self.do_switch_tests('print_expected', 'expected', to_buildbot=False) 209 210 def test_print_timing(self): 211 self.do_switch_tests('print_timing', 'timing', to_buildbot=False) 212 213 def test_print_update(self): 214 # Note that there shouldn't be a carriage return here; updates() 215 # are meant to be overwritten. 216 self.do_switch_tests('print_update', 'updates', to_buildbot=False, 217 message='hello', exp_err=['hello']) 218 219 def test_print_one_line_summary(self): 220 printer, err, out = self.get_printer(['--print', 'nothing']) 221 printer.print_one_line_summary(1, 1, 0) 222 self.assertTrue(err.empty()) 223 224 printer, err, out = self.get_printer(['--print', 'one-line-summary']) 225 printer.print_one_line_summary(1, 1, 0) 226 self.assertEquals(err.get(), ["All 1 tests ran as expected.\n", "\n"]) 227 228 printer, err, out = self.get_printer(['--print', 'everything']) 229 printer.print_one_line_summary(1, 1, 0) 230 self.assertEquals(err.get(), ["All 1 tests ran as expected.\n", "\n"]) 231 232 err.reset() 233 printer.print_one_line_summary(2, 1, 1) 234 self.assertEquals(err.get(), 235 ["1 test ran as expected, 1 didn't:\n", "\n"]) 236 237 err.reset() 238 printer.print_one_line_summary(3, 2, 1) 239 self.assertEquals(err.get(), 240 ["2 tests ran as expected, 1 didn't:\n", "\n"]) 241 242 err.reset() 243 printer.print_one_line_summary(3, 2, 0) 244 self.assertEquals(err.get(), 245 ['\n', "2 tests ran as expected (1 didn't run).\n", 246 '\n']) 247 248 249 def test_print_test_result(self): 250 # Note here that we don't use meaningful exp_str and got_str values; 251 # the actual contents of the string are treated opaquely by 252 # print_test_result() when tracing, and usually we don't want 253 # to test what exactly is printed, just that something 254 # was printed (or that nothing was printed). 255 # 256 # FIXME: this is actually some goofy layering; it would be nice 257 # we could refactor it so that the args weren't redundant. Maybe 258 # the TestResult should contain what was expected, and the 259 # strings could be derived from the TestResult? 260 printer, err, out = self.get_printer(['--print', 'nothing']) 261 result = self.get_result('passes/image.html') 262 printer.print_test_result(result, expected=False, exp_str='', 263 got_str='') 264 self.assertTrue(err.empty()) 265 266 printer, err, out = self.get_printer(['--print', 'unexpected']) 267 printer.print_test_result(result, expected=True, exp_str='', 268 got_str='') 269 self.assertTrue(err.empty()) 270 printer.print_test_result(result, expected=False, exp_str='', 271 got_str='') 272 self.assertEquals(err.get(), 273 [' passes/image.html -> unexpected pass\n']) 274 275 printer, err, out = self.get_printer(['--print', 'everything']) 276 printer.print_test_result(result, expected=True, exp_str='', 277 got_str='') 278 self.assertTrue(err.empty()) 279 280 printer.print_test_result(result, expected=False, exp_str='', 281 got_str='') 282 self.assertEquals(err.get(), 283 [' passes/image.html -> unexpected pass\n']) 284 285 printer, err, out = self.get_printer(['--print', 'nothing']) 286 printer.print_test_result(result, expected=False, exp_str='', 287 got_str='') 288 self.assertTrue(err.empty()) 289 290 printer, err, out = self.get_printer(['--print', 291 'trace-unexpected']) 292 printer.print_test_result(result, expected=True, exp_str='', 293 got_str='') 294 self.assertTrue(err.empty()) 295 296 printer, err, out = self.get_printer(['--print', 297 'trace-unexpected']) 298 printer.print_test_result(result, expected=False, exp_str='', 299 got_str='') 300 self.assertFalse(err.empty()) 301 302 printer, err, out = self.get_printer(['--print', 303 'trace-unexpected']) 304 result = self.get_result("passes/text.html") 305 printer.print_test_result(result, expected=False, exp_str='', 306 got_str='') 307 self.assertFalse(err.empty()) 308 309 err.reset() 310 printer.print_test_result(result, expected=False, exp_str='', 311 got_str='') 312 self.assertFalse(err.empty()) 313 314 printer, err, out = self.get_printer(['--print', 'trace-everything']) 315 result = self.get_result('passes/image.html') 316 printer.print_test_result(result, expected=True, exp_str='', 317 got_str='') 318 result = self.get_result('failures/expected/missing_text.html') 319 printer.print_test_result(result, expected=True, exp_str='', 320 got_str='') 321 result = self.get_result('failures/expected/missing_check.html') 322 printer.print_test_result(result, expected=True, exp_str='', 323 got_str='') 324 result = self.get_result('failures/expected/missing_image.html') 325 printer.print_test_result(result, expected=True, exp_str='', 326 got_str='') 327 self.assertFalse(err.empty()) 328 329 err.reset() 330 printer.print_test_result(result, expected=False, exp_str='', 331 got_str='') 332 333 def test_print_progress(self): 334 expectations = '' 335 336 # test that we print nothing 337 printer, err, out = self.get_printer(['--print', 'nothing']) 338 tests = ['passes/text.html', 'failures/expected/timeout.html', 339 'failures/expected/crash.html'] 340 paths, rs, exp = self.get_result_summary(tests, expectations) 341 342 printer.print_progress(rs, False, paths) 343 self.assertTrue(out.empty()) 344 self.assertTrue(err.empty()) 345 346 printer.print_progress(rs, True, paths) 347 self.assertTrue(out.empty()) 348 self.assertTrue(err.empty()) 349 350 # test regular functionality 351 printer, err, out = self.get_printer(['--print', 352 'one-line-progress']) 353 printer.print_progress(rs, False, paths) 354 self.assertTrue(out.empty()) 355 self.assertFalse(err.empty()) 356 357 err.reset() 358 out.reset() 359 printer.print_progress(rs, True, paths) 360 self.assertFalse(err.empty()) 361 self.assertTrue(out.empty()) 362 363 def test_print_progress__detailed(self): 364 tests = ['passes/text.html', 'failures/expected/timeout.html', 365 'failures/expected/crash.html'] 366 expectations = 'BUGX : failures/expected/timeout.html = TIMEOUT' 367 368 # first, test that it is disabled properly 369 # should still print one-line-progress 370 printer, err, out = self.get_printer( 371 ['--print', 'detailed-progress'], single_threaded=False) 372 paths, rs, exp = self.get_result_summary(tests, expectations) 373 printer.print_progress(rs, False, paths) 374 self.assertFalse(err.empty()) 375 self.assertTrue(out.empty()) 376 377 # now test the enabled paths 378 printer, err, out = self.get_printer( 379 ['--print', 'detailed-progress'], single_threaded=True) 380 paths, rs, exp = self.get_result_summary(tests, expectations) 381 printer.print_progress(rs, False, paths) 382 self.assertFalse(err.empty()) 383 self.assertTrue(out.empty()) 384 385 err.reset() 386 out.reset() 387 printer.print_progress(rs, True, paths) 388 self.assertFalse(err.empty()) 389 self.assertTrue(out.empty()) 390 391 rs.add(self.get_result('passes/text.html', test_expectations.TIMEOUT), False) 392 rs.add(self.get_result('failures/expected/timeout.html'), True) 393 rs.add(self.get_result('failures/expected/crash.html', test_expectations.CRASH), True) 394 err.reset() 395 out.reset() 396 printer.print_progress(rs, False, paths) 397 self.assertFalse(err.empty()) 398 self.assertTrue(out.empty()) 399 400 # We only clear the meter when retrying w/ detailed-progress. 401 err.reset() 402 out.reset() 403 printer.print_progress(rs, True, paths) 404 self.assertFalse(err.empty()) 405 self.assertTrue(out.empty()) 406 407 printer, err, out = self.get_printer( 408 ['--print', 'detailed-progress,unexpected'], single_threaded=True) 409 paths, rs, exp = self.get_result_summary(tests, expectations) 410 printer.print_progress(rs, False, paths) 411 self.assertFalse(err.empty()) 412 self.assertTrue(out.empty()) 413 414 err.reset() 415 out.reset() 416 printer.print_progress(rs, True, paths) 417 self.assertFalse(err.empty()) 418 self.assertTrue(out.empty()) 419 420 rs.add(self.get_result('passes/text.html', test_expectations.TIMEOUT), False) 421 rs.add(self.get_result('failures/expected/timeout.html'), True) 422 rs.add(self.get_result('failures/expected/crash.html', test_expectations.CRASH), True) 423 err.reset() 424 out.reset() 425 printer.print_progress(rs, False, paths) 426 self.assertFalse(err.empty()) 427 self.assertTrue(out.empty()) 428 429 # We only clear the meter when retrying w/ detailed-progress. 430 err.reset() 431 out.reset() 432 printer.print_progress(rs, True, paths) 433 self.assertFalse(err.empty()) 434 self.assertTrue(out.empty()) 435 436 def test_write_nothing(self): 437 printer, err, out = self.get_printer(['--print', 'nothing']) 438 printer.write("foo") 439 self.assertTrue(err.empty()) 440 441 def test_write_misc(self): 442 printer, err, out = self.get_printer(['--print', 'misc']) 443 printer.write("foo") 444 self.assertFalse(err.empty()) 445 err.reset() 446 printer.write("foo", "config") 447 self.assertTrue(err.empty()) 448 449 def test_write_everything(self): 450 printer, err, out = self.get_printer(['--print', 'everything']) 451 printer.write("foo") 452 self.assertFalse(err.empty()) 453 err.reset() 454 printer.write("foo", "config") 455 self.assertFalse(err.empty()) 456 457 def test_write_verbose(self): 458 printer, err, out = self.get_printer(['--verbose']) 459 printer.write("foo") 460 self.assertTrue(not err.empty() and "foo" in err.get()[0]) 461 self.assertTrue(out.empty()) 462 463 def test_print_unexpected_results(self): 464 # This routine is the only one that prints stuff that the bots 465 # care about. 466 # 467 # FIXME: there's some weird layering going on here. It seems 468 # like we shouldn't be both using an expectations string and 469 # having to specify whether or not the result was expected. 470 # This whole set of tests should probably be rewritten. 471 # 472 # FIXME: Plus, the fact that we're having to call into 473 # run_webkit_tests is clearly a layering inversion. 474 def get_unexpected_results(expected, passing, flaky): 475 """Return an unexpected results summary matching the input description. 476 477 There are a lot of different combinations of test results that 478 can be tested; this routine produces various combinations based 479 on the values of the input flags. 480 481 Args 482 expected: whether the tests ran as expected 483 passing: whether the tests should all pass 484 flaky: whether the tests should be flaky (if False, they 485 produce the same results on both runs; if True, they 486 all pass on the second run). 487 488 """ 489 paths, rs, exp = self.get_result_summary(tests, expectations) 490 if expected: 491 rs.add(self.get_result('passes/text.html', test_expectations.PASS), 492 expected) 493 rs.add(self.get_result('failures/expected/timeout.html', 494 test_expectations.TIMEOUT), expected) 495 rs.add(self.get_result('failures/expected/crash.html', test_expectations.CRASH), 496 expected) 497 elif passing: 498 rs.add(self.get_result('passes/text.html'), expected) 499 rs.add(self.get_result('failures/expected/timeout.html'), expected) 500 rs.add(self.get_result('failures/expected/crash.html'), expected) 501 else: 502 rs.add(self.get_result('passes/text.html', test_expectations.TIMEOUT), 503 expected) 504 rs.add(self.get_result('failures/expected/timeout.html', 505 test_expectations.CRASH), expected) 506 rs.add(self.get_result('failures/expected/crash.html', 507 test_expectations.TIMEOUT), 508 expected) 509 retry = rs 510 if flaky: 511 paths, retry, exp = self.get_result_summary(tests, 512 expectations) 513 retry.add(self.get_result('passes/text.html'), True) 514 retry.add(self.get_result('failures/expected/timeout.html'), True) 515 retry.add(self.get_result('failures/expected/crash.html'), True) 516 unexpected_results = test_runner.summarize_results(self._port, exp, rs, retry, test_timings={}, only_unexpected=True) 517 return unexpected_results 518 519 tests = ['passes/text.html', 'failures/expected/timeout.html', 520 'failures/expected/crash.html'] 521 expectations = '' 522 523 printer, err, out = self.get_printer(['--print', 'nothing']) 524 ur = get_unexpected_results(expected=False, passing=False, flaky=False) 525 printer.print_unexpected_results(ur) 526 self.assertTrue(err.empty()) 527 self.assertTrue(out.empty()) 528 529 printer, err, out = self.get_printer(['--print', 530 'unexpected-results']) 531 532 # test everything running as expected 533 ur = get_unexpected_results(expected=True, passing=False, flaky=False) 534 printer.print_unexpected_results(ur) 535 self.assertTrue(err.empty()) 536 self.assertTrue(out.empty()) 537 538 # test failures 539 err.reset() 540 out.reset() 541 ur = get_unexpected_results(expected=False, passing=False, flaky=False) 542 printer.print_unexpected_results(ur) 543 self.assertTrue(err.empty()) 544 self.assertFalse(out.empty()) 545 546 # test unexpected flaky results 547 err.reset() 548 out.reset() 549 ur = get_unexpected_results(expected=False, passing=True, flaky=False) 550 printer.print_unexpected_results(ur) 551 self.assertTrue(err.empty()) 552 self.assertFalse(out.empty()) 553 554 # test unexpected passes 555 err.reset() 556 out.reset() 557 ur = get_unexpected_results(expected=False, passing=False, flaky=True) 558 printer.print_unexpected_results(ur) 559 self.assertTrue(err.empty()) 560 self.assertFalse(out.empty()) 561 562 err.reset() 563 out.reset() 564 printer, err, out = self.get_printer(['--print', 'everything']) 565 ur = get_unexpected_results(expected=False, passing=False, flaky=False) 566 printer.print_unexpected_results(ur) 567 self.assertTrue(err.empty()) 568 self.assertFalse(out.empty()) 569 570 expectations = """ 571 BUGX : failures/expected/crash.html = CRASH 572 BUGX : failures/expected/timeout.html = TIMEOUT 573 """ 574 err.reset() 575 out.reset() 576 ur = get_unexpected_results(expected=False, passing=False, flaky=False) 577 printer.print_unexpected_results(ur) 578 self.assertTrue(err.empty()) 579 self.assertFalse(out.empty()) 580 581 err.reset() 582 out.reset() 583 ur = get_unexpected_results(expected=False, passing=True, flaky=False) 584 printer.print_unexpected_results(ur) 585 self.assertTrue(err.empty()) 586 self.assertFalse(out.empty()) 587 588 # Test handling of --verbose as well. 589 err.reset() 590 out.reset() 591 printer, err, out = self.get_printer(['--verbose']) 592 ur = get_unexpected_results(expected=False, passing=False, flaky=False) 593 printer.print_unexpected_results(ur) 594 self.assertTrue(err.empty()) 595 self.assertFalse(out.empty()) 596 597 def test_print_unexpected_results_buildbot(self): 598 # FIXME: Test that print_unexpected_results() produces the printer the 599 # buildbot is expecting. 600 pass 601 602 if __name__ == '__main__': 603 unittest.main() 604