OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 """A wrapper around adb commands called by chromedriver. | |
7 | |
8 Preconditions: | |
9 - A single device is attached. | |
10 - adb is in PATH. | |
11 | |
12 This script should write everything (including stacktraces) to stdout. | |
13 """ | |
14 | |
15 import collections | |
16 import optparse | |
17 import subprocess | |
18 import sys | |
19 import traceback | |
20 | |
21 | |
22 PackageInfo = collections.namedtuple('PackageInfo', ['activity', 'socket']) | |
23 CHROME_INFO = PackageInfo('Main', 'chrome_devtools_remote') | |
24 PACKAGE_INFO = { | |
25 'org.chromium.chrome.testshell': | |
26 PackageInfo('ChromiumTestShellActivity', | |
27 'chromium_testshell_devtools_remote'), | |
28 'com.google.android.apps.chrome': CHROME_INFO, | |
29 'com.chrome.dev': CHROME_INFO, | |
30 'com.chrome.beta': CHROME_INFO, | |
31 'com.android.chrome': CHROME_INFO, | |
32 } | |
33 | |
34 | |
35 class AdbError(Exception): | |
36 def __init__(self, message, output, cmd): | |
37 self.message = message | |
38 self.output = output | |
39 self.cmd = cmd | |
40 | |
41 def __str__(self): | |
42 return ('%s\nCommand: "%s"\nOutput: "%s"' % | |
43 (self.message, self.cmd, self.output)) | |
44 | |
45 | |
46 def RunAdbCommand(args): | |
47 """Executes an ADB command and returns its output. | |
48 | |
49 Args: | |
50 args: A sequence of program arguments supplied to adb. | |
51 | |
52 Returns: | |
53 output of the command (stdout + stderr). | |
54 | |
55 Raises: | |
56 AdbError: if exit code is non-zero. | |
57 """ | |
58 args = ['adb', '-d'] + args | |
59 try: | |
60 p = subprocess.Popen(args, stdout=subprocess.PIPE, | |
61 stderr=subprocess.STDOUT) | |
62 out, _ = p.communicate() | |
63 if p.returncode: | |
64 raise AdbError('Command failed.', out, args) | |
65 return out | |
66 except OSError as e: | |
67 print 'Make sure adb command is in PATH.' | |
68 raise e | |
69 | |
70 | |
71 def SetChromeFlags(): | |
72 """Sets the command line flags file on device. | |
73 | |
74 Raises: | |
75 AdbError: If failed to write the flags file to device. | |
76 """ | |
77 cmd = [ | |
78 'shell', | |
79 'echo chrome --disable-fre --metrics-recording-only ' | |
80 '--enable-remote-debugging > /data/local/chrome-command-line;' | |
81 'echo $?' | |
82 ] | |
83 out = RunAdbCommand(cmd).strip() | |
84 if out != '0': | |
85 raise AdbError('Failed to set the command line flags.', out, cmd) | |
86 | |
87 | |
88 def ClearAppData(package): | |
89 """Clears the app data. | |
90 | |
91 Args: | |
92 package: Application package name. | |
93 | |
94 Raises: | |
95 AdbError: if any step fails. | |
96 """ | |
97 cmd = ['shell', 'pm clear %s' % package] | |
98 # am/pm package do not return valid exit codes. | |
99 out = RunAdbCommand(cmd) | |
100 if 'Success' not in out: | |
101 raise AdbError('Failed to clear the profile.', out, cmd) | |
102 | |
103 | |
104 def StartActivity(package): | |
105 """Start the activity in the package. | |
106 | |
107 Args: | |
108 package: Application package name. | |
109 | |
110 Raises: | |
111 AdbError: if any step fails. | |
112 """ | |
113 cmd = [ | |
114 'shell', | |
115 'am start -a android.intent.action.VIEW -S -W -n %s/.%s ' | |
116 '-d "data:text/html;charset=utf-8,"' % | |
117 (package, PACKAGE_INFO[package].activity)] | |
118 out = RunAdbCommand(cmd) | |
119 if 'Complete' not in out: | |
120 raise AdbError('Failed to start the activity. %s', out, cmd) | |
121 | |
122 | |
123 def Forward(package, host_port): | |
124 """Forward host socket to devtools socket on the device. | |
125 | |
126 Args: | |
127 package: Application package name. | |
128 host_port: Port on host to forward. | |
129 | |
130 Raises: | |
131 AdbError: if command fails. | |
132 """ | |
133 cmd = ['forward', 'tcp:%d' % host_port, | |
134 'localabstract:%s' % PACKAGE_INFO[package].socket] | |
135 RunAdbCommand(cmd) | |
136 | |
137 | |
138 if __name__ == '__main__': | |
139 try: | |
140 parser = optparse.OptionParser() | |
141 parser.add_option( | |
142 '', '--package', help='Application package name.') | |
143 parser.add_option( | |
144 '', '--launch', action='store_true', | |
145 help='Launch the app with a fresh profile and forward devtools port.') | |
146 parser.add_option( | |
147 '', '--port', type='int', default=33081, | |
148 help='Host port to forward for launch operation [default: %default].') | |
149 options, _ = parser.parse_args() | |
150 | |
151 if not options.package: | |
152 raise Exception('No package specified.') | |
153 | |
154 if options.package not in PACKAGE_INFO: | |
155 raise Exception('Unknown package provided. Supported packages are:\n %s' % | |
156 PACKAGE_INFO.keys()) | |
157 | |
158 if options.launch: | |
159 SetChromeFlags() | |
160 ClearAppData(options.package) | |
161 StartActivity(options.package) | |
162 Forward(options.package, options.port) | |
163 else: | |
164 raise Exception('No options provided.') | |
165 except: | |
166 traceback.print_exc(file=sys.stdout) | |
167 sys.exit(1) | |
168 | |
OLD | NEW |