OLD | NEW |
| (Empty) |
1 # Copyright 2013 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 import copy | |
6 import ctypes | |
7 import email | |
8 import logging | |
9 import os | |
10 import shutil | |
11 import smtplib | |
12 import subprocess | |
13 import sys | |
14 import types | |
15 | |
16 import pyauto_functional | |
17 import pyauto | |
18 import pyauto_utils | |
19 import pyauto_errors | |
20 | |
21 | |
22 """Commonly used functions for PyAuto tests.""" | |
23 | |
24 def CrashBrowser(test): | |
25 """Crashes the browser by navigating to special URL.""" | |
26 try: | |
27 test.NavigateToURL('chrome://inducebrowsercrashforrealz') | |
28 except pyauto_errors.JSONInterfaceError: | |
29 pass | |
30 else: | |
31 raise RuntimeError( | |
32 'Browser did not crash at chrome://inducebrowsercrashforrealz') | |
33 | |
34 | |
35 def CopyFileFromDataDirToDownloadDir(test, file_path): | |
36 """Copy a file from data directory to downloads directory. | |
37 | |
38 Args: | |
39 test: derived from pyauto.PyUITest - base class for UI test cases. | |
40 path: path of the file relative to the data directory | |
41 """ | |
42 data_file = os.path.join(test.DataDir(), file_path) | |
43 download_dir = test.GetDownloadDirectory().value() | |
44 shutil.copy(data_file, download_dir) | |
45 | |
46 | |
47 def CopyFileFromContentDataDirToDownloadDir(test, file_path): | |
48 """Copy a file from content data directory to downloads directory. | |
49 | |
50 Args: | |
51 test: derived from pyauto.PyUITest - base class for UI test cases. | |
52 path: path of the file relative to the data directory | |
53 """ | |
54 data_file = os.path.join(test.ContentDataDir(), file_path) | |
55 download_dir = test.GetDownloadDirectory().value() | |
56 shutil.copy(data_file, download_dir) | |
57 | |
58 | |
59 def RemoveDownloadedTestFile(test, file_name): | |
60 """Delete a file from the downloads directory. | |
61 | |
62 Arg: | |
63 test: derived from pyauto.PyUITest - base class for UI test cases | |
64 file_name: name of file to remove | |
65 """ | |
66 downloaded_pkg = os.path.join(test.GetDownloadDirectory().value(), | |
67 file_name) | |
68 pyauto_utils.RemovePath(downloaded_pkg) | |
69 pyauto_utils.RemovePath(downloaded_pkg + '.crdownload') | |
70 | |
71 | |
72 def GoogleAccountsLogin(test, username, password, | |
73 tab_index=0, windex=0, url=None): | |
74 """Log into Google Accounts. | |
75 | |
76 Attempts to login to Google by entering the username/password into the google | |
77 login page and click submit button. | |
78 | |
79 Args: | |
80 test: derived from pyauto.PyUITest - base class for UI test cases. | |
81 username: users login input. | |
82 password: users login password input. | |
83 tab_index: The tab index, default is 0. | |
84 windex: The window index, default is 0. | |
85 url: an alternative url for login page, if None, original one will be used. | |
86 """ | |
87 url = url or 'https://accounts.google.com/' | |
88 test.NavigateToURL(url, windex, tab_index) | |
89 email_id = 'document.getElementById("Email").value = "%s"; ' \ | |
90 'window.domAutomationController.send("done")' % username | |
91 password = 'document.getElementById("Passwd").value = "%s"; ' \ | |
92 'window.domAutomationController.send("done")' % password | |
93 test.ExecuteJavascript(email_id, tab_index, windex) | |
94 test.ExecuteJavascript(password, tab_index, windex) | |
95 test.assertTrue(test.SubmitForm('gaia_loginform', tab_index, windex)) | |
96 | |
97 | |
98 def VerifyGoogleAccountCredsFilled(test, username, password, tab_index=0, | |
99 windex=0): | |
100 """Verify stored/saved user and password values to the values in the field. | |
101 | |
102 Args: | |
103 test: derived from pyauto.PyUITest - base class for UI test cases. | |
104 username: user log in input. | |
105 password: user log in password input. | |
106 tab_index: The tab index, default is 0. | |
107 windex: The window index, default is 0. | |
108 """ | |
109 email_value = test.GetDOMValue('document.getElementById("Email").value', | |
110 tab_index, windex) | |
111 passwd_value = test.GetDOMValue('document.getElementById("Passwd").value', | |
112 tab_index, windex) | |
113 test.assertEqual(email_value, username) | |
114 # Not using assertEqual because if it fails it would end up dumping the | |
115 # password (which is supposed to be private) | |
116 test.assertTrue(passwd_value == password) | |
117 | |
118 | |
119 def Shell2(cmd_string, bg=False): | |
120 """Run a shell command. | |
121 | |
122 Args: | |
123 cmd_string: command to run | |
124 bg: should the process be run in background? Default: False | |
125 | |
126 Returns: | |
127 Output, return code | |
128 """ | |
129 if not cmd_string: return ('', 0) | |
130 if bg: | |
131 cmd_string += ' 1>/dev/null 2>&1 &' | |
132 proc = os.popen(cmd_string) | |
133 if bg: return ('Background process: %s' % cmd_string, 0) | |
134 out = proc.read() | |
135 retcode = proc.close() | |
136 if not retcode: # Success | |
137 retcode = 0 | |
138 return (out, retcode) | |
139 | |
140 | |
141 def SendMail(send_from, send_to, subject, text, smtp, file_to_send=None): | |
142 """Send mail to all the group to notify about the crash and uploaded data. | |
143 | |
144 Args: | |
145 send_from: From mail id as a string. | |
146 send_to: To mail id. Can be a string representing a single address, or a | |
147 list of strings representing multiple addresses. | |
148 subject: Mail subject as a string. | |
149 text: Mail body as a string. | |
150 smtp: The smtp to use, as a string. | |
151 file_to_send: Attachments for the mail. | |
152 """ | |
153 msg = email.MIMEMultipart.MIMEMultipart() | |
154 msg['From'] = send_from | |
155 if isinstance(send_to, list): | |
156 msg['To'] = ','.join(send_to) | |
157 else: | |
158 msg['To'] = send_to | |
159 msg['Date'] = email.Utils.formatdate(localtime=True) | |
160 msg['Subject'] = subject | |
161 | |
162 # To send multiple files in one message, introduce for loop here for files. | |
163 msg.attach(email.MIMEText.MIMEText(text)) | |
164 part = email.MIMEBase.MIMEBase('application', 'octet-stream') | |
165 if file_to_send is not None: | |
166 part.set_payload(open(file_to_send,'rb').read()) | |
167 email.Encoders.encode_base64(part) | |
168 part.add_header('Content-Disposition', | |
169 'attachment; filename="%s"' | |
170 % os.path.basename(file_to_send)) | |
171 msg.attach(part) | |
172 smtp_obj = smtplib.SMTP(smtp) | |
173 smtp_obj.sendmail(send_from, send_to, msg.as_string()) | |
174 smtp_obj.close() | |
175 | |
176 | |
177 def GetFreeSpace(path): | |
178 """Returns the free space (in bytes) on the drive containing |path|.""" | |
179 if sys.platform == 'win32': | |
180 free_bytes = ctypes.c_ulonglong(0) | |
181 ctypes.windll.kernel32.GetDiskFreeSpaceExW( | |
182 ctypes.c_wchar_p(os.path.dirname(path)), None, None, | |
183 ctypes.pointer(free_bytes)) | |
184 return free_bytes.value | |
185 fs_stat = os.statvfs(path) | |
186 return fs_stat.f_bsize * fs_stat.f_bavail | |
187 | |
188 | |
189 def StripUnmatchedKeys(item_to_strip, reference_item): | |
190 """Returns a copy of 'item_to_strip' where unmatched key-value pairs in | |
191 every dictionary are removed. | |
192 | |
193 This will examine each dictionary in 'item_to_strip' recursively, and will | |
194 remove keys that are not found in the corresponding dictionary in | |
195 'reference_item'. This is useful for testing equality of a subset of data. | |
196 | |
197 Items may contain dictionaries, lists, or primitives, but only corresponding | |
198 dictionaries will be stripped. A corresponding entry is one which is found | |
199 in the same index in the corresponding parent array or at the same key in the | |
200 corresponding parent dictionary. | |
201 | |
202 Arg: | |
203 item_to_strip: item to copy and remove all unmatched key-value pairs | |
204 reference_item: item that serves as a reference for which keys-value pairs | |
205 to strip from 'item_to_strip' | |
206 | |
207 Returns: | |
208 a copy of 'item_to_strip' where all key-value pairs that do not have a | |
209 matching key in 'reference_item' are removed | |
210 | |
211 Example: | |
212 item_to_strip = {'tabs': 3, | |
213 'time': 5908} | |
214 reference_item = {'tabs': 2} | |
215 StripUnmatchedKeys(item_to_strip, reference_item) will return {'tabs': 3} | |
216 """ | |
217 def StripList(list1, list2): | |
218 return_list = copy.deepcopy(list2) | |
219 for i in range(min(len(list1), len(list2))): | |
220 return_list[i] = StripUnmatchedKeys(list1[i], list2[i]) | |
221 return return_list | |
222 | |
223 def StripDict(dict1, dict2): | |
224 return_dict = {} | |
225 for key in dict1: | |
226 if key in dict2: | |
227 return_dict[key] = StripUnmatchedKeys(dict1[key], dict2[key]) | |
228 return return_dict | |
229 | |
230 item_to_strip_type = type(item_to_strip) | |
231 if item_to_strip_type is type(reference_item): | |
232 if item_to_strip_type is types.ListType: | |
233 return StripList(item_to_strip, reference_item) | |
234 elif item_to_strip_type is types.DictType: | |
235 return StripDict(item_to_strip, reference_item) | |
236 return copy.deepcopy(item_to_strip) | |
237 | |
238 | |
239 def StringContentCheck(test, content_string, have_list, nothave_list): | |
240 """Check for the presence or absence of strings within content. | |
241 | |
242 Confirm all strings in |have_list| are found in |content_string|. | |
243 Confirm all strings in |nothave_list| are not found in |content_string|. | |
244 | |
245 Args: | |
246 content_string: string containing the content to check. | |
247 have_list: list of strings expected to be found within the content. | |
248 nothave_list: list of strings expected to not be found within the content. | |
249 """ | |
250 for s in have_list: | |
251 test.assertTrue(s in content_string, | |
252 msg='"%s" missing from content.' % s) | |
253 for s in nothave_list: | |
254 test.assertTrue(s not in content_string, | |
255 msg='"%s" unexpectedly contained in content.' % s) | |
256 | |
257 | |
258 def CallFunctionWithNewTimeout(self, new_timeout, function): | |
259 """Sets the timeout to |new_timeout| and calls |function|. | |
260 | |
261 This method resets the timeout before returning. | |
262 """ | |
263 timeout_changer = pyauto.PyUITest.ActionTimeoutChanger( | |
264 self, new_timeout) | |
265 logging.info('Automation execution timeout has been changed to %d. ' | |
266 'If the timeout is large the test might appear to hang.' | |
267 % new_timeout) | |
268 function() | |
269 del timeout_changer | |
270 | |
271 | |
272 def GetOmniboxMatchesFor(self, text, windex=0, attr_dict=None): | |
273 """Fetch omnibox matches with the given attributes for the given query. | |
274 | |
275 Args: | |
276 text: the query text to use | |
277 windex: the window index to work on. Defaults to 0 (first window) | |
278 attr_dict: the dictionary of properties to be satisfied | |
279 | |
280 Returns: | |
281 a list of match items | |
282 """ | |
283 self.SetOmniboxText(text, windex=windex) | |
284 self.WaitUntilOmniboxQueryDone(windex=windex) | |
285 if not attr_dict: | |
286 matches = self.GetOmniboxInfo(windex=windex).Matches() | |
287 else: | |
288 matches = self.GetOmniboxInfo(windex=windex).MatchesWithAttributes( | |
289 attr_dict=attr_dict) | |
290 return matches | |
291 | |
292 | |
293 def GetMemoryUsageOfProcess(pid): | |
294 """Queries the system for the current memory usage of a specified process. | |
295 | |
296 This function only works in Linux and ChromeOS. | |
297 | |
298 Args: | |
299 pid: The integer process identifier for the process to use. | |
300 | |
301 Returns: | |
302 The memory usage of the process in MB, given as a float. If the process | |
303 doesn't exist on the machine, then the value 0 is returned. | |
304 """ | |
305 assert pyauto.PyUITest.IsLinux() or pyauto.PyUITest.IsChromeOS() | |
306 process = subprocess.Popen('ps h -o rss -p %s' % pid, shell=True, | |
307 stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
308 stdout = process.communicate()[0] | |
309 if stdout: | |
310 return float(stdout.strip()) / 1024 | |
311 else: | |
312 return 0 | |
313 | |
314 | |
315 def LoginToDevice(test, test_account='test_google_account'): | |
316 """Login to the Chromeos device using the given test account. | |
317 | |
318 If no test account is specified, we use test_google_account as the default. | |
319 You can choose test accounts from - | |
320 chrome/test/data/pyauto_private/private_tests_info.txt | |
321 | |
322 Args: | |
323 test_account: The account used to login to the Chromeos device. | |
324 """ | |
325 if not test.GetLoginInfo()['is_logged_in']: | |
326 credentials = test.GetPrivateInfo()[test_account] | |
327 test.Login(credentials['username'], credentials['password']) | |
328 login_info = test.GetLoginInfo() | |
329 test.assertTrue(login_info['is_logged_in'], msg='Login failed.') | |
330 else: | |
331 test.fail(msg='Another user is already logged in. Please logout first.') | |
332 | |
333 def GetInfobarIndexByType(test, infobar_type, windex=0, tab_index=0): | |
334 """Returns the index of the infobar of the given type. | |
335 | |
336 Args: | |
337 test: Derived from pyauto.PyUITest - base class for UI test cases. | |
338 infobar_type: The infobar type to look for. | |
339 windex: Window index. | |
340 tab_index: Tab index. | |
341 | |
342 Returns: | |
343 Index of infobar for infobar type, or None if not found. | |
344 """ | |
345 infobar_list = ( | |
346 test.GetBrowserInfo()['windows'][windex]['tabs'][tab_index] \ | |
347 ['infobars']) | |
348 for infobar in infobar_list: | |
349 if infobar_type == infobar['type']: | |
350 return infobar_list.index(infobar) | |
351 return None | |
352 | |
353 def WaitForInfobarTypeAndGetIndex(test, infobar_type, windex=0, tab_index=0): | |
354 """Wait for infobar type to appear and returns its index. | |
355 | |
356 If the infobar never appears, an exception will be raised. | |
357 | |
358 Args: | |
359 test: Derived from pyauto.PyUITest - base class for UI test cases. | |
360 infobar_type: The infobar type to look for. | |
361 windex: Window index. Defaults to 0 (first window). | |
362 tab_index: Tab index. Defaults to 0 (first tab). | |
363 | |
364 Returns: | |
365 Index of infobar for infobar type. | |
366 """ | |
367 test.assertTrue( | |
368 test.WaitUntil(lambda: GetInfobarIndexByType( | |
369 test, infobar_type, windex, tab_index) is not None), | |
370 msg='Infobar type for %s did not appear.' % infobar_type) | |
371 # Return the infobar index. | |
372 return GetInfobarIndexByType(test, infobar_type, windex, tab_index) | |
373 | |
374 def AssertInfobarTypeDoesNotAppear(test, infobar_type, windex=0, tab_index=0): | |
375 """Check that the infobar type does not appear. | |
376 | |
377 This function waits 20s to assert that the infobar does not appear. | |
378 | |
379 Args: | |
380 test: Derived from pyauto.PyUITest - base class for UI test cases. | |
381 infobar_type: The infobar type to look for. | |
382 windex: Window index. Defaults to 0 (first window). | |
383 tab_index: Tab index. Defaults to 0 (first tab). | |
384 """ | |
385 test.assertFalse( | |
386 test.WaitUntil(lambda: GetInfobarIndexByType( | |
387 test, infobar_type, windex, tab_index) is not None, timeout=20), | |
388 msg=('Infobar type for %s appeared when it should be hidden.' | |
389 % infobar_type)) | |
390 | |
391 def OpenCroshVerification(self): | |
392 """This test opens crosh. | |
393 | |
394 This function assumes that no browser windows are open. | |
395 """ | |
396 self.assertEqual(0, self.GetBrowserWindowCount()) | |
397 self.OpenCrosh() | |
398 self.assertEqual(1, self.GetBrowserWindowCount()) | |
399 self.assertEqual(1, self.GetTabCount(), | |
400 msg='Could not open crosh') | |
401 self.assertEqual('crosh', self.GetActiveTabTitle()) | |
OLD | NEW |