Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / components / proximity_auth / e2e_test / setup_test.py
blob9c47d80cc2a91d747f5fea3cb5de5abd3e81b6ba
1 # Copyright 2015 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.
5 """ Script that exercises the Smart Lock setup flow, testing that a nearby phone
6 can be found and used to unlock a Chromebook.
8 Note: This script does not currently automate Android phones, so make sure that
9 a phone is properly configured and online before starting the test.
11 Usage:
12 python setup_test.py --remote_address REMOTE_ADDRESS
13 --username USERNAME
14 --password PASSWORD
15 [--app_path APP_PATH]
16 [--ssh_port SSH_PORT]
17 If |--app_path| is provided, then a copy of the Smart Lock app on the local
18 machine will be used instead of the app on the ChromeOS device.
19 """
21 import argparse
22 import cros
23 import cryptauth
24 import logging
25 import os
26 import subprocess
27 import sys
28 import tempfile
30 logger = logging.getLogger('proximity_auth.%s' % __name__)
32 class SmartLockSetupError(Exception):
33 pass
35 def pingable_address(address):
36 try:
37 subprocess.check_output(['ping', '-c', '1', '-W', '1', address])
38 except subprocess.CalledProcessError:
39 raise argparse.ArgumentError('%s cannot be reached.' % address)
40 return address
42 def email(arg):
43 tokens = arg.lower().split('@')
44 if len(tokens) != 2 or '.' not in tokens[1]:
45 raise argparse.ArgumentError('%s is not a valid email address' % arg)
46 name, domain = tokens
47 if domain == 'gmail.com':
48 name = name.replace('.', '')
49 return '@'.join([name, domain])
51 def directory(path):
52 if not os.path.isdir(path):
53 raise argparse.ArgumentError('%s is not a directory' % path)
54 return path
56 def ParseArgs():
57 parser = argparse.ArgumentParser(prog='python setup_test.py')
58 parser.add_argument('--remote_address', required=True, type=pingable_address)
59 parser.add_argument('--username', required=True, type=email)
60 parser.add_argument('--password', required=True)
61 parser.add_argument('--ssh_port', type=int)
62 parser.add_argument('--app_path', type=directory)
63 args = parser.parse_args()
64 return args
66 def CheckCryptAuthState(access_token):
67 cryptauth_client = cryptauth.CryptAuthClient(access_token)
69 # Check if we can make CryptAuth requests.
70 if cryptauth_client.GetMyDevices() is None:
71 logger.error('Cannot reach CryptAuth on test machine.')
72 return False
74 if cryptauth_client.GetUnlockKey() is not None:
75 logger.info('Smart Lock currently enabled, turning off on Cryptauth...')
76 if not cryptauth_client.ToggleEasyUnlock(False):
77 logger.error('ToggleEasyUnlock request failed.')
78 return False
80 result = cryptauth_client.FindEligibleUnlockDevices()
81 if result is None:
82 logger.error('FindEligibleUnlockDevices request failed')
83 return False
84 eligibleDevices, _ = result
85 if len(eligibleDevices) == 0:
86 logger.warn('No eligible phones found, trying to ping phones...')
87 result = cryptauth_client.PingPhones()
88 if result is None or not len(result[0]):
89 logger.error('Pinging phones failed :(')
90 return False
91 else:
92 logger.info('Pinging phones succeeded!')
93 else:
94 logger.info('Found eligible device: %s' % (
95 eligibleDevices[0]['friendlyDeviceName']))
96 return True
98 def _NavigateSetupDialog(chromeos, app):
99 logger.info('Scanning for nearby phones...')
100 btmon = chromeos.RunBtmon()
101 find_phone_success = app.FindPhone()
102 btmon.terminate()
104 if not find_phone_success:
105 fd, filepath = tempfile.mkstemp(prefix='btmon-')
106 os.write(fd, btmon.stdout.read())
107 os.close(fd)
108 logger.info('Logs for btmon can be found at %s' % filepath)
109 raise SmartLockSetupError("Failed to find nearby phone.")
111 logger.info('Phone found! Starting pairing...')
112 if not app.PairPhone():
113 raise SmartLockSetupError("Failed to pair with phone.")
114 logger.info('Pairing success! Starting trial run...')
115 app.StartTrialRun()
117 logger.info('Unlocking for trial run...')
118 lock_screen = chromeos.GetAccountPickerScreen()
119 lock_screen.WaitForSmartLockState(
120 lock_screen.SmartLockState.AUTHENTICATED)
121 lock_screen.UnlockWithClick()
123 logger.info('Trial run success! Dismissing app...')
124 app.DismissApp()
126 def RunSetupTest(args):
127 logger.info('Starting test for %s at %s' % (
128 args.username, args.remote_address))
129 if args.app_path is not None:
130 logger.info('Replacing Smart Lock app with %s' % args.app_path)
132 chromeos = cros.ChromeOS(
133 args.remote_address, args.username, args.password, ssh_port=args.ssh_port)
134 with chromeos.Start(local_app_path=args.app_path):
135 logger.info('Chrome initialized')
137 # TODO(tengs): The access token is currently fetched from the Smart Lock
138 # app's background page. To be more robust, we should instead mint the
139 # access token ourselves.
140 if not CheckCryptAuthState(chromeos.cryptauth_access_token):
141 raise SmartLockSetupError('Failed to check CryptAuth state')
143 logger.info('Opening Smart Lock settings...')
144 settings = chromeos.GetSmartLockSettings()
145 assert(not settings.is_smart_lock_enabled)
146 logger.info('Starting Smart Lock setup flow...')
147 app = settings.StartSetupAndReturnApp()
149 _NavigateSetupDialog(chromeos, app)
151 def main():
152 logging.basicConfig()
153 logging.getLogger('proximity_auth').setLevel(logging.INFO)
154 args = ParseArgs()
155 RunSetupTest(args)
157 if __name__ == '__main__':
158 main()