Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """The testing Environment class.""" | 5 """The testing Environment class.""" |
| 6 | 6 |
| 7 import logging | |
| 8 import shutil | 7 import shutil |
| 9 import sys | |
| 10 import time | 8 import time |
| 11 import traceback | |
| 12 from xml.etree import ElementTree | 9 from xml.etree import ElementTree |
| 13 from xml.sax.saxutils import escape | |
| 14 | |
| 15 sys.path.insert(0, '../../../../third_party/webdriver/pylib/') | |
| 16 | 10 |
| 17 from selenium import webdriver | 11 from selenium import webdriver |
| 18 from selenium.common.exceptions import NoSuchElementException | |
| 19 from selenium.common.exceptions import WebDriverException | |
| 20 from selenium.webdriver.chrome.options import Options | 12 from selenium.webdriver.chrome.options import Options |
| 21 | 13 |
| 22 | 14 |
| 23 # Message strings to look for in chrome://password-manager-internals | 15 # Message strings to look for in chrome://password-manager-internals |
| 24 MESSAGE_ASK = "Message: Decision: ASK the user" | 16 MESSAGE_ASK = "Message: Decision: ASK the user" |
| 25 MESSAGE_SAVE = "Message: Decision: SAVE the password" | 17 MESSAGE_SAVE = "Message: Decision: SAVE the password" |
| 26 | 18 |
| 27 | 19 |
| 28 class TestResult: | 20 class TestResult: |
| 29 """Stores the information related to a test result. """ | 21 """Stores the information related to a test result. """ |
| 30 def __init__(self, name, test_type, successful, message): | 22 def __init__(self, name, test_type, successful, message): |
| 31 """Creates a new TestResult. | 23 """Creates a new TestResult. |
| 32 | 24 |
| 33 Args: | 25 Args: |
| 34 name: The tested website name. | 26 name: The tested website name. |
| 35 test_type: The test type. | 27 test_type: The test type. |
| 36 successful: Whether or not the test was successful. | 28 successful: Whether or not the test was successful. |
| 37 message: The error message of the test. | 29 message: The error message of the test. |
| 38 """ | 30 """ |
| 39 self.name = name | 31 self.name = name |
| 40 self.test_type = test_type | 32 self.test_type = test_type |
| 41 self.successful = successful | 33 self.successful = successful |
| 42 self.message = message | 34 self.message = message |
| 43 | 35 |
| 44 | 36 |
| 45 class Environment: | 37 class Environment: |
| 46 """Sets up the testing Environment. """ | 38 """Sets up the testing Environment. """ |
| 47 | 39 |
| 48 def __init__(self, chrome_path, chromedriver_path, profile_path, | 40 def __init__(self, chrome_path, chromedriver_path, profile_path, |
| 49 passwords_path, enable_automatic_password_saving, | 41 passwords_path, enable_automatic_password_saving): |
| 50 numeric_level=None, log_to_console=False, log_file=""): | |
| 51 """Creates a new testing Environment. | 42 """Creates a new testing Environment. |
| 52 | 43 |
| 53 Args: | 44 Args: |
| 54 chrome_path: The chrome binary file. | 45 chrome_path: The chrome binary file. |
| 55 chromedriver_path: The chromedriver binary file. | 46 chromedriver_path: The chromedriver binary file. |
| 56 profile_path: The chrome testing profile folder. | 47 profile_path: The chrome testing profile folder. |
| 57 passwords_path: The usernames and passwords file. | 48 passwords_path: The usernames and passwords file. |
| 58 enable_automatic_password_saving: If True, the passwords are going to be | 49 enable_automatic_password_saving: If True, the passwords are going to be |
| 59 saved without showing the prompt. | 50 saved without showing the prompt. |
| 60 numeric_level: The log verbosity. | |
| 61 log_to_console: If True, the debug logs will be shown on the console. | |
| 62 log_file: The file where to store the log. If it's empty, the log will | |
| 63 not be stored. | |
| 64 | 51 |
| 65 Raises: | 52 Raises: |
| 66 Exception: An exception is raised if |profile_path| folder could not be | 53 Exception: An exception is raised if |profile_path| folder could not be |
| 67 removed. | 54 removed. |
| 68 """ | 55 """ |
| 69 # Setting up the login. | |
| 70 if numeric_level is not None: | |
| 71 if log_file: | |
| 72 # Set up logging to file. | |
| 73 logging.basicConfig(level=numeric_level, | |
| 74 filename=log_file, | |
| 75 filemode='w') | |
| 76 | |
| 77 if log_to_console: | |
| 78 console = logging.StreamHandler() | |
| 79 console.setLevel(numeric_level) | |
| 80 # Add the handler to the root logger. | |
| 81 logging.getLogger('').addHandler(console) | |
| 82 | |
| 83 elif log_to_console: | |
| 84 logging.basicConfig(level=numeric_level) | |
| 85 | 56 |
| 86 # Cleaning the chrome testing profile folder. | 57 # Cleaning the chrome testing profile folder. |
| 87 try: | 58 shutil.rmtree(profile_path, ignore_errors=True) |
|
engedy
2015/03/26 16:18:55
nit: Given that this masks both kinds of errors (i
vabr (Chromium)
2015/03/26 18:01:50
Done.
| |
| 88 shutil.rmtree(profile_path) | 59 options = Options() |
| 89 except Exception, e: | 60 self.enable_automatic_password_saving = enable_automatic_password_saving |
| 90 pass | 61 if enable_automatic_password_saving: |
| 91 # If |chrome_path| is not defined, this means that we are in the dashboard | 62 options.add_argument("enable-automatic-password-saving") |
|
engedy
2015/03/26 16:18:55
Could you please mention in the CL description tha
vabr (Chromium)
2015/03/26 18:01:50
This was not so much removed, as simplified. In th
engedy
2015/03/26 18:04:54
Acknowledged.
| |
| 92 # website, and we just need to get the list of all websites. In this case, | 63 # Chrome path. |
| 93 # we don't need to initilize the webdriver. | 64 options.binary_location = chrome_path |
| 94 if chrome_path: | 65 # Chrome testing profile path. |
| 95 options = Options() | 66 options.add_argument("user-data-dir=%s" % profile_path) |
| 96 self.enable_automatic_password_saving = enable_automatic_password_saving | |
| 97 if enable_automatic_password_saving: | |
| 98 options.add_argument("enable-automatic-password-saving") | |
| 99 # Chrome path. | |
| 100 options.binary_location = chrome_path | |
| 101 # Chrome testing profile path. | |
| 102 options.add_argument("user-data-dir=%s" % profile_path) | |
| 103 | 67 |
| 104 # The webdriver. It's possible to choose the port the service is going to | 68 # The webdriver. It's possible to choose the port the service is going to |
| 105 # run on. If it's left to 0, a free port will be found. | 69 # run on. If it's left to 0, a free port will be found. |
| 106 self.driver = webdriver.Chrome(chromedriver_path, 0, options) | 70 self.driver = webdriver.Chrome(chromedriver_path, 0, options) |
| 107 # The password internals window. | 71 # The password internals window. |
| 108 self.internals_window = self.driver.current_window_handle | 72 self.internals_window = self.driver.current_window_handle |
| 109 if passwords_path: | 73 if passwords_path: |
| 110 # An xml tree filled with logins and passwords. | 74 # An xml tree filled with logins and passwords. |
| 111 self.passwords_tree = ElementTree.parse(passwords_path).getroot() | 75 self.passwords_tree = ElementTree.parse(passwords_path).getroot() |
| 112 else: | 76 else: |
| 113 raise Exception("Error: |passwords_path| needs to be provided if" | 77 raise Exception("Error: |passwords_path| needs to be provided if" |
| 114 "|chrome_path| is provided, otherwise the tests could not be run") | 78 "|chrome_path| is provided, otherwise the tests could not be run") |
| 115 # Password internals page. | 79 # Password internals page. |
| 116 self.internals_page = "chrome://password-manager-internals/" | 80 self.internals_page = "chrome://password-manager-internals/" |
| 117 # The Website window. | 81 # The Website window. |
| 118 self.website_window = None | 82 self.website_window = None |
| 119 # The WebsiteTests list. | 83 # The WebsiteTests list. |
| 120 self.websitetests = [] | 84 self.websitetests = [] |
| 121 # Map messages to the number of their appearance in the log. | 85 # Map messages to the number of their appearance in the log. |
| 122 self.message_count = dict() | 86 self.message_count = { MESSAGE_ASK: 0, MESSAGE_SAVE: 0 } |
| 123 self.message_count[MESSAGE_ASK] = 0 | |
| 124 self.message_count[MESSAGE_SAVE] = 0 | |
| 125 # The tests needs two tabs to work. A new tab is opened with the first | 87 # The tests needs two tabs to work. A new tab is opened with the first |
| 126 # GoTo. This is why we store here whether or not it's the first time to | 88 # GoTo. This is why we store here whether or not it's the first time to |
| 127 # execute GoTo. | 89 # execute GoTo. |
| 128 self.first_go_to = True | 90 self.first_go_to = True |
| 129 # List of all tests results. | 91 # List of all tests results. |
| 130 self.tests_results = [] | 92 self.tests_results = [] |
| 131 | 93 |
| 132 def AddWebsiteTest(self, websitetest): | 94 def AddWebsiteTest(self, websitetest): |
| 133 """Adds a WebsiteTest to the testing Environment. | 95 """Adds a WebsiteTest to the testing Environment. |
| 134 | 96 |
| 97 TODO(vabr): Currently, this is only called at most once for each | |
| 98 Environment instance. That is because to run all tests efficiently in | |
| 99 parallel, each test gets its own process spawned (outside of Python). | |
| 100 That makes sense, but then we should flatten the hierarchy of calls | |
| 101 and consider making the 1:1 relation of environment to tests more | |
| 102 explicit. | |
| 103 | |
| 135 Args: | 104 Args: |
| 136 websitetest: The WebsiteTest instance to be added. | 105 websitetest: The WebsiteTest instance to be added. |
| 137 """ | 106 """ |
| 138 websitetest.environment = self | 107 websitetest.environment = self |
| 139 if hasattr(self, "driver"): | 108 websitetest.driver = self.driver |
|
engedy
2015/03/26 16:18:55
nit: Please add a todo to make this a property.
vabr (Chromium)
2015/03/26 18:01:49
Done.
| |
| 140 websitetest.driver = self.driver | 109 if not websitetest.username: |
| 141 if hasattr(self, "passwords_tree") and self.passwords_tree is not None: | 110 username_tag = ( |
| 142 if not websitetest.username: | 111 self.passwords_tree.find( |
| 143 username_tag = ( | 112 ".//*[@name='%s']/username" % websitetest.name)) |
| 144 self.passwords_tree.find( | 113 websitetest.username = username_tag.text |
| 145 ".//*[@name='%s']/username" % websitetest.name)) | 114 if not websitetest.password: |
| 146 if username_tag.text: | 115 password_tag = ( |
| 147 websitetest.username = username_tag.text | 116 self.passwords_tree.find( |
| 148 if not websitetest.password: | 117 ".//*[@name='%s']/password" % websitetest.name)) |
| 149 password_tag = ( | 118 websitetest.password = password_tag.text |
| 150 self.passwords_tree.find( | |
| 151 ".//*[@name='%s']/password" % websitetest.name)) | |
| 152 if password_tag.text: | |
| 153 websitetest.password = password_tag.text | |
| 154 self.websitetests.append(websitetest) | 119 self.websitetests.append(websitetest) |
| 155 | 120 |
| 156 def ClearCache(self, clear_passwords): | 121 def ClearCache(self, clear_passwords): |
| 157 """Clear the browser cookies. If |clear_passwords| is true, clear all the | 122 """Clear the browser cookies. If |clear_passwords| is true, clear all the |
| 158 saved passwords too. | 123 saved passwords too. |
| 159 | 124 |
| 160 Args: | 125 Args: |
| 161 clear_passwords : Clear all the passwords if the bool value is true. | 126 clear_passwords : Clear all the passwords if the bool value is true. |
| 162 """ | 127 """ |
| 163 logging.info("\nClearCache\n") | |
| 164 self.driver.get("chrome://settings/clearBrowserData") | 128 self.driver.get("chrome://settings/clearBrowserData") |
| 165 self.driver.switch_to_frame("settings") | 129 self.driver.switch_to_frame("settings") |
| 166 script = ( | 130 script = ( |
| 167 "if (!document.querySelector('#delete-cookies-checkbox').checked)" | 131 "if (!document.querySelector('#delete-cookies-checkbox').checked)" |
| 168 " document.querySelector('#delete-cookies-checkbox').click();" | 132 " document.querySelector('#delete-cookies-checkbox').click();" |
| 169 ) | 133 ) |
| 170 negation = "" | 134 negation = "" |
| 171 if clear_passwords: | 135 if clear_passwords: |
| 172 negation = "!" | 136 negation = "!" |
| 173 script += ( | 137 script += ( |
| 174 "if (%sdocument.querySelector('#delete-passwords-checkbox').checked)" | 138 "if (%sdocument.querySelector('#delete-passwords-checkbox').checked)" |
| 175 " document.querySelector('#delete-passwords-checkbox').click();" | 139 " document.querySelector('#delete-passwords-checkbox').click();" |
| 176 % negation) | 140 % negation) |
| 177 script += "document.querySelector('#clear-browser-data-commit').click();" | 141 script += "document.querySelector('#clear-browser-data-commit').click();" |
| 178 self.driver.execute_script(script) | 142 self.driver.execute_script(script) |
| 179 time.sleep(2) | 143 time.sleep(2) |
| 180 # Every time we do something to the cache let's enable password saving. | 144 # Every time we do something to the cache let's enable password saving. |
| 181 # TODO(melandory): We should check why it's off in a first place. | 145 # TODO(melandory): We should check why it's off in a first place. |
| 182 # TODO(melandory): Investigate, maybe there is no need to enable it that | 146 # TODO(melandory): Investigate, maybe there is no need to enable it that |
| 183 # often. | 147 # often. |
| 184 self.EnablePasswordsSaving() | 148 self.EnablePasswordsSaving() |
| 185 | 149 |
| 186 def EnablePasswordsSaving(self): | 150 def EnablePasswordsSaving(self): |
| 187 logging.info("\nEnablePasswordSaving\n") | |
| 188 self.driver.get("chrome://settings") | 151 self.driver.get("chrome://settings") |
| 189 self.driver.switch_to_frame("settings") | 152 self.driver.switch_to_frame("settings") |
| 190 script = "document.getElementById('advanced-settings-expander').click();" | 153 script = "document.getElementById('advanced-settings-expander').click();" |
| 191 self.driver.execute_script(script) | 154 self.driver.execute_script(script) |
| 192 time.sleep(2) | 155 time.sleep(2) |
| 193 script = ( | 156 script = ( |
| 194 "if (!document.querySelector('#password-manager-enabled').checked)" | 157 "if (!document.querySelector('#password-manager-enabled').checked)" |
| 195 "{ document.querySelector('#password-manager-enabled').click();}") | 158 "{ document.querySelector('#password-manager-enabled').click();}") |
| 196 self.driver.execute_script(script) | 159 self.driver.execute_script(script) |
| 197 time.sleep(2) | 160 time.sleep(2) |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 287 Exception: An exception is raised in case the result does not match the | 250 Exception: An exception is raised in case the result does not match the |
| 288 expectation | 251 expectation |
| 289 """ | 252 """ |
| 290 if (self._DidMessageAppearUntilTimeout(log_message, timeout) != | 253 if (self._DidMessageAppearUntilTimeout(log_message, timeout) != |
| 291 message_should_show_up): | 254 message_should_show_up): |
| 292 raise Exception(error_message) | 255 raise Exception(error_message) |
| 293 | 256 |
| 294 def AllTests(self, prompt_test): | 257 def AllTests(self, prompt_test): |
| 295 """Runs the tests on all the WebsiteTests. | 258 """Runs the tests on all the WebsiteTests. |
| 296 | 259 |
| 260 TODO(vabr): Currently, "all tests" always means one. | |
| 261 | |
| 297 Args: | 262 Args: |
| 298 prompt_test: If True, tests caring about showing the save-password | 263 prompt_test: If True, tests caring about showing the save-password |
| 299 prompt are going to be run, otherwise tests which don't care about | 264 prompt are going to be run, otherwise tests which don't care about |
| 300 the prompt are going to be run. | 265 the prompt are going to be run. |
| 301 | 266 |
| 302 Raises: | 267 Raises: |
| 303 Exception: An exception is raised if the tests fail. | 268 Exception: An exception is raised if the tests fail. |
| 304 """ | 269 """ |
| 305 if prompt_test: | 270 if prompt_test: |
| 306 self.PromptTestList(self.websitetests) | 271 self.PromptTestList(self.websitetests) |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 380 except Exception as e: | 345 except Exception as e: |
| 381 successful = False | 346 successful = False |
| 382 error = e.message | 347 error = e.message |
| 383 self.tests_results.append(TestResult(websitetest.name, "prompt", | 348 self.tests_results.append(TestResult(websitetest.name, "prompt", |
| 384 successful, error)) | 349 successful, error)) |
| 385 | 350 |
| 386 def Quit(self): | 351 def Quit(self): |
| 387 """Closes the tests.""" | 352 """Closes the tests.""" |
| 388 # Close the webdriver. | 353 # Close the webdriver. |
| 389 self.driver.quit() | 354 self.driver.quit() |
| OLD | NEW |