Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(590)

Side by Side Diff: tests/selenium/main.py

Issue 212031: Changed selenium tests to recover from test crashes/hangs. Divided perceptual... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/o3d/
Patch Set: '' Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tests/selenium/javascript_unit_tests.py ('k') | tests/selenium/pdiff_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python2.4 1 #!/usr/bin/python2.4
2 # Copyright 2009, Google Inc. 2 # Copyright 2009, Google Inc.
3 # All rights reserved. 3 # All rights reserved.
4 # 4 #
5 # Redistribution and use in source and binary forms, with or without 5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are 6 # modification, are permitted provided that the following conditions are
7 # met: 7 # met:
8 # 8 #
9 # * Redistributions of source code must retain the above copyright 9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer. 10 # notice, this list of conditions and the following disclaimer.
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 import re 54 import re
55 import SimpleHTTPServer 55 import SimpleHTTPServer
56 import socket 56 import socket
57 import SocketServer 57 import SocketServer
58 import subprocess 58 import subprocess
59 import threading 59 import threading
60 import time 60 import time
61 import unittest 61 import unittest
62 import gflags 62 import gflags
63 import javascript_unit_tests 63 import javascript_unit_tests
64 # Import custom testrunner for pulse 64 import test_runner
65 import pulse_testrunner
66 import selenium 65 import selenium
67 import samples_tests 66 import samples_tests
68 import selenium_constants 67 import selenium_constants
69 import selenium_utilities 68 import selenium_utilities
69 import pdiff_test
70 import Queue
70 71
71 if sys.platform == 'win32' or sys.platform == 'cygwin': 72 if sys.platform == 'win32' or sys.platform == 'cygwin':
72 default_java_exe = "java.exe" 73 default_java_exe = "java.exe"
73 else: 74 else:
74 default_java_exe = "java" 75 default_java_exe = "java"
75 76
76 # Command line flags 77 # Command line flags
77 FLAGS = gflags.FLAGS 78 FLAGS = gflags.FLAGS
78 gflags.DEFINE_boolean("verbose", False, "verbosity") 79 gflags.DEFINE_boolean("verbose", False, "verbosity")
79 gflags.DEFINE_boolean("screenshots", False, "takes screenshots") 80 gflags.DEFINE_boolean("screenshots", False, "takes screenshots")
(...skipping 27 matching lines...) Expand all
107 "specifies the prefix of tests to run") 108 "specifies the prefix of tests to run")
108 gflags.DEFINE_string( 109 gflags.DEFINE_string(
109 "testsuffixes", 110 "testsuffixes",
110 "small,medium,large", 111 "small,medium,large",
111 "specifies the suffixes, separated by commas of tests to run") 112 "specifies the suffixes, separated by commas of tests to run")
112 gflags.DEFINE_string( 113 gflags.DEFINE_string(
113 "servertimeout", 114 "servertimeout",
114 "30", 115 "30",
115 "Specifies the timeout value, in seconds, for the selenium server.") 116 "Specifies the timeout value, in seconds, for the selenium server.")
116 117
118
117 # Browsers to choose from (for browser flag). 119 # Browsers to choose from (for browser flag).
118 # use --browser $BROWSER_NAME to run 120 # use --browser $BROWSER_NAME to run
119 # tests for that browser 121 # tests for that browser
120 gflags.DEFINE_list( 122 gflags.DEFINE_list(
121 "browser", 123 "browser",
122 "*firefox", 124 "*firefox",
123 "\n".join(["comma-separated list of browsers to test", 125 "\n".join(["comma-separated list of browsers to test",
124 "Options:"] + 126 "Options:"] +
125 selenium_constants.SELENIUM_BROWSER_SET)) 127 selenium_constants.SELENIUM_BROWSER_SET))
126 gflags.DEFINE_string( 128 gflags.DEFINE_string(
127 "browserpath", 129 "browserpath",
128 "", 130 "",
129 "specifies the path to the browser executable " 131 "specifies the path to the browser executable "
130 "(for platforms that don't support MOZ_PLUGIN_PATH)") 132 "(for platforms that don't support MOZ_PLUGIN_PATH)")
131 gflags.DEFINE_string( 133 gflags.DEFINE_string(
132 "samplespath", 134 "samplespath",
133 "", 135 "",
134 "specifies the path from the web root to the samples.") 136 "specifies the path from the web root to the samples.")
135 137
136
137 class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 138 class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
138 """Hook to handle HTTP server requests. 139 """Hook to handle HTTP server requests.
139 140
140 Functions as a handler for logging and other utility functions. 141 Functions as a handler for logging and other utility functions.
141 """ 142 """
142 143
143 def log_message(self, format, *args): 144 def log_message(self, format, *args):
144 """Logging hook for HTTP server.""" 145 """Logging hook for HTTP server."""
145 146
146 # For now, just suppress logging. 147 # For now, just suppress logging.
147 pass 148 pass
148 # TODO: might be nice to have a verbose option for debugging. 149 # TODO: might be nice to have a verbose option for debugging.
149 150
151
152
150 153
151 class LocalFileHTTPServer(threading.Thread): 154 class LocalFileHTTPServer(threading.Thread):
152 """Minimal HTTP server that serves local files. 155 """Minimal HTTP server that serves local files.
153 156
154 Members: 157 Members:
155 http_alive: event to signal that http server is up and running 158 http_alive: event to signal that http server is up and running
156 http_port: the TCP port the server is using 159 http_port: the TCP port the server is using
157 """ 160 """
158 161
159 START_PORT = 8100 162 START_PORT = 8100
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after
341 if not selenium_server.selenium_port: 344 if not selenium_server.selenium_port:
342 print 'Timed out.' 345 print 'Timed out.'
343 return None 346 return None
344 347
345 print("Selenium RC server started on port %d" 348 print("Selenium RC server started on port %d"
346 % selenium_server.selenium_port) 349 % selenium_server.selenium_port)
347 350
348 return selenium_server 351 return selenium_server
349 352
350 353
351 class SeleniumSession(object): 354 class SeleniumSessionBuilder:
352 """A selenium browser session, with support servers. 355 def __init__(self, sel_port, sel_timeout, http_port, browserpath):
353 356
354 The support servers include a Selenium Remote Control server, and 357 self.sel_port = sel_port
355 a local HTTP server to serve static test files. 358 self.sel_timeout = sel_timeout
359 self.http_port = http_port
360 self.browserpath = browserpath
356 361
357 Members: 362 def NewSeleniumSession(self, browser):
358 session: a selenium() instance
359 selenium_server: a SeleniumRemoteControl() instance
360 http_server: a LocalFileHTTPServer() instance
361 runner: a TestRunner() instance
362 """
363
364 def __init__(self, verbose, java_path, selenium_server, server_timeout,
365 http_root=None):
366 """Initializes a Selenium Session.
367
368 Args:
369 verbose: boolean verbose flag
370 java_path: path to java used to run selenium.
371 selenium_server: path to jar containing selenium server.
372 server_timeout: server timeout value, in seconds.
373 http_root: Serve http pages using this path as the document root. When
374 None, use the default.
375 """
376 # Start up a static file server, to serve the test pages.
377
378 if not http_root:
379 http_root = FLAGS.product_dir
380
381 self.http_server = LocalFileHTTPServer.StartServer(http_root)
382
383 if self.http_server:
384 # Start up the Selenium Remote Control Server
385 self.selenium_server = SeleniumRemoteControl.StartServer(verbose,
386 java_path,
387 selenium_server,
388 server_timeout)
389 if not self.http_server or not self.selenium_server:
390 return
391
392 # Set up a testing runner
393 self.runner = pulse_testrunner.PulseTestRunner()
394
395 # Set up a phantom selenium session so we can call shutdown if needed.
396 self.session = selenium.selenium(
397 "localhost", self.selenium_server.selenium_port, "*firefox",
398 "http://" + socket.gethostname() + ":" +
399 str(self.http_server.http_port))
400
401 def StartSession(self, browser):
402 """Starts the Selenium Session and connects to the HTTP server.
403
404 Args:
405 browser: selenium browser name
406 """
407
408 if browser == "*googlechrome": 363 if browser == "*googlechrome":
409 # TODO: Replace socket.gethostname() with "localhost" 364 # TODO: Replace socket.gethostname() with "localhost"
410 # once Chrome local proxy fix is in. 365 # once Chrome local proxy fix is in.
411 server_url = "http://" + socket.gethostname() + ":" 366 server_url = "http://" + socket.gethostname() + ":"
412 else: 367 else:
413 server_url = "http://localhost:" 368 server_url = "http://localhost:"
414 server_url += str(self.http_server.http_port) 369 server_url += str(self.http_port)
415 370
416 browser_path_with_space = "" 371 browser_path_with_space = ""
417 if FLAGS.browserpath: 372 if self.browserpath:
418 browser_path_with_space = " " + FLAGS.browserpath 373 browser_path_with_space = " " + self.browserpath
419
420 self.session = selenium.selenium("localhost",
421 self.selenium_server.selenium_port,
422 browser + browser_path_with_space,
423 server_url)
424 self.session.start()
425
426 def CloseSession(self):
427 """Closes the selenium sesssion."""
428 self.session.stop()
429
430 def TearDown(self):
431 """Stops the selenium server."""
432 self.session.shut_down_selenium_server()
433
434 def TestBrowser(self, browser, test_list, test_prefix, test_suffixes,
435 server_timeout):
436 """Runs Selenium tests for a specific browser.
437
438 Args:
439 browser: selenium browser name (eg. *iexplore, *firefox).
440 test_list: list to add tests to.
441 test_prefix: prefix of tests to run.
442 test_suffixes: comma separated suffixes of tests to run.
443 server_timeout: server timeout value, in milliseconds
444
445 Returns:
446 result: result of test runner.
447 """
448 print "Testing %s..." % browser
449 self.StartSession(browser)
450 self.session.set_timeout(server_timeout)
451 self.runner.setBrowser(browser)
452
453 try:
454 result = self.runner.run(
455 SeleniumSuite(self.session, browser, test_list,
456 test_prefix, test_suffixes))
457 finally:
458 self.CloseSession()
459
460 return result
461 374
462 375
463 class LocalTestSuite(unittest.TestSuite): 376 new_session = selenium.selenium("localhost",
464 """Wrapper for unittest.TestSuite so we can collect the tests.""" 377 self.sel_port,
378 browser + browser_path_with_space,
379 server_url)
380
381 new_session.start()
382 new_session.set_timeout(self.sel_timeout)
383
384 return new_session
465 385
466 def __init__(self):
467 unittest.TestSuite.__init__(self)
468 self.test_list = []
469 386
470 def addTest(self, name, test): 387 def TestBrowser(session_builder, browser, test_list):
471 """Adds a test to the TestSuite and records its name and test_path. 388 """Runs Selenium tests for a specific browser.
472 389
473 Args: 390 Args:
474 name: name of test. 391 session_builder: session_builder for creating new selenium sessions.
475 test: test to pass to unittest.TestSuite. 392 browser: selenium browser name (eg. *iexplore, *firefox).
476 """ 393 test_list: list of tests.
477 unittest.TestSuite.addTest(self, test) 394
478 try: 395 Returns:
479 self.test_list.append((name, test.options)) 396 summary_result: result of test runners.
480 except AttributeError: 397 """
481 self.test_list.append((name, [])) 398 print "Testing %s..." % browser
399
400 summary_result = test_runner.TestResult(test_runner.StringBuffer(), browser)
401
402 # Fill up the selenium test queue.
403 test_queue = Queue.Queue()
404 for test in test_list:
405 test_queue.put(test)
406
407
408 pdiff_queue = None
409 if FLAGS.screenshots:
410 # Need to do screen comparisons.
411 # |pdiff_queue| is the queue of perceptual diff tests that need to be done.
412 # This queue is added to by individual slenium test runners.
413 # |pdiff_result_queue| is the result of the perceptual diff tests.
414 pdiff_queue = Queue.Queue()
415 pdiff_result_queue = Queue.Queue()
416 pdiff_worker = test_runner.PDiffTestRunner(pdiff_queue,
417 pdiff_result_queue,
418 browser)
419 pdiff_worker.start()
420
421 # Start initial selenium test runner.
422 worker = test_runner.SeleniumTestRunner(session_builder, browser,
423 test_queue, pdiff_queue)
424 worker.start()
425
426 # Run through all selenium tests.
427 while not worker.IsCompletelyDone():
428 if worker.IsTesting() and worker.IsPastDeadline():
429 # Test has taken more than allotted. Abort and go to next test.
430 worker.AbortTest()
431
432 elif worker.DidFinishTest():
433 # Do this so that a worker does not grab test off queue till we tell it.
434 result = worker.Continue()
435 result.printAll(sys.stdout)
436 summary_result.merge(result)
437
438 if FLAGS.screenshots:
439 # Finish screenshot comparisons.
440 pdiff_worker.EndTesting()
441 while not pdiff_worker.IsCompletelyDone():
442 time.sleep(1)
443
444 # Be careful here, make sure no one else is editing |pdiff_reult_queue|.
445 while not pdiff_result_queue.empty():
446 result = pdiff_result_queue.get()
447 result.printAll(sys.stdout)
448 summary_result.merge(result)
449
450 return summary_result
451
482 452
483 453
484 def MatchesSuffix(name, suffixes): 454 def MatchesSuffix(name, suffixes):
485 """Checks if a name ends in one of the suffixes. 455 """Checks if a name ends in one of the suffixes.
486 456
487 Args: 457 Args:
488 name: Name to test. 458 name: Name to test.
489 suffixes: list of suffixes to test for. 459 suffixes: list of suffixes to test for.
490 Returns: 460 Returns:
491 True if name ends in one of the suffixes or if suffixes is empty. 461 True if name ends in one of the suffixes or if suffixes is empty.
492 """ 462 """
493 if suffixes: 463 if suffixes:
494 name_lower = name.lower() 464 name_lower = name.lower()
495 for suffix in suffixes: 465 for suffix in suffixes:
496 if name_lower.endswith(suffix): 466 if name_lower.endswith(suffix):
497 return True 467 return True
498 return False 468 return False
499 else: 469 else:
500 return True 470 return True
501 471
502 472
503 def AddTests(test_suite, session, browser, module, filename, prefix, 473 def _GetTestsFromFile(filename, prefix, test_prefix_filter, test_suffixes,
504 test_prefix_filter, test_suffixes, path_to_html): 474 browser, module, path_to_html):
505 """Add tests defined in filename. 475 """Add tests defined in filename, and associated perceptual diff test, if
476 needed.
506 477
507 Assumes module has a method "GenericTest" that uses self.args to run. 478 Assumes module has a method "GenericTest" that uses self.args to run.
508 479
509 Args: 480 Args:
510 test_suite: A Selenium test_suite to add tests to.
511 session: a Selenium instance.
512 browser: browser name.
513 module: module which will have method GenericTest() called to run each test.
514 filename: filename of file with list of tests. 481 filename: filename of file with list of tests.
515 prefix: prefix to add to the beginning of each test. 482 prefix: prefix to add to the beginning of each test.
516 test_prefix_filter: Only adds a test if it starts with this. 483 test_prefix_filter: Only adds a test if it starts with this.
517 test_suffixes: list of suffixes to filter by. An empty list = pass all. 484 test_suffixes: list of suffixes to filter by. An empty list = pass all.
485 browser: browser name.
486 module: module which will have method GenericTest() called to run each test.
518 path_to_html: Path from server root to html 487 path_to_html: Path from server root to html
519 """ 488 """
520 # See comments in that file for the expected format. 489 # See comments in that file for the expected format.
521 # skip lines that are blank or have "#" or ";" as their first non whitespace 490 # skip lines that are blank or have "#" or ";" as their first non whitespace
522 # character. 491 # character.
523 test_list_file = open(filename, "r") 492 test_list_file = open(filename, "r")
524 samples = test_list_file.readlines() 493 samples = test_list_file.readlines()
525 test_list_file.close() 494 test_list_file.close()
526 495
496 tests = []
497
527 for sample in samples: 498 for sample in samples:
528 sample = sample.strip() 499 sample = sample.strip()
529 if not sample or sample[0] == ";" or sample[0] == "#": 500 if not sample or sample[0] == ";" or sample[0] == "#":
530 continue 501 continue
531 502
532 arguments = sample.split() 503 arguments = sample.split()
533 test_type = arguments[0].lower() 504 test_type = arguments[0].lower()
534 test_path = arguments[1] 505 test_path = arguments[1]
535 options = arguments[2:] 506 options = arguments[2:]
536 507
537 # TODO: Add filter based on test_type 508 # TODO: Add filter based on test_type
538 509 if test_path.startswith("Test"):
539 name = ("Test" + prefix + re.sub("\W", "_", test_path) + 510 name = test_path
540 test_type.capitalize()) 511 else:
512 # Need to make a name.
513 name = ("Test" + prefix + re.sub("\W", "_", test_path) +
514 test_type.capitalize())
541 515
542 # Only execute this test if the current browser is not in the list 516 # Only execute this test if the current browser is not in the list
543 # of skipped browsers. 517 # of skipped browsers.
544 test_skipped = False 518 test_skipped = False
519 screenshot_count = 0
545 for option in options: 520 for option in options:
546 if option.startswith("except"): 521 if option.startswith("except"):
547 skipped_platforms = selenium_utilities.GetArgument(option) 522 skipped_platforms = selenium_utilities.GetArgument(option)
548 if not skipped_platforms is None: 523 if not skipped_platforms is None:
549 skipped_platforms = skipped_platforms.split(",") 524 skipped_platforms = skipped_platforms.split(",")
550 if browser in skipped_platforms: 525 if browser in skipped_platforms:
551 test_skipped = True 526 test_skipped = True
527 elif option.startswith("screenshots"):
528 screenshot_count += int(selenium_utilities.GetArgument(option))
529 elif option.startswith("screenshot"):
530 screenshot_count += 1
531
532 if (test_prefix_filter and not name.startswith(test_prefix_filter) or
533 test_suffixes and not MatchesSuffix(name, test_suffixes)):
534 test_skipped = True
552 535
553 if not test_skipped: 536 if not test_skipped:
554 # Check if there is already a test function by this name in the module. 537 # Add a test method with this name if it doesn't exist.
555 if (test_path.startswith(test_prefix_filter) and 538 if not (hasattr(module, name) and callable(getattr(module, name))):
556 hasattr(module, test_path) and callable(getattr(module, test_path))):
557 test_suite.addTest(test_path, module(test_path, session, browser,
558 path_to_html, options=options))
559 elif (name.startswith(test_prefix_filter) and
560 MatchesSuffix(name, test_suffixes)):
561 # no, so add a method that will run a test generically.
562 setattr(module, name, module.GenericTest) 539 setattr(module, name, module.GenericTest)
563 test_suite.addTest(name, module(name, session, browser, path_to_html, 540
564 test_type, test_path, options)) 541 new_test = module(name, browser, path_to_html, test_type, test_path,
542 options)
543
544 if screenshot_count and FLAGS.screenshots:
545 pdiff_name = name + 'Screenshots'
546 screenshot = selenium_utilities.ScreenshotNameFromTestName(test_path)
547 setattr(pdiff_test.PDiffTest, pdiff_name,
548 pdiff_test.PDiffTest.PDiffTest)
549 new_pdiff = pdiff_test.PDiffTest(pdiff_name,
550 screenshot_count,
551 screenshot,
552 FLAGS.screencompare,
553 FLAGS.screenshotsdir,
554 FLAGS.referencedir,
555 options)
556 tests += [(new_test, new_pdiff)]
557 else:
558 tests += [new_test]
559
560
561 return tests
565 562
566 563
567 def SeleniumSuite(session, browser, test_list, test_prefix, test_suffixes): 564 def GetTestsForBrowser(browser, test_prefix, test_suffixes):
568 """Creates a test suite to run the unit tests. 565 """Returns list of tests from test files.
569 566
570 Args: 567 Args:
571 session: a selenium() instance
572 browser: browser name 568 browser: browser name
573 test_list: list to add tests to.
574 test_prefix: prefix of tests to run. 569 test_prefix: prefix of tests to run.
575 test_suffixes: A comma separated string of suffixes to filter by. 570 test_suffixes: A comma separated string of suffixes to filter by.
576 Returns: 571 Returns:
577 A selenium test suite. 572 A list of unittest.TestCase.
578 """ 573 """
579 574 tests = []
580 test_suite = LocalTestSuite()
581
582 suffixes = test_suffixes.split(",") 575 suffixes = test_suffixes.split(",")
583 576
584 # add sample tests. 577 # add sample tests.
585 filename = os.path.abspath(os.path.join(script_dir, "sample_list.txt")) 578 filename = os.path.abspath(os.path.join(script_dir, "sample_list.txt"))
586 AddTests(test_suite, 579 tests += _GetTestsFromFile(filename, "Sample", test_prefix, suffixes, browser,
587 session, 580 samples_tests.SampleTests,
588 browser, 581 FLAGS.samplespath.replace("\\","/"))
589 samples_tests.SampleTests, 582
590 filename,
591 "Sample",
592 test_prefix,
593 suffixes,
594 FLAGS.samplespath.replace("\\", "/"))
595
596 # add javascript tests. 583 # add javascript tests.
597 filename = os.path.abspath(os.path.join(script_dir, 584 filename = os.path.abspath(os.path.join(script_dir,
598 "javascript_unit_test_list.txt")) 585 "javascript_unit_test_list.txt"))
599 AddTests(test_suite, 586 tests += _GetTestsFromFile(filename, "UnitTest", test_prefix, suffixes,
600 session, 587 browser, javascript_unit_tests.JavaScriptUnitTests,
601 browser, 588 "")
602 javascript_unit_tests.JavaScriptUnitTests,
603 filename,
604 "UnitTest",
605 test_prefix,
606 suffixes,
607 '')
608 589
609 test_list += test_suite.test_list 590 return tests
610
611 return test_suite
612 591
613 592
614 def CompareScreenshots(browser, test_list, screencompare, screenshotsdir, 593 def GetChromePath():
615 screencompare_tool, verbose): 594 value = None
616 """Performs the image validation for test-case frame captures. 595 if sys.platform == "win32" or sys.platform == "cygwin":
617 596 import _winreg
618 Args: 597 try:
619 browser: selenium browser name 598 key = _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT,
620 test_list: list of tests that ran. 599 "Applications\\chrome.exe\\shell\\open\\command")
621 screencompare: True to actually run tests. 600 (value, type) = _winreg.QueryValueEx(key, None)
622 screenshotsdir: path of directory containing images to compare. 601 _winreg.CloseKey(key)
623 screencompare_tool: path to image diff tool. 602 value = os.path.dirname(value)
624 verbose: If True then outputs verbose info. 603
625 604 except WindowsError:
626 Returns: 605 value = None
627 A Results object. 606 if '*googlechrome' in FLAGS.browser:
628 """ 607 raise Exception("Unable to determine location for Chrome -- " +
629 print "Validating captured frames against reference data..." 608 "is it installed?")
630 609
631 class Results(object): 610 return value
632 """An object to return results of screenshot compares.
633
634 Similar to unittest.TestResults.
635 """
636
637 def __init__(self):
638 object.__init__(self)
639 self.tests_run = 0
640 self.current_test = None
641 self.errors = []
642 self.failures = []
643 self.start_time = 0
644
645 def StartTest(self, test):
646 """Adds a test.
647
648 Args:
649 test: name of test.
650 """
651 self.start_time = time.time()
652 self.tests_run += 1
653 self.current_test = test
654
655 def TimeTaken(self):
656 """Returns the time since the last call to StartTest."""
657 return time.time() - self.start_time
658
659 def AddFailure(self, test, browser, message):
660 """Adds a failure.
661
662 Args:
663 test: name of the test.
664 browser: name of the browser.
665 message: error message to print
666 """
667 self.failures.append(test)
668 print "ERROR: ", message
669 print("SELENIUMRESULT %s <%s> [%.3fs]: FAIL"
670 % (test, browser, self.TimeTaken()))
671
672 def AddSuccess(self, test):
673 """Adds a success.
674
675 Args:
676 test: name of the test.
677 """
678 print("SELENIUMRESULT %s <%s> [%.3fs]: PASS"
679 % (test, browser, self.TimeTaken()))
680
681 def WasSuccessful(self):
682 """Returns true if all tests were successful."""
683 return not self.errors and not self.failures
684
685 results = Results()
686
687 if not screencompare:
688 return results
689
690 base_path = os.getcwd()
691
692 reference_files = os.listdir(os.path.join(
693 base_path,
694 selenium_constants.REFERENCE_SCREENSHOT_PATH))
695
696 generated_files = os.listdir(os.path.join(base_path, screenshotsdir))
697
698 # Prep the test list for matching
699 temp = []
700 for (test, options) in test_list:
701 test = selenium_utilities.StripTestTypeSuffix(test)
702 temp.append((test.lower(), options))
703 test_list = temp
704
705 # Create regex object for filename
706 # file is in format "FILENAME_reference.png"
707 reference_file_name_regex = re.compile(r"^(.*)_reference\.png")
708 generated_file_name_regex = re.compile(r"^(.*)\.png")
709
710 # check that there is a reference file for each generated file.
711 for file_name in generated_files:
712 match = generated_file_name_regex.search(file_name)
713
714 if match is None:
715 # no matches
716 continue
717
718 # Generated file name without png extension
719 actual_name = match.group(1)
720
721 # Get full paths to reference and generated files
722 reference_file = os.path.join(
723 base_path,
724 selenium_constants.REFERENCE_SCREENSHOT_PATH,
725 actual_name + "_reference.png")
726 generated_file = os.path.join(
727 base_path,
728 screenshotsdir,
729 actual_name + ".png")
730
731 test_name = "TestReferenceScreenshotExists_" + actual_name
732 results.StartTest(test_name)
733 if not os.path.exists(reference_file):
734 # reference file does not exist
735 results.AddFailure(
736 test_name, browser,
737 "Missing reference file %s for generated file %s." %
738 (reference_file, generated_file))
739 else:
740 results.AddSuccess(test_name)
741
742 # Assuming both the result and reference image sets are the same size,
743 # verify that corresponding images are similar within tolerance.
744 for file_name in reference_files:
745 match = reference_file_name_regex.search(file_name)
746
747 if match is None:
748 # no matches
749 continue
750
751 # Generated file name without png extension
752 actual_name = match.group(1)
753 # Get full paths to reference and generated files
754 reference_file = os.path.join(
755 base_path,
756 selenium_constants.REFERENCE_SCREENSHOT_PATH,
757 file_name)
758 platform_specific_reference_file = os.path.join(
759 base_path,
760 selenium_constants.PLATFORM_SPECIFIC_REFERENCE_SCREENSHOT_PATH,
761 actual_name + "_reference.png")
762 generated_file = os.path.join(
763 base_path,
764 screenshotsdir,
765 actual_name + ".png")
766
767 # Generate a test case name
768 test_name = "TestScreenCompare_" + actual_name
769
770 # skip the reference file if the test is not in the test list.
771 basename = os.path.splitext(os.path.basename(file_name))[0]
772 basename = re.sub("\d+_reference", "", basename).lower()
773 basename = re.sub("\W", "_", basename)
774 test_was_run = False
775 test_options = []
776 for (test, options) in test_list:
777 if test.endswith(basename):
778 test_was_run = True
779 test_options = options or []
780 break
781
782 if test_was_run:
783 results.StartTest(test_name)
784 else:
785 # test was not planned to run for this reference image.
786 if os.path.exists(generated_file):
787 # a generated file exists? The test name does not match the screenshot.
788 results.StartTest(test_name)
789 results.AddFailure(test_name, browser,
790 "Test name and screenshot name do not match.")
791 continue
792
793 # Check if there is a platform specific version of the reference image
794 if os.path.exists(platform_specific_reference_file):
795 reference_file = platform_specific_reference_file
796
797 # Check if perceptual diff exists
798 pdiff_path = os.path.join(base_path, screencompare_tool)
799 if not os.path.exists(pdiff_path):
800 # Perceptualdiff.exe does not exist, fail.
801 results.AddFailure(
802 test_name, browser,
803 "Perceptual diff %s does not exist." % pdiff_path)
804 continue
805
806 pixel_threshold = "10"
807 alpha_threshold = "1.0"
808 use_colorfactor = False
809 use_downsample = False
810 use_edge = True
811 edge_threshold = "5"
812
813 # Find out if the test specified any options relating to perceptual diff
814 # that will override the defaults.
815 for opt in test_options:
816 if opt.startswith("pdiff_threshold"):
817 pixel_threshold = selenium_utilities.GetArgument(opt)
818 elif (opt.startswith("pdiff_threshold_mac") and
819 sys.platform == "darwin"):
820 pixel_threshold = selenium_utilities.GetArgument(opt)
821 elif (opt.startswith("pdiff_threshold_win") and
822 sys.platform == 'win32' or sys.platform == "cygwin"):
823 pixel_threshold = selenium_utilities.GetArgument(opt)
824 elif (opt.startswith("pdiff_threshold_linux") and
825 sys.platform[:5] == "linux"):
826 pixel_threshold = selenium_utilities.GetArgument(opt)
827 elif (opt.startswith("colorfactor")):
828 colorfactor = selenium_utilities.GetArgument(opt)
829 use_colorfactor = True
830 elif (opt.startswith("downsample")):
831 downsample_factor = selenium_utilities.GetArgument(opt)
832 use_downsample = True
833 elif (opt.startswith("pdiff_edge_ignore_off")):
834 use_edge = False
835 elif (opt.startswith("pdiff_edge_threshold")):
836 edge_threshold = selenium_utilities.GetArgument(opt)
837
838 # Check if file exists
839 if os.path.exists(generated_file):
840 diff_file = os.path.join(base_path, screenshotsdir,
841 "compare_%s.png" % actual_name)
842
843 # Run perceptual diff
844 arguments = [pdiff_path,
845 reference_file,
846 generated_file,
847 "-output", diff_file,
848 "-fov", "45",
849 "-alphaThreshold", alpha_threshold,
850 # Turn on verbose output for the percetual diff so we
851 # can see how far off we are on the threshold.
852 "-verbose",
853 # Set the threshold to zero so we can get a count
854 # of the different pixels. This causes the program
855 # to return failure for most images, but we can compare
856 # the values ourselves below.
857 "-threshold", "0"]
858 if use_colorfactor:
859 arguments += ["-colorfactor", colorfactor]
860 if use_downsample:
861 arguments += ["-downsample", downsample_factor]
862 if use_edge:
863 arguments += ["-ignoreEdges", edge_threshold]
864
865 # Print the perceptual diff command line so we can debug easier.
866 if verbose:
867 print " ".join(arguments)
868
869 # diff tool should return 0 on success
870 expected_result = 0
871
872 pdiff_pipe = subprocess.Popen(arguments,
873 stdout=subprocess.PIPE,
874 stderr=subprocess.PIPE)
875 (pdiff_stdout, pdiff_stderr) = pdiff_pipe.communicate()
876 result = pdiff_pipe.returncode
877
878 # Find out how many pixels were different by looking at the output.
879 pixel_re = re.compile("(\d+) pixels are different", re.DOTALL)
880 pixel_match = pixel_re.search(pdiff_stdout)
881 different_pixels = "0"
882 if pixel_match:
883 different_pixels = pixel_match.group(1)
884
885 alpha_re = re.compile("max alpha delta of ([0-9\.]+)", re.DOTALL)
886 alpha_delta = "0.0"
887 alpha_match = alpha_re.search(pdiff_stdout)
888 if alpha_match:
889 alpha_delta = alpha_match.group(1)
890
891 if (result == expected_result or (pixel_match and
892 int(different_pixels) <= int(pixel_threshold))):
893 # The perceptual diff passed.
894 pass_re = re.compile("PASS: (.*?)\n", re.DOTALL)
895 pass_match = pass_re.search(pdiff_stdout)
896 reason = "Images are not perceptually different."
897 if pass_match:
898 reason = pass_match.group(1)
899 print ("%s PASSED with %s different pixels "
900 "(threshold %s) because: %s" % (test_name,
901 different_pixels,
902 pixel_threshold,
903 reason))
904 results.AddSuccess(test_name)
905 else:
906 # The perceptual diff failed.
907 if pixel_match and int(different_pixels) > int(pixel_threshold):
908 results.AddFailure(
909 test_name, browser,
910 ("Reference framebuffer (%s) does not match generated "
911 "file (%s): %s non-matching pixels, max alpha delta: %s, "
912 "threshold: %s, alphaThreshold: %s." %
913 (reference_file, generated_file, different_pixels, alpha_delta,
914 pixel_threshold, alpha_threshold)))
915 else:
916 # The perceptual diff failed for some reason other than
917 # pixel differencing.
918 fail_re = re.compile("FAIL: (.*?)\n", re.DOTALL)
919 fail_match = fail_re.search(pdiff_stdout)
920 reason = "Unknown failure"
921 if fail_match:
922 reason = fail_match.group(1)
923 results.AddFailure(
924 test_name, browser,
925 ("Perceptual diff of reference (%s) and generated (%s) files "
926 "failed because: %s" %
927 (reference_file, generated_file, reason)))
928 else:
929 # Generated file does not exist
930 results.AddFailure(test_name, browser,
931 "File %s does not exist." % generated_file)
932
933 return results
934 611
935 612
936 def main(unused_argv): 613 def main(unused_argv):
937 # Boolean to record if all tests passed. 614 # Boolean to record if all tests passed.
938 all_tests_passed = True 615 all_tests_passed = True
939 616
940 selenium_constants.REFERENCE_SCREENSHOT_PATH = os.path.join( 617 selenium_constants.REFERENCE_SCREENSHOT_PATH = os.path.join(
941 FLAGS.referencedir, 618 FLAGS.referencedir,
942 "reference", 619 "reference",
943 "") 620 "")
944 selenium_constants.PLATFORM_SPECIFIC_REFERENCE_SCREENSHOT_PATH = os.path.join( 621 selenium_constants.PLATFORM_SPECIFIC_REFERENCE_SCREENSHOT_PATH = os.path.join(
945 FLAGS.referencedir, 622 FLAGS.referencedir,
946 selenium_constants.PLATFORM_SCREENSHOT_DIR, 623 selenium_constants.PLATFORM_SCREENSHOT_DIR,
947 "") 624 "")
625
626 # Launch HTTP server.
627 http_server = LocalFileHTTPServer.StartServer(FLAGS.product_dir)
948 628
949 # Open a new session to Selenium Remote Control 629 if not http_server:
950 selenium_session = SeleniumSession(FLAGS.verbose, FLAGS.java, 630 print "Could not start a local http server with root." % FLAGS.product_dir
951 os.path.abspath(FLAGS.selenium_server), 631 return 1
952 FLAGS.servertimeout) 632
953 if not selenium_session.http_server or not selenium_session.selenium_server: 633
634 # Start Selenium Remote Control and Selenium Session Builder.
635 sel_server_jar = os.path.abspath(FLAGS.selenium_server)
636 sel_server = SeleniumRemoteControl.StartServer(
637 FLAGS.verbose, FLAGS.java, sel_server_jar,
638 FLAGS.servertimeout)
639
640 if not sel_server:
641 print "Could not start selenium server at %s." % sel_server_jar
954 return 1 642 return 1
955 643
644 session_builder = SeleniumSessionBuilder(
645 sel_server.selenium_port,
646 int(FLAGS.servertimeout) * 1000,
647 http_server.http_port,
648 FLAGS.browserpath)
649
650 all_tests_passed = True
651 # Test browsers.
956 for browser in FLAGS.browser: 652 for browser in FLAGS.browser:
957 if browser in set(selenium_constants.SELENIUM_BROWSER_SET): 653 if browser in set(selenium_constants.SELENIUM_BROWSER_SET):
958 test_list = [] 654 test_list = GetTestsForBrowser(browser, FLAGS.testprefix,
959 result = selenium_session.TestBrowser(browser, test_list, 655 FLAGS.testsuffixes)
960 FLAGS.testprefix,
961 FLAGS.testsuffixes,
962 int(FLAGS.servertimeout) * 1000)
963 656
964 # Compare screenshots 657 result = TestBrowser(session_builder, browser, test_list)
965 compare_result = CompareScreenshots(browser, 658
966 test_list, 659 if not result.wasSuccessful():
967 FLAGS.screenshots,
968 FLAGS.screenshotsdir,
969 FLAGS.screencompare,
970 FLAGS.verbose)
971 if not result.wasSuccessful() or not compare_result.WasSuccessful():
972 all_tests_passed = False 660 all_tests_passed = False
973 # Log results 661
974 print "Results for %s:" % browser 662 # Log non-succesful tests, for convenience.
975 print " %d tests run." % (result.testsRun + compare_result.tests_run) 663 print ""
976 print " %d errors." % (len(result.errors) + len(compare_result.errors)) 664 print "Failures for %s:" % browser
977 print " %d failures.\n" % (len(result.failures) + 665 print "[Selenium tests]"
978 len(compare_result.failures)) 666 for entry in test_list:
667 if type(entry) == tuple:
668 test = entry[0]
669 else:
670 test = entry
671
672 if test in result.results:
673 if result.results[test] != 'PASS':
674 print test.name
675
676 print ""
677 print "[Perceptual Diff tests]"
678 for entry in test_list:
679 if type(entry) == tuple:
680 pdiff_test = entry[1]
681 if pdiff_test in result.results:
682 if result.results[pdiff_test] != 'PASS':
683 print pdiff_test.name
684
685
686 # Log summary results.
687 print ""
688 print "Summary for %s:" % browser
689 print " %d tests run." % result.testsRun
690 print " %d errors." % len(result.errors)
691 print " %d failures.\n" % len(result.failures)
979 692
980 else: 693 else:
981 print "ERROR: Browser %s is invalid." % browser 694 print "ERROR: Browser %s is invalid." % browser
982 print "Run with --help to view list of supported browsers.\n" 695 print "Run with --help to view list of supported browsers.\n"
983 all_tests_passed = False 696 all_tests_passed = False
984 697
985 # Wrap up session 698 # Shut down remote control
986 selenium_session.TearDown() 699 shutdown_session = selenium.selenium("localhost",
700 sel_server.selenium_port, "*firefox",
701 "http://%s:%d" % (socket.gethostname(), http_server.http_port))
702 shutdown_session.shut_down_selenium_server()
987 703
988 if all_tests_passed: 704 if all_tests_passed:
989 # All tests successful. 705 # All tests successful.
990 return 0 706 return 0
991 else: 707 else:
992 # Return error code 1. 708 # Return error code 1.
993 return 1 709 return 1
994 710
995 def GetChromePath():
996 value = None
997 if sys.platform == "win32" or sys.platform == "cygwin":
998 import _winreg
999 try:
1000 key = _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT,
1001 "Applications\\chrome.exe\\shell\\open\\command")
1002 (value, type) = _winreg.QueryValueEx(key, None)
1003 _winreg.CloseKey(key)
1004 except WindowsError:
1005 raise Exception("Unable to determine location for Chrome -- "
1006 "it is installed?")
1007 value = os.path.dirname(value)
1008 return value
1009
1010 if __name__ == "__main__": 711 if __name__ == "__main__":
1011 remaining_argv = FLAGS(sys.argv) 712 remaining_argv = FLAGS(sys.argv)
1012 713
1013 # Setup the environment for Firefox 714 # Setup the environment for Firefox
1014 os.environ["MOZ_CRASHREPORTER_DISABLE"] = "1" 715 os.environ["MOZ_CRASHREPORTER_DISABLE"] = "1"
1015 os.environ["MOZ_PLUGIN_PATH"] = os.path.normpath(FLAGS.product_dir) 716 os.environ["MOZ_PLUGIN_PATH"] = os.path.normpath(FLAGS.product_dir)
1016 717
1017 # Setup the path for chrome. 718 # Setup the path for chrome.
1018 chrome_path = GetChromePath() 719 chrome_path = GetChromePath()
1019 if chrome_path: 720 if chrome_path:
1020 if os.environ.get("PATH"): 721 if os.environ.get("PATH"):
1021 os.environ["PATH"] = os.pathsep.join([os.environ["PATH"], chrome_path]) 722 os.environ["PATH"] = os.pathsep.join([os.environ["PATH"], chrome_path])
1022 else: 723 else:
1023 os.environ["PATH"] = chrome_path 724 os.environ["PATH"] = chrome_path
1024 725
1025 # Setup the LD_LIBRARY_PATH on Linux. 726 # Setup the LD_LIBRARY_PATH on Linux.
1026 if sys.platform[:5] == "linux": 727 if sys.platform[:5] == "linux":
1027 if os.environ.get("LD_LIBRARY_PATH"): 728 if os.environ.get("LD_LIBRARY_PATH"):
1028 os.environ["LD_LIBRARY_PATH"] = os.pathsep.join( 729 os.environ["LD_LIBRARY_PATH"] = os.pathsep.join(
1029 [os.environ["LD_LIBRARY_PATH"], os.path.normpath(FLAGS.product_dir)]) 730 [os.environ["LD_LIBRARY_PATH"], os.path.normpath(FLAGS.product_dir)])
1030 else: 731 else:
1031 os.environ["LD_LIBRARY_PATH"] = os.path.normpath(FLAGS.product_dir) 732 os.environ["LD_LIBRARY_PATH"] = os.path.normpath(FLAGS.product_dir)
1032 733
1033 sys.exit(main(remaining_argv)) 734 sys.exit(main(remaining_argv))
OLDNEW
« no previous file with comments | « tests/selenium/javascript_unit_tests.py ('k') | tests/selenium/pdiff_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698