summaryrefslogtreecommitdiffstats
path: root/chrome/test/chromedriver/adb_commands.py
blob: 241b68b83265184f21e8f7ab888270b17fbf5da6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/env python
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""A wrapper around adb commands called by chromedriver.

Prerequisites:
  - A single device is attached.
  - adb is in PATH.

This script should write everything (including stacktraces) to stdout.
"""

import optparse
import subprocess
import sys
import traceback


# {<package name>: (<activity name>, <device abstract socket>)}
PACKAGE_INFO = {'org.chromium.chrome.testshell':
                ('org.chromium.chrome.testshell.ChromiumTestShellActivity',
                 'chromium_testshell_devtools_remote'),
                'com.google.android.apps.chrome':
                ('com.google.android.apps.chrome.Main',
                 'chrome_devtools_remote')}


class AdbError(Exception):
  def __init__(self, message, output, cmd):
    self.message = message
    self.output = output
    self.cmd = cmd

  def __str__(self):
    return ('%s\nCommand: "%s"\nOutput: "%s"' %
            (self.message, self.cmd, self.output))


def RunAdbCommand(args, cwd=None):
  """Executes an ADB command and returns its output.

  Args:
    args: A sequence of program arguments supplied to adb.
    cwd: If not None, the subprocess's current directory will be changed to
         |cwd| before it's executed.

  Returns:
    A tuple: (stdout + stderr, command string)

  Raises:
    AdbError: if exit code is non-zero.
  """
  args.insert(0, 'adb')
  try:
    p = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)
    stdout, _ = p.communicate()
    args = ' '.join(args)
    if p.returncode:
      raise AdbError('Command failed.', stdout, args)
    return stdout, args
  except OSError as e:
    print 'Make sure adb command is in PATH.'
    raise e


def ClearAppData(package):
  """Clears the app data.

  Args:
    package: Application package name.

  Raises:
    AdbError: if any step fails.
  """
  out, cmd = RunAdbCommand(['shell', 'pm', 'clear', package])
  # am/pm package do not return valid exit codes.
  if 'Success' not in out:
    raise AdbError('Failed to clear the profile.', out, cmd)


def LaunchApp(package):
  """Launches the application.

  Args:
    package: Application package name.

  Raises:
    AdbError: if any step fails.
  """
  out, cmd = RunAdbCommand(
      ['shell', 'am', 'start', '-a', 'android.intent.action.VIEW',
       '-S', '-W', '-n', '%s/%s' % (package, PACKAGE_INFO[package][0])])
  if 'Complete' not in out:
    raise AdbError('Failed to start the app. %s', out, cmd)


def Forward(package, host_port):
  """Forward host socket to devtools socket on the device.

  Args:
    package: Application package name.
    host_port: Port on host to forward.

  Raises:
    AdbError: if command fails.
  """
  RunAdbCommand(['forward', 'tcp:%d' % host_port, 'localabstract:%s' %
                 PACKAGE_INFO[package][1]])


if __name__ == '__main__':
  try:
    parser = optparse.OptionParser()
    parser.add_option(
        '', '--package', help='Application package name.')
    parser.add_option(
        '', '--launch', action='store_true',
        help='Launch the app with a fresh profile and forward devtools port.')
    parser.add_option(
        '', '--port', type='int', default=33081,
        help='Host port to forward for launch operation [default: %default].')
    options, _ = parser.parse_args()

    if not options.package:
      raise Exception('No package specified.')

    if options.package not in PACKAGE_INFO:
      raise Exception('Unkown package provided. Supported packages are:\n %s' %
                      PACKAGE_INFO.keys())

    if options.launch:
      ClearAppData(options.package)
      LaunchApp(options.package)
      Forward(options.package, options.port)
    else:
      raise Exception('No options provided.')
  except:
    traceback.print_exc(file=sys.stdout)
    sys.exit(1)