OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 | 2 |
3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 """PyAuto: Python Interface to Chromium's Automation Proxy. | 7 """PyAuto: Python Interface to Chromium's Automation Proxy. |
8 | 8 |
9 PyAuto uses swig to expose Automation Proxy interfaces to Python. | 9 PyAuto uses swig to expose Automation Proxy interfaces to Python. |
10 For complete documentation on the functionality available, | 10 For complete documentation on the functionality available, |
(...skipping 404 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
415 while timeout is None or time.time() - begin <= timeout: | 415 while timeout is None or time.time() - begin <= timeout: |
416 retval = function(*args) | 416 retval = function(*args) |
417 if (expect_retval is None and retval) or expect_retval == retval: | 417 if (expect_retval is None and retval) or expect_retval == retval: |
418 return True | 418 return True |
419 logging.debug('WaitUntil(%s) still waiting. ' | 419 logging.debug('WaitUntil(%s) still waiting. ' |
420 'Expecting %s. Last returned %s.' % ( | 420 'Expecting %s. Last returned %s.' % ( |
421 function, expect_retval, retval)) | 421 function, expect_retval, retval)) |
422 time.sleep(retry_sleep) | 422 time.sleep(retry_sleep) |
423 return False | 423 return False |
424 | 424 |
425 class CmdExecutionTimeoutChanger(object): | 425 |
426 """Facilitate temporary changes to command_execution_timeout_ms. | 426 class ActionTimeoutChanger(object): |
| 427 """Facilitate temporary changes to action_timeout_ms. |
427 | 428 |
428 Automatically resets to original timeout when object is destroyed. | 429 Automatically resets to original timeout when object is destroyed. |
429 """ | 430 """ |
430 _saved_timeout = -1 # Saved value for command_execution_timeout_ms | 431 _saved_timeout = -1 # Saved value for action_timeout_ms |
431 | 432 |
432 def __init__(self, ui_test, new_timeout): | 433 def __init__(self, ui_test, new_timeout): |
433 """Initialize. | 434 """Initialize. |
434 | 435 |
435 Args: | 436 Args: |
436 ui_test: a PyUITest object | 437 ui_test: a PyUITest object |
437 new_timeout: new timeout to use (in milli secs) | 438 new_timeout: new timeout to use (in milli secs) |
438 """ | 439 """ |
439 self._saved_timeout = ui_test.command_execution_timeout_ms() | 440 self._saved_timeout = ui_test.action_timeout_ms() |
440 if new_timeout != self._saved_timeout: | 441 if new_timeout != self._saved_timeout: |
441 ui_test.set_command_execution_timeout_ms(new_timeout) | 442 ui_test.set_action_timeout_ms(new_timeout) |
442 self._ui_test = ui_test | 443 self._ui_test = ui_test |
443 | 444 |
444 def __del__(self): | 445 def __del__(self): |
445 """Reset command_execution_timeout_ms to original value.""" | 446 """Reset command_execution_timeout_ms to original value.""" |
446 if self._ui_test.command_execution_timeout_ms() != self._saved_timeout: | 447 if self._ui_test.action_timeout_ms() != self._saved_timeout: |
447 self._ui_test.set_command_execution_timeout_ms(self._saved_timeout) | 448 self._ui_test.set_action_timeout_ms(self._saved_timeout) |
448 | 449 |
449 | 450 |
450 def _GetResultFromJSONRequest(self, cmd_dict, windex=0): | 451 def _GetResultFromJSONRequest(self, cmd_dict, windex=0, timeout=-1): |
451 """Issue call over the JSON automation channel and fetch output. | 452 """Issue call over the JSON automation channel and fetch output. |
452 | 453 |
453 This method packages the given dictionary into a json string, sends it | 454 This method packages the given dictionary into a json string, sends it |
454 over the JSON automation channel, loads the json output string returned, | 455 over the JSON automation channel, loads the json output string returned, |
455 and returns it back as a dictionary. | 456 and returns it back as a dictionary. |
456 | 457 |
457 Args: | 458 Args: |
458 cmd_dict: the command dictionary. It must have a 'command' key | 459 cmd_dict: the command dictionary. It must have a 'command' key |
459 Sample: | 460 Sample: |
460 { | 461 { |
461 'command': 'SetOmniboxText', | 462 'command': 'SetOmniboxText', |
462 'text': text, | 463 'text': text, |
463 } | 464 } |
464 windex: 0-based window index on which to work. Default: 0 (first window) | 465 windex: 0-based window index on which to work. Default: 0 (first window) |
465 Use -ve windex if the automation command does not apply to a | 466 Use -ve windex if the automation command does not apply to a |
466 browser window. example: chromeos login | 467 browser window. example: chromeos login |
467 | 468 |
| 469 timeout: request timeout (in milliseconds) |
| 470 |
468 Returns: | 471 Returns: |
469 a dictionary for the output returned by the automation channel. | 472 a dictionary for the output returned by the automation channel. |
470 | 473 |
471 Raises: | 474 Raises: |
472 pyauto_errors.JSONInterfaceError if the automation call returns an error. | 475 pyauto_errors.JSONInterfaceError if the automation call returns an error. |
473 """ | 476 """ |
474 result = self._SendJSONRequest(windex, json.dumps(cmd_dict)) | 477 if timeout == -1: # Default |
| 478 timeout = self.action_max_timeout_ms() |
| 479 result = self._SendJSONRequest(windex, json.dumps(cmd_dict), timeout) |
475 if len(result) == 0: | 480 if len(result) == 0: |
476 raise JSONInterfaceError('Automation call received no response.') | 481 raise JSONInterfaceError('Automation call received no response.') |
477 ret_dict = json.loads(result) | 482 ret_dict = json.loads(result) |
478 if ret_dict.has_key('error'): | 483 if ret_dict.has_key('error'): |
479 raise JSONInterfaceError(ret_dict['error']) | 484 raise JSONInterfaceError(ret_dict['error']) |
480 return ret_dict | 485 return ret_dict |
481 | 486 |
482 def GetBookmarkModel(self): | 487 def GetBookmarkModel(self): |
483 """Return the bookmark model as a BookmarkModel object. | 488 """Return the bookmark model as a BookmarkModel object. |
484 | 489 |
485 This is a snapshot of the bookmark model; it is not a proxy and | 490 This is a snapshot of the bookmark model; it is not a proxy and |
486 does not get updated as the bookmark model changes. | 491 does not get updated as the bookmark model changes. |
487 """ | 492 """ |
488 return bookmark_model.BookmarkModel(self._GetBookmarksAsJSON()) | 493 return bookmark_model.BookmarkModel(self._GetBookmarksAsJSON()) |
489 | 494 |
490 def GetDownloadsInfo(self, windex=0): | 495 def GetDownloadsInfo(self, windex=0): |
491 """Return info about downloads. | 496 """Return info about downloads. |
492 | 497 |
493 This includes all the downloads recognized by the history system. | 498 This includes all the downloads recognized by the history system. |
494 | 499 |
495 Returns: | 500 Returns: |
496 an instance of downloads_info.DownloadInfo | 501 an instance of downloads_info.DownloadInfo |
497 """ | 502 """ |
498 return download_info.DownloadInfo( | 503 return download_info.DownloadInfo( |
499 self._SendJSONRequest( | 504 self._SendJSONRequest( |
500 windex, json.dumps({'command': 'GetDownloadsInfo'}))) | 505 windex, json.dumps({'command': 'GetDownloadsInfo'}), |
| 506 self.action_max_timeout_ms())) |
501 | 507 |
502 def GetOmniboxInfo(self, windex=0): | 508 def GetOmniboxInfo(self, windex=0): |
503 """Return info about Omnibox. | 509 """Return info about Omnibox. |
504 | 510 |
505 This represents a snapshot of the omnibox. If you expect changes | 511 This represents a snapshot of the omnibox. If you expect changes |
506 you need to call this method again to get a fresh snapshot. | 512 you need to call this method again to get a fresh snapshot. |
507 Note that this DOES NOT shift focus to the omnibox; you've to ensure that | 513 Note that this DOES NOT shift focus to the omnibox; you've to ensure that |
508 the omnibox is in focus or else you won't get any interesting info. | 514 the omnibox is in focus or else you won't get any interesting info. |
509 | 515 |
510 It's OK to call this even when the omnibox popup is not showing. In this | 516 It's OK to call this even when the omnibox popup is not showing. In this |
511 case however, there won't be any matches, but other properties (like the | 517 case however, there won't be any matches, but other properties (like the |
512 current text in the omnibox) will still be fetched. | 518 current text in the omnibox) will still be fetched. |
513 | 519 |
514 Due to the nature of the omnibox, this function is sensitive to mouse | 520 Due to the nature of the omnibox, this function is sensitive to mouse |
515 focus. DO NOT HOVER MOUSE OVER OMNIBOX OR CHANGE WINDOW FOCUS WHEN USING | 521 focus. DO NOT HOVER MOUSE OVER OMNIBOX OR CHANGE WINDOW FOCUS WHEN USING |
516 THIS METHOD. | 522 THIS METHOD. |
517 | 523 |
518 Args: | 524 Args: |
519 windex: the index of the browser window to work on. | 525 windex: the index of the browser window to work on. |
520 Default: 0 (first window) | 526 Default: 0 (first window) |
521 | 527 |
522 Returns: | 528 Returns: |
523 an instance of omnibox_info.OmniboxInfo | 529 an instance of omnibox_info.OmniboxInfo |
524 """ | 530 """ |
525 return omnibox_info.OmniboxInfo( | 531 return omnibox_info.OmniboxInfo( |
526 self._SendJSONRequest(windex, | 532 self._SendJSONRequest(windex, |
527 json.dumps({'command': 'GetOmniboxInfo'}))) | 533 json.dumps({'command': 'GetOmniboxInfo'}), |
| 534 self.action_max_timeout_ms())) |
528 | 535 |
529 def SetOmniboxText(self, text, windex=0): | 536 def SetOmniboxText(self, text, windex=0): |
530 """Enter text into the omnibox. This shifts focus to the omnibox. | 537 """Enter text into the omnibox. This shifts focus to the omnibox. |
531 | 538 |
532 Args: | 539 Args: |
533 text: the text to be set. | 540 text: the text to be set. |
534 windex: the index of the browser window to work on. | 541 windex: the index of the browser window to work on. |
535 Default: 0 (first window) | 542 Default: 0 (first window) |
536 """ | 543 """ |
537 # Ensure that keyword data is loaded from the profile. | 544 # Ensure that keyword data is loaded from the profile. |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
719 """Return info about preferences. | 726 """Return info about preferences. |
720 | 727 |
721 This represents a snapshot of the preferences. If you expect preferences | 728 This represents a snapshot of the preferences. If you expect preferences |
722 to have changed, you need to call this method again to get a fresh | 729 to have changed, you need to call this method again to get a fresh |
723 snapshot. | 730 snapshot. |
724 | 731 |
725 Returns: | 732 Returns: |
726 an instance of prefs_info.PrefsInfo | 733 an instance of prefs_info.PrefsInfo |
727 """ | 734 """ |
728 return prefs_info.PrefsInfo( | 735 return prefs_info.PrefsInfo( |
729 self._SendJSONRequest(0, json.dumps({'command': 'GetPrefsInfo'}))) | 736 self._SendJSONRequest(0, json.dumps({'command': 'GetPrefsInfo'}), |
| 737 self.action_max_timeout_ms())) |
730 | 738 |
731 def SetPrefs(self, path, value): | 739 def SetPrefs(self, path, value): |
732 """Set preference for the given path. | 740 """Set preference for the given path. |
733 | 741 |
734 Preferences are stored by Chromium as a hierarchical dictionary. | 742 Preferences are stored by Chromium as a hierarchical dictionary. |
735 dot-separated paths can be used to refer to a particular preference. | 743 dot-separated paths can be used to refer to a particular preference. |
736 example: "session.restore_on_startup" | 744 example: "session.restore_on_startup" |
737 | 745 |
738 Some preferences are managed, that is, they cannot be changed by the | 746 Some preferences are managed, that is, they cannot be changed by the |
739 user. It's upto the user to know which ones can be changed. Typically, | 747 user. It's upto the user to know which ones can be changed. Typically, |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
777 'windowsKeyCode': key_code, | 785 'windowsKeyCode': key_code, |
778 'modifiers': 0, | 786 'modifiers': 0, |
779 'windex': windex, | 787 'windex': windex, |
780 'tab_index': tab_index, | 788 'tab_index': tab_index, |
781 } | 789 } |
782 # Sending two requests, one each for "key down" and "key up". | 790 # Sending two requests, one each for "key down" and "key up". |
783 self._GetResultFromJSONRequest(cmd_dict) | 791 self._GetResultFromJSONRequest(cmd_dict) |
784 cmd_dict['type'] = 3 # kKeyUpType | 792 cmd_dict['type'] = 3 # kKeyUpType |
785 self._GetResultFromJSONRequest(cmd_dict) | 793 self._GetResultFromJSONRequest(cmd_dict) |
786 | 794 |
787 def WaitForAllDownloadsToComplete(self, windex=0): | 795 def WaitForAllDownloadsToComplete(self, windex=0, timeout=-1): |
788 """Wait for all downloads to complete. | 796 """Wait for all downloads to complete. |
789 | 797 |
790 Note: This method does not work for dangerous downloads. Use | 798 Note: This method does not work for dangerous downloads. Use |
791 WaitForGivenDownloadsToComplete (below) instead. | 799 WaitForGivenDownloadsToComplete (below) instead. |
792 """ | 800 """ |
793 cmd_dict = {'command': 'WaitForAllDownloadsToComplete'} | 801 cmd_dict = {'command': 'WaitForAllDownloadsToComplete'} |
794 self._GetResultFromJSONRequest(cmd_dict, windex=windex) | 802 self._GetResultFromJSONRequest(cmd_dict, windex=windex, timeout=timeout) |
795 | 803 |
796 def WaitForDownloadToComplete(self, download_path, timeout=-1): | 804 def WaitForDownloadToComplete(self, download_path, timeout=-1): |
797 """Wait for the given downloads to complete. | 805 """Wait for the given downloads to complete. |
798 | 806 |
799 This method works for dangerous downloads as well as regular downloads. | 807 This method works for dangerous downloads as well as regular downloads. |
800 | 808 |
801 Args: | 809 Args: |
802 download_path: The path to the final download. This is only necessary for | 810 download_path: The path to the final download. This is only necessary for |
803 the workaround described in the comments below and should | 811 the workaround described in the comments below and should |
804 be removed when downloads are re-implemented. | 812 be removed when downloads are re-implemented. |
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1079 the chrome://history/ UI. | 1087 the chrome://history/ UI. |
1080 | 1088 |
1081 Returns: | 1089 Returns: |
1082 an instance of history_info.HistoryInfo | 1090 an instance of history_info.HistoryInfo |
1083 """ | 1091 """ |
1084 cmd_dict = { # Prepare command for the json interface | 1092 cmd_dict = { # Prepare command for the json interface |
1085 'command': 'GetHistoryInfo', | 1093 'command': 'GetHistoryInfo', |
1086 'search_text': search_text, | 1094 'search_text': search_text, |
1087 } | 1095 } |
1088 return history_info.HistoryInfo( | 1096 return history_info.HistoryInfo( |
1089 self._SendJSONRequest(0, json.dumps(cmd_dict))) | 1097 self._SendJSONRequest(0, json.dumps(cmd_dict), |
| 1098 self.action_max_timeout_ms())) |
1090 | 1099 |
1091 def GetTranslateInfo(self, tab_index=0, window_index=0): | 1100 def GetTranslateInfo(self, tab_index=0, window_index=0): |
1092 """Returns info about translate for the given page. | 1101 """Returns info about translate for the given page. |
1093 | 1102 |
1094 If the translate bar is showing, also returns information about the bar. | 1103 If the translate bar is showing, also returns information about the bar. |
1095 | 1104 |
1096 Args: | 1105 Args: |
1097 tab_index: The tab index, default is 0. | 1106 tab_index: The tab index, default is 0. |
1098 window_index: The window index, default is 0. | 1107 window_index: The window index, default is 0. |
1099 | 1108 |
(...skipping 316 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1416 | 1425 |
1417 def GetPluginsInfo(self): | 1426 def GetPluginsInfo(self): |
1418 """Return info about plugins. | 1427 """Return info about plugins. |
1419 | 1428 |
1420 This is the info available from about:plugins | 1429 This is the info available from about:plugins |
1421 | 1430 |
1422 Returns: | 1431 Returns: |
1423 an instance of plugins_info.PluginsInfo | 1432 an instance of plugins_info.PluginsInfo |
1424 """ | 1433 """ |
1425 return plugins_info.PluginsInfo( | 1434 return plugins_info.PluginsInfo( |
1426 self._SendJSONRequest(0, json.dumps({'command': 'GetPluginsInfo'}))) | 1435 self._SendJSONRequest(0, json.dumps({'command': 'GetPluginsInfo'}), |
| 1436 self.action_max_timeout_ms())) |
1427 | 1437 |
1428 def EnablePlugin(self, path): | 1438 def EnablePlugin(self, path): |
1429 """Enable the plugin at the given path. | 1439 """Enable the plugin at the given path. |
1430 | 1440 |
1431 Use GetPluginsInfo() to fetch path info about a plugin. | 1441 Use GetPluginsInfo() to fetch path info about a plugin. |
1432 | 1442 |
1433 Raises: | 1443 Raises: |
1434 pyauto_errors.JSONInterfaceError if the automation call returns an error. | 1444 pyauto_errors.JSONInterfaceError if the automation call returns an error. |
1435 """ | 1445 """ |
1436 cmd_dict = { | 1446 cmd_dict = { |
(...skipping 1609 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3046 if self._options.verbose: | 3056 if self._options.verbose: |
3047 verbosity = 2 | 3057 verbosity = 2 |
3048 result = PyAutoTextTestRuner(verbosity=verbosity).run(pyauto_suite) | 3058 result = PyAutoTextTestRuner(verbosity=verbosity).run(pyauto_suite) |
3049 del loaded_tests # Need to destroy test cases before the suite | 3059 del loaded_tests # Need to destroy test cases before the suite |
3050 del pyauto_suite | 3060 del pyauto_suite |
3051 sys.exit(not result.wasSuccessful()) | 3061 sys.exit(not result.wasSuccessful()) |
3052 | 3062 |
3053 | 3063 |
3054 if __name__ == '__main__': | 3064 if __name__ == '__main__': |
3055 Main() | 3065 Main() |
OLD | NEW |