OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """PyAuto: Python Interface to Chromium's Automation Proxy. | 6 """PyAuto: Python Interface to Chromium's Automation Proxy. |
7 | 7 |
8 PyAuto uses swig to expose Automation Proxy interfaces to Python. | 8 PyAuto uses swig to expose Automation Proxy interfaces to Python. |
9 For complete documentation on the functionality available, | 9 For complete documentation on the functionality available, |
10 run pydoc on this file. | 10 run pydoc on this file. |
(...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
470 self.CloseChromeOnChromeOS() | 470 self.CloseChromeOnChromeOS() |
471 self.EnableChromeTestingOnChromeOS() | 471 self.EnableChromeTestingOnChromeOS() |
472 self.SetUp() | 472 self.SetUp() |
473 return | 473 return |
474 # Not chromeos | 474 # Not chromeos |
475 orig_clear_state = self.get_clear_profile() | 475 orig_clear_state = self.get_clear_profile() |
476 self.CloseBrowserAndServer() | 476 self.CloseBrowserAndServer() |
477 self.set_clear_profile(clear_profile) | 477 self.set_clear_profile(clear_profile) |
478 if pre_launch_hook: | 478 if pre_launch_hook: |
479 pre_launch_hook() | 479 pre_launch_hook() |
480 logging.debug('Restarting browser with clear_profile=%s' % | 480 logging.debug('Restarting browser with clear_profile=%s', |
481 self.get_clear_profile()) | 481 self.get_clear_profile()) |
482 self.LaunchBrowserAndServer() | 482 self.LaunchBrowserAndServer() |
483 self.set_clear_profile(orig_clear_state) # Reset to original state. | 483 self.set_clear_profile(orig_clear_state) # Reset to original state. |
484 | 484 |
485 @staticmethod | 485 @staticmethod |
486 def DataDir(): | 486 def DataDir(): |
487 """Returns the path to the data dir chrome/test/data.""" | 487 """Returns the path to the data dir chrome/test/data.""" |
488 return os.path.normpath( | 488 return os.path.normpath( |
489 os.path.join(os.path.dirname(__file__), os.pardir, "data")) | 489 os.path.join(os.path.dirname(__file__), os.pardir, "data")) |
490 | 490 |
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
711 debug: if True, displays debug info at each retry. | 711 debug: if True, displays debug info at each retry. |
712 | 712 |
713 Returns: | 713 Returns: |
714 True, if returning when |function| evaluated to True | 714 True, if returning when |function| evaluated to True |
715 False, when returning due to timeout | 715 False, when returning due to timeout |
716 """ | 716 """ |
717 if timeout == -1: # Default | 717 if timeout == -1: # Default |
718 timeout = self.action_max_timeout_ms() / 1000.0 | 718 timeout = self.action_max_timeout_ms() / 1000.0 |
719 assert callable(function), "function should be a callable" | 719 assert callable(function), "function should be a callable" |
720 begin = time.time() | 720 begin = time.time() |
| 721 debug_begin = begin |
721 while timeout is None or time.time() - begin <= timeout: | 722 while timeout is None or time.time() - begin <= timeout: |
722 retval = function(*args) | 723 retval = function(*args) |
723 if (expect_retval is None and retval) or expect_retval == retval: | 724 if (expect_retval is None and retval) or expect_retval == retval: |
724 return True | 725 return True |
725 if debug: | 726 if debug and time.time() - debug_begin > 5: |
726 logging.debug('WaitUntil(%s) still waiting. ' | 727 debug_begin += 5 |
727 'Expecting %s. Last returned %s.' % ( | 728 if function.func_name == (lambda: True).func_name: |
728 function, expect_retval, retval)) | 729 function_info = inspect.getsource(function).strip() |
| 730 else: |
| 731 function_info = '%s()' % function.func_name |
| 732 logging.debug('WaitUntil(%s:%d %s) still waiting. ' |
| 733 'Expecting %s. Last returned %s.', |
| 734 os.path.basename(inspect.getsourcefile(function)), |
| 735 inspect.getsourcelines(function)[1], |
| 736 function_info, |
| 737 True if expect_retval is None else expect_retval, |
| 738 retval) |
729 time.sleep(retry_sleep) | 739 time.sleep(retry_sleep) |
730 return False | 740 return False |
731 | 741 |
732 def StartSyncServer(self): | 742 def StartSyncServer(self): |
733 """Start a local sync server. | 743 """Start a local sync server. |
734 | 744 |
735 Adds a dictionary attribute 'ports' in returned object. | 745 Adds a dictionary attribute 'ports' in returned object. |
736 | 746 |
737 Returns: | 747 Returns: |
738 A handle to Sync Server, an instance of TestServer | 748 A handle to Sync Server, an instance of TestServer |
739 """ | 749 """ |
740 sync_server = pyautolib.TestServer(pyautolib.TestServer.TYPE_SYNC, | 750 sync_server = pyautolib.TestServer(pyautolib.TestServer.TYPE_SYNC, |
741 pyautolib.FilePath('')) | 751 pyautolib.FilePath('')) |
742 assert sync_server.Start(), 'Could not start sync server' | 752 assert sync_server.Start(), 'Could not start sync server' |
743 sync_server.ports = dict(port=sync_server.GetPort(), | 753 sync_server.ports = dict(port=sync_server.GetPort(), |
744 xmpp_port=sync_server.GetSyncXmppPort()) | 754 xmpp_port=sync_server.GetSyncXmppPort()) |
745 logging.debug('Started sync server at ports %s.' % sync_server.ports) | 755 logging.debug('Started sync server at ports %s.', sync_server.ports) |
746 return sync_server | 756 return sync_server |
747 | 757 |
748 def StopSyncServer(self, sync_server): | 758 def StopSyncServer(self, sync_server): |
749 """Stop the local sync server.""" | 759 """Stop the local sync server.""" |
750 assert sync_server, 'Sync Server not yet started' | 760 assert sync_server, 'Sync Server not yet started' |
751 assert sync_server.Stop(), 'Could not stop sync server' | 761 assert sync_server.Stop(), 'Could not stop sync server' |
752 logging.debug('Stopped sync server at ports %s.' % sync_server.ports) | 762 logging.debug('Stopped sync server at ports %s.', sync_server.ports) |
753 | 763 |
754 def StartFTPServer(self, data_dir): | 764 def StartFTPServer(self, data_dir): |
755 """Start a local file server hosting data files over ftp:// | 765 """Start a local file server hosting data files over ftp:// |
756 | 766 |
757 Args: | 767 Args: |
758 data_dir: path where ftp files should be served | 768 data_dir: path where ftp files should be served |
759 | 769 |
760 Returns: | 770 Returns: |
761 handle to FTP Server, an instance of TestServer | 771 handle to FTP Server, an instance of TestServer |
762 """ | 772 """ |
763 ftp_server = pyautolib.TestServer(pyautolib.TestServer.TYPE_FTP, | 773 ftp_server = pyautolib.TestServer(pyautolib.TestServer.TYPE_FTP, |
764 pyautolib.FilePath(data_dir)) | 774 pyautolib.FilePath(data_dir)) |
765 assert ftp_server.Start(), 'Could not start ftp server' | 775 assert ftp_server.Start(), 'Could not start ftp server' |
766 logging.debug('Started ftp server at "%s".' % data_dir) | 776 logging.debug('Started ftp server at "%s".', data_dir) |
767 return ftp_server | 777 return ftp_server |
768 | 778 |
769 def StopFTPServer(self, ftp_server): | 779 def StopFTPServer(self, ftp_server): |
770 """Stop the local ftp server.""" | 780 """Stop the local ftp server.""" |
771 assert ftp_server, 'FTP Server not yet started' | 781 assert ftp_server, 'FTP Server not yet started' |
772 assert ftp_server.Stop(), 'Could not stop ftp server' | 782 assert ftp_server.Stop(), 'Could not stop ftp server' |
773 logging.debug('Stopped ftp server.') | 783 logging.debug('Stopped ftp server.') |
774 | 784 |
775 def StartHTTPServer(self, data_dir): | 785 def StartHTTPServer(self, data_dir): |
776 """Starts a local HTTP TestServer serving files from |data_dir|. | 786 """Starts a local HTTP TestServer serving files from |data_dir|. |
777 | 787 |
778 Args: | 788 Args: |
779 data_dir: path where the TestServer should serve files from. This will be | 789 data_dir: path where the TestServer should serve files from. This will be |
780 appended to the source dir to get the final document root. | 790 appended to the source dir to get the final document root. |
781 | 791 |
782 Returns: | 792 Returns: |
783 handle to the HTTP TestServer | 793 handle to the HTTP TestServer |
784 """ | 794 """ |
785 http_server = pyautolib.TestServer(pyautolib.TestServer.TYPE_HTTP, | 795 http_server = pyautolib.TestServer(pyautolib.TestServer.TYPE_HTTP, |
786 pyautolib.FilePath(data_dir)) | 796 pyautolib.FilePath(data_dir)) |
787 assert http_server.Start(), 'Could not start HTTP server' | 797 assert http_server.Start(), 'Could not start HTTP server' |
788 logging.debug('Started HTTP server at "%s".' % data_dir) | 798 logging.debug('Started HTTP server at "%s".', data_dir) |
789 return http_server | 799 return http_server |
790 | 800 |
791 def StopHTTPServer(self, http_server): | 801 def StopHTTPServer(self, http_server): |
792 assert http_server, 'HTTP server not yet started' | 802 assert http_server, 'HTTP server not yet started' |
793 assert http_server.Stop(), 'Cloud not stop the HTTP server' | 803 assert http_server.Stop(), 'Cloud not stop the HTTP server' |
794 logging.debug('Stopped HTTP server.') | 804 logging.debug('Stopped HTTP server.') |
795 | 805 |
796 class ActionTimeoutChanger(object): | 806 class ActionTimeoutChanger(object): |
797 """Facilitate temporary changes to action_timeout_ms. | 807 """Facilitate temporary changes to action_timeout_ms. |
798 | 808 |
(...skipping 3717 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4516 | 4526 |
4517 def _StartHTTPServer(self): | 4527 def _StartHTTPServer(self): |
4518 """Start a local file server hosting data files over http://""" | 4528 """Start a local file server hosting data files over http://""" |
4519 global _HTTP_SERVER | 4529 global _HTTP_SERVER |
4520 assert not _HTTP_SERVER, 'HTTP Server already started' | 4530 assert not _HTTP_SERVER, 'HTTP Server already started' |
4521 http_data_dir = _OPTIONS.http_data_dir | 4531 http_data_dir = _OPTIONS.http_data_dir |
4522 http_server = pyautolib.TestServer(pyautolib.TestServer.TYPE_HTTP, | 4532 http_server = pyautolib.TestServer(pyautolib.TestServer.TYPE_HTTP, |
4523 pyautolib.FilePath(http_data_dir)) | 4533 pyautolib.FilePath(http_data_dir)) |
4524 assert http_server.Start(), 'Could not start http server' | 4534 assert http_server.Start(), 'Could not start http server' |
4525 _HTTP_SERVER = http_server | 4535 _HTTP_SERVER = http_server |
4526 logging.debug('Started http server at "%s".' % http_data_dir) | 4536 logging.debug('Started http server at "%s".', http_data_dir) |
4527 | 4537 |
4528 def _StopHTTPServer(self): | 4538 def _StopHTTPServer(self): |
4529 """Stop the local http server.""" | 4539 """Stop the local http server.""" |
4530 global _HTTP_SERVER | 4540 global _HTTP_SERVER |
4531 assert _HTTP_SERVER, 'HTTP Server not yet started' | 4541 assert _HTTP_SERVER, 'HTTP Server not yet started' |
4532 assert _HTTP_SERVER.Stop(), 'Could not stop http server' | 4542 assert _HTTP_SERVER.Stop(), 'Could not stop http server' |
4533 _HTTP_SERVER = None | 4543 _HTTP_SERVER = None |
4534 logging.debug('Stopped http server.') | 4544 logging.debug('Stopped http server.') |
4535 | 4545 |
4536 def _ConnectToRemoteHosts(self, addresses): | 4546 def _ConnectToRemoteHosts(self, addresses): |
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4755 obj = getattr(obj, part) | 4765 obj = getattr(obj, part) |
4756 | 4766 |
4757 if type(obj) == types.ModuleType: | 4767 if type(obj) == types.ModuleType: |
4758 return _GetTestsFromModule(obj) | 4768 return _GetTestsFromModule(obj) |
4759 elif (isinstance(obj, (type, types.ClassType)) and | 4769 elif (isinstance(obj, (type, types.ClassType)) and |
4760 issubclass(obj, PyUITest) and obj != PyUITest): | 4770 issubclass(obj, PyUITest) and obj != PyUITest): |
4761 return [module.__name__ + '.' + x for x in _GetTestsFromTestCase(obj)] | 4771 return [module.__name__ + '.' + x for x in _GetTestsFromTestCase(obj)] |
4762 elif type(obj) == types.UnboundMethodType: | 4772 elif type(obj) == types.UnboundMethodType: |
4763 return [name] | 4773 return [name] |
4764 else: | 4774 else: |
4765 logging.warn('No tests in "%s"' % name) | 4775 logging.warn('No tests in "%s"', name) |
4766 return [] | 4776 return [] |
4767 | 4777 |
4768 def _ListMissingTests(self): | 4778 def _ListMissingTests(self): |
4769 """Print tests missing from PYAUTO_TESTS.""" | 4779 """Print tests missing from PYAUTO_TESTS.""" |
4770 # Fetch tests from all test scripts | 4780 # Fetch tests from all test scripts |
4771 all_test_files = filter(lambda x: x.endswith('.py'), | 4781 all_test_files = filter(lambda x: x.endswith('.py'), |
4772 os.listdir(self.TestsDir())) | 4782 os.listdir(self.TestsDir())) |
4773 all_tests_modules = [os.path.splitext(x)[0] for x in all_test_files] | 4783 all_tests_modules = [os.path.splitext(x)[0] for x in all_test_files] |
4774 all_tests = reduce(lambda x, y: x + y, | 4784 all_tests = reduce(lambda x, y: x + y, |
4775 map(self._ImportTestsFromName, all_tests_modules)) | 4785 map(self._ImportTestsFromName, all_tests_modules)) |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4816 ] | 4826 ] |
4817 """ | 4827 """ |
4818 if not args: # Load tests ourselves | 4828 if not args: # Load tests ourselves |
4819 if self._HasTestCases('__main__'): # we are running a test script | 4829 if self._HasTestCases('__main__'): # we are running a test script |
4820 module_name = os.path.splitext(os.path.basename(sys.argv[0]))[0] | 4830 module_name = os.path.splitext(os.path.basename(sys.argv[0]))[0] |
4821 args.append(module_name) # run the test cases found in it | 4831 args.append(module_name) # run the test cases found in it |
4822 else: # run tests from the test description file | 4832 else: # run tests from the test description file |
4823 pyauto_tests_file = os.path.join(self.TestsDir(), self._tests_filename) | 4833 pyauto_tests_file = os.path.join(self.TestsDir(), self._tests_filename) |
4824 logging.debug("Reading %s", pyauto_tests_file) | 4834 logging.debug("Reading %s", pyauto_tests_file) |
4825 if not os.path.exists(pyauto_tests_file): | 4835 if not os.path.exists(pyauto_tests_file): |
4826 logging.warn("%s missing. Cannot load tests." % pyauto_tests_file) | 4836 logging.warn("%s missing. Cannot load tests.", pyauto_tests_file) |
4827 else: | 4837 else: |
4828 args = self._ExpandTestNamesFrom(pyauto_tests_file, | 4838 args = self._ExpandTestNamesFrom(pyauto_tests_file, |
4829 self._options.suite) | 4839 self._options.suite) |
4830 return args | 4840 return args |
4831 | 4841 |
4832 def _ExpandTestNamesFrom(self, filename, suite): | 4842 def _ExpandTestNamesFrom(self, filename, suite): |
4833 """Load test names from the given file. | 4843 """Load test names from the given file. |
4834 | 4844 |
4835 Args: | 4845 Args: |
4836 filename: the file to read the tests from | 4846 filename: the file to read the tests from |
4837 suite: the name of the suite to load from |filename|. | 4847 suite: the name of the suite to load from |filename|. |
4838 | 4848 |
4839 Returns: | 4849 Returns: |
4840 a list of test names | 4850 a list of test names |
4841 [module.testcase.testX, module.testcase.testY, ..] | 4851 [module.testcase.testX, module.testcase.testY, ..] |
4842 """ | 4852 """ |
4843 suites = PyUITest.EvalDataFrom(filename) | 4853 suites = PyUITest.EvalDataFrom(filename) |
4844 platform = sys.platform | 4854 platform = sys.platform |
4845 if PyUITest.IsChromeOS(): # check if it's chromeos | 4855 if PyUITest.IsChromeOS(): # check if it's chromeos |
4846 platform = 'chromeos' | 4856 platform = 'chromeos' |
4847 assert platform in self._platform_map, '%s unsupported' % platform | 4857 assert platform in self._platform_map, '%s unsupported' % platform |
4848 def _NamesInSuite(suite_name): | 4858 def _NamesInSuite(suite_name): |
4849 logging.debug('Expanding suite %s' % suite_name) | 4859 logging.debug('Expanding suite %s', suite_name) |
4850 platforms = suites.get(suite_name) | 4860 platforms = suites.get(suite_name) |
4851 names = platforms.get('all', []) + \ | 4861 names = platforms.get('all', []) + \ |
4852 platforms.get(self._platform_map[platform], []) | 4862 platforms.get(self._platform_map[platform], []) |
4853 ret = [] | 4863 ret = [] |
4854 # Recursively include suites if any. Suites begin with @. | 4864 # Recursively include suites if any. Suites begin with @. |
4855 for name in names: | 4865 for name in names: |
4856 if name.startswith('@'): # Include another suite | 4866 if name.startswith('@'): # Include another suite |
4857 ret.extend(_NamesInSuite(name[1:])) | 4867 ret.extend(_NamesInSuite(name[1:])) |
4858 else: | 4868 else: |
4859 ret.append(name) | 4869 ret.append(name) |
4860 return ret | 4870 return ret |
4861 | 4871 |
4862 assert suite in suites, '%s: No such suite in %s' % (suite, filename) | 4872 assert suite in suites, '%s: No such suite in %s' % (suite, filename) |
4863 all_names = _NamesInSuite(suite) | 4873 all_names = _NamesInSuite(suite) |
4864 args = [] | 4874 args = [] |
4865 excluded = [] | 4875 excluded = [] |
4866 # Find all excluded tests. Excluded tests begin with '-'. | 4876 # Find all excluded tests. Excluded tests begin with '-'. |
4867 for name in all_names: | 4877 for name in all_names: |
4868 if name.startswith('-'): # Exclude | 4878 if name.startswith('-'): # Exclude |
4869 excluded.extend(self._ImportTestsFromName(name[1:])) | 4879 excluded.extend(self._ImportTestsFromName(name[1:])) |
4870 else: | 4880 else: |
4871 args.extend(self._ImportTestsFromName(name)) | 4881 args.extend(self._ImportTestsFromName(name)) |
4872 for name in excluded: | 4882 for name in excluded: |
4873 if name in args: | 4883 if name in args: |
4874 args.remove(name) | 4884 args.remove(name) |
4875 else: | 4885 else: |
4876 logging.warn('Cannot exclude %s. Not included. Ignoring' % name) | 4886 logging.warn('Cannot exclude %s. Not included. Ignoring', name) |
4877 if excluded: | 4887 if excluded: |
4878 logging.debug('Excluded %d test(s): %s' % (len(excluded), excluded)) | 4888 logging.debug('Excluded %d test(s): %s', len(excluded), excluded) |
4879 return args | 4889 return args |
4880 | 4890 |
4881 def _Run(self): | 4891 def _Run(self): |
4882 """Run the tests.""" | 4892 """Run the tests.""" |
4883 if self._options.wait_for_debugger: | 4893 if self._options.wait_for_debugger: |
4884 raw_input('Attach debugger to process %s and hit <enter> ' % os.getpid()) | 4894 raw_input('Attach debugger to process %s and hit <enter> ' % os.getpid()) |
4885 | 4895 |
4886 suite_args = [sys.argv[0]] | 4896 suite_args = [sys.argv[0]] |
4887 chrome_flags = self._options.chrome_flags | 4897 chrome_flags = self._options.chrome_flags |
4888 # Set CHROME_HEADLESS. It enables crash reporter on posix. | 4898 # Set CHROME_HEADLESS. It enables crash reporter on posix. |
(...skipping 18 matching lines...) Expand all Loading... |
4907 successful = result.wasSuccessful() | 4917 successful = result.wasSuccessful() |
4908 if not successful: | 4918 if not successful: |
4909 pyauto_tests_file = os.path.join(self.TestsDir(), self._tests_filename) | 4919 pyauto_tests_file = os.path.join(self.TestsDir(), self._tests_filename) |
4910 print >>sys.stderr, 'Tests can be disabled by editing %s. ' \ | 4920 print >>sys.stderr, 'Tests can be disabled by editing %s. ' \ |
4911 'Ref: %s' % (pyauto_tests_file, _PYAUTO_DOC_URL) | 4921 'Ref: %s' % (pyauto_tests_file, _PYAUTO_DOC_URL) |
4912 sys.exit(not successful) | 4922 sys.exit(not successful) |
4913 | 4923 |
4914 | 4924 |
4915 if __name__ == '__main__': | 4925 if __name__ == '__main__': |
4916 Main() | 4926 Main() |
OLD | NEW |