Commit 9c621c92 authored by jhorwich@chromium.org's avatar jhorwich@chromium.org

Revert 151717 - Initial checkin of the me2me pyauto automation

- Modified chromoting.py to enable me2me automation
- Added chromoting_helper.py to handle install, uninstall, enable, disable, changepin which requires admin privilege
- Added chromoting.base.py as the base for all chromoting test cases
- Modified chromoting_basic, renamed it it2me_basic and moved it under chromoting dir
- Added auth, me2me_enable, me2me_connect test
- Added a cert and a private key for signing host on mac
- Added mock_pref_pane.* files to mock the pref pane for different scenarios


NOTRY=true

Review URL: https://chromiumcodereview.appspot.com/10821015

TBR=yihongg@chromium.org
Review URL: https://chromiumcodereview.appspot.com/10837266

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@151722 0039d316-1c4b-4281-b951-d872f2087c98
parent 35d86814
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
'bookmarks', 'bookmarks',
'browser', 'browser',
'browsing_data', 'browsing_data',
'chromoting.it2me_basic', 'chromoting_basic',
'codesign', 'codesign',
'content', 'content',
'cookies', 'cookies',
...@@ -314,7 +314,7 @@ ...@@ -314,7 +314,7 @@
# You cannot resize the browser window on ChromeOS. # You cannot resize the browser window on ChromeOS.
'-browser.BrowserTest.testWindowResize', '-browser.BrowserTest.testWindowResize',
# ChromeOS doesn't yet support the chromoting host. # ChromeOS doesn't yet support the chromoting host.
'-chromoting.it2me_basic', '-chromoting_basic',
# No codesign verification on ChromeOS. # No codesign verification on ChromeOS.
'-codesign', '-codesign',
# Import tests are invalid on ChromeOS since Chrome is the only browser. # Import tests are invalid on ChromeOS since Chrome is the only browser.
...@@ -497,7 +497,7 @@ ...@@ -497,7 +497,7 @@
# Permanently-disabled tests. # Permanently-disabled tests.
# =========================== # ===========================
# The chromoting webapp isn't currently available when FULL is run. # The chromoting webapp isn't currently available when FULL is run.
'-chromoting.it2me_basic', '-chromoting_basic',
# ================================================== # ==================================================
# Disabled tests that need to be investigated/fixed. # Disabled tests that need to be investigated/fixed.
......
#!/usr/bin/env python
# Copyright (c) 2012 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.
"""Chromoting authentication related test cases."""
import chromoting_base
import pyauto
class ChromotingAuth(chromoting_base.ChromotingBase):
"""Chromoting authentication related test cases."""
def setUp(self):
"""Set up for auth test."""
pyauto.PyUITest.setUp(self)
webapp = self.InstallExtension(self.GetWebappPath())
self.host.LaunchApp(webapp)
self.account = self.GetPrivateInfo()['test_chromoting_account']
def testDenyAllowAccess(self):
"""Denies access and then allows access."""
self.host.ContinueAuth()
self.host.SignIn(self.account['username'], self.account['password'])
self.host.DenyAccess()
self.host.ContinueAuth()
self.host.AllowAccess()
def testSignOutAndSignBackIn(self):
"""Signs out from chromoting and signs back in."""
self.host.ContinueAuth()
self.host.SignIn(self.account['username'], self.account['password'])
self.host.AllowAccess()
self.host.SignOut()
self.host.ContinueAuth()
self.host.AllowAccess()
if __name__ == '__main__':
chromoting_base.Main()
# Copyright (c) 2012 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.
"""Common imports, setup, etc for chromoting tests."""
import os
def _SetupPaths():
"""Add chrome/test/functional to sys.path for importing pyauto_functional"""
functional_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
os.sys.path.append(functional_dir)
_SetupPaths()
import pyauto_functional # Must come before chromoting and pyauto.
from pyauto_functional import Main
import chromoting
import pyauto
class ChromotingBase(chromoting.ChromotingMixIn, pyauto.PyUITest):
"""Chromoting pyauto test base class.
The following member variables can be used in the child classes:
client_local: True if the client is on the same machines as host
host: The chromoting host side, instance of ChromotingBase
client: The chromoting client side, intance of ChromotingBase
client_tab_index: The tab index to the chromoting client tab
"""
def __init__(self, methodName):
pyauto.PyUITest.__init__(self, methodName)
self.client_local = (self.remote == None)
self.host = self
self.client = self if self.client_local else self.remote
self.client_tab_index = 2 if self.client_local else 1
def ExtraChromeFlags(self):
"""Add --allow-nacl-socket-api to connect chromoting successfully."""
extra_chrome_flags = ['--allow-nacl-socket-api=*',]
return pyauto.PyUITest.ExtraChromeFlags(self) + extra_chrome_flags
\ No newline at end of file
#!/usr/bin/env python
# Copyright (c) 2012 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.
"""Basic tests for Chromoting it2me."""
import chromoting_base
import pyauto
class IT2MeBasic(chromoting_base.ChromotingBase):
"""Drives it2me basic test cases."""
def setUp(self):
"""Set up for it2me basic test."""
pyauto.PyUITest.setUp(self)
webapp = self.InstallExtension(self.GetWebappPath())
self.LaunchApp(webapp)
self.Authenticate()
if self.client_local:
self.client.LaunchApp(webapp)
def testIT2MeBasic(self):
"""Verify that we can start and disconnect a Chromoting it2me session."""
access_code = self.host.Share()
self.assertTrue(access_code,
msg='Host attempted to share, but it failed. '
'No access code was found.')
self.client.Connect(access_code, self.client_tab_index)
self.host.CancelShare()
self.client.Disconnect(self.client_tab_index)
if __name__ == '__main__':
chromoting_base.Main()
#!/usr/bin/env python
# Copyright (c) 2012 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.
"""Chromoting me2me connect/disconnect related test cases."""
import chromoting_base
import pyauto
class Me2MeConnect(chromoting_base.ChromotingBase):
"""Drives me2me connect test cases."""
def setUp(self):
"""Set up for me2me connect test."""
pyauto.PyUITest.setUp(self)
self.InstallHostDaemon()
webapp = self.InstallExtension(self.GetWebappPath())
self.host.LaunchApp(webapp)
self.host.Authenticate()
self.host.StartMe2Me()
self.host.EnableConnectionsInstalled()
self.client.LaunchApp(webapp)
def tearDown(self):
"""Mainly uninstalls the host daemon."""
self.host.DisableConnections()
self.UninstallHostDaemon()
pyauto.PyUITest.tearDown(self)
def testMe2MeConnectDisconnectReconnectDisconnect(self):
"""Connects, disconnects, reconnects and disconnects"""
self.client.ConnectMe2Me('111111', 'IN_SESSION',
self.client_tab_index)
self.client.DisconnectMe2Me(False, self.client_tab_index)
self.client.ReconnectMe2Me('111111', self.client_tab_index)
self.client.DisconnectMe2Me(True, self.client_tab_index)
def testMe2MeConnectWithWrongPin(self):
"""Connects and disconnects."""
self.client.ConnectMe2Me('222222', 'CLIENT_CONNECT_FAILED_ME2ME',
self.client_tab_index)
self.client.ReconnectMe2Me('111111', self.client_tab_index)
self.client.DisconnectMe2Me(True, self.client_tab_index)
def testMe2MeChangePin(self):
"""Changes pin, connects with new pin and then disconnects."""
self.host.ChangePin('222222')
self.client.ConnectMe2Me('222222', 'IN_SESSION',
self.client_tab_index)
self.client.DisconnectMe2Me(True, self.client_tab_index)
def testMe2MeChangeName(self):
"""Changes host name, connects and then disconnects."""
self.client.ChangeName("Changed")
self.client.ConnectMe2Me('111111', 'IN_SESSION',
self.client_tab_index)
self.client.DisconnectMe2Me(True, self.client_tab_index)
if __name__ == '__main__':
chromoting_base.Main()
#!/usr/bin/env python
# Copyright (c) 2012 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.
"""Chromoting me2me enable/disable related test cases."""
import chromoting_base
import pyauto
class Me2MeEnable(chromoting_base.ChromotingBase):
"""Drives the me2me enable test cases."""
def setUp(self):
"""Set up for me2me enable test."""
pyauto.PyUITest.setUp(self)
self.InstallHostDaemon()
webapp = self.InstallExtension(self.GetWebappPath())
self.host.LaunchApp(webapp)
self.host.Authenticate()
self.host.StartMe2Me()
def tearDown(self):
"""Mainly uninstalls the host daemon."""
self.UninstallHostDaemon()
pyauto.PyUITest.tearDown(self)
def testMe2MeEnableDisable(self):
"""Enables/disables remote connections.
This test also exercises different pin conditions.
"""
self.host.EnableConnectionsInstalled(True)
self.host.DisableConnections()
if __name__ == '__main__':
chromoting_base.Main()
#!/usr/bin/env python
# Copyright (c) 2012 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.
"""Mock pref pane for testing purpose on Mac."""
import Foundation
import os
import signal
import subprocess
import sys
import tempfile
import time
class MockPrefPane(object):
"""Mock Pref Pane to enable/disable/changepin without system prompt.
This only applies to Mac.
"""
def __init__(self):
self._service_name = 'org.chromium.chromoting'
self._real_user_id = os.getuid()
self._config_file = os.path.join(tempfile.gettempdir(),
'%s.json' % self._service_name)
self._tool_script = '/Library/PrivilegedHelperTools/%s.me2me.sh' % \
self._service_name
def _GetJobPid(self):
"""Gets the org.chromium.chromoting job id."""
process = subprocess.Popen(['launchctl', 'list'], stdout=subprocess.PIPE)
pid = None
for line in process.stdout:
# Format is:
# 12345 - my.job (if my.job is running, number is job's PID)
# - 0 my.other.job (if my.other.job is not running)
fields = line.strip().split('\t')
if fields[2] == self._service_name and fields[0] != "-":
pid = fields[0]
break
process.wait()
return pid
def Enable(self):
"""Handles what pref pane does for enabling connection."""
# Elevate privileges, otherwise tool_script executes with EUID != 0.
os.setuid(0)
subprocess.call([self._tool_script, '--enable'],
stdin=open(self._config_file))
# Drop privileges, start the launchd job as the logged-in user.
os.setuid(self._real_user_id)
subprocess.call(['launchctl', 'start', self._service_name])
# Starting a launchd job is an asynchronous operation that typically takes
# a couple of seconds, so poll until the job has started.
for _ in range(1, 10):
if self._GetJobPid():
print '*** org.chromium.chromoting is running ***'
break
time.sleep(2)
def Disable(self):
"""Handles what pref pane does for disabling connection."""
# Elevate privileges, otherwise tool_script executes with EUID != 0.
os.setuid(0)
subprocess.call([self._tool_script, '--disable'],
stdin=open(self._config_file))
# Drop privileges, stop the launchd job as the logged-in user.
os.setuid(self._real_user_id)
subprocess.call(['launchctl', 'stop', self._service_name])
# Stopping a launchd job is an asynchronous operation that typically takes
# a couple of seconds, so poll until the job has stopped.
for _ in range(1, 10):
if not self._GetJobPid():
print '*** org.chromium.chromoting is not running ***'
break
time.sleep(2)
def ChangePin(self):
"""Handles what pref pane does for changing pin."""
# Elevate privileges, otherwise tool_script executes with EUID != 0.
os.setuid(0)
subprocess.call([self._tool_script, '--save-config'],
stdin=open(self._config_file))
# Drop privileges and send SIGHUP to org.chromium.chromoting
os.setuid(self._real_user_id)
os.kill(int(self._GetJobPid()), signal.SIGHUP)
def NotifyWebapp(self):
"""Notifies the web app that pref pane operation is done."""
notif_center = Foundation.NSDistributedNotificationCenter.defaultCenter()
notif_center.postNotificationName_object_userInfo_(
self._service_name + '.update_succeeded', None, None)
def Main():
"""Handles the mock pref pane actions."""
assert sys.platform.startswith('darwin')
print '*** Started mock pref pane ***'
print '*** EUID=%d, UID=%d ***' % (os.geteuid(), os.getuid())
pref_pane = MockPrefPane()
if sys.argv[1] == 'enable':
pref_pane.Enable()
elif sys.argv[1] == 'disable':
pref_pane.Disable()
elif sys.argv[1] == 'changepin':
pref_pane.ChangePin()
else:
print >>sys.stderr, 'Invalid syntax'
return
pref_pane.NotifyWebapp()
if __name__ == '__main__':
Main()
#!/usr/bin/env python
# Copyright (c) 2012 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.
import os
import pyauto_functional # Must come before chromoting and pyauto.
import chromoting
import pyauto
class ChromotingBasic(chromoting.ChromotingMixIn, pyauto.PyUITest):
"""Basic tests for Chromoting."""
_EXTRA_CHROME_FLAGS = [
'--allow-nacl-socket-api=*',
]
def ExtraChromeFlags(self):
"""Ensures Chrome is launched with some custom flags.
Overrides the default list of extra flags passed to Chrome. See
ExtraChromeFlags() in pyauto.py.
"""
return pyauto.PyUITest.ExtraChromeFlags(self) + self._EXTRA_CHROME_FLAGS
def setUp(self):
"""Set up test for Chromoting on both local and remote machines.
Installs the Chromoting app, launches it, and authenticates
using the default Chromoting test account.
"""
super(ChromotingBasic, self).setUp()
self._app = self.InstallExtension(self.GetWebappPath())
self.LaunchApp(self._app)
account = self.GetPrivateInfo()['test_chromoting_account']
self.Authenticate(account['username'], account['password'])
def testChromoting(self):
"""Verify that we can start and disconnect from a Chromoting session."""
client_local = (self.remote == None)
host = self
client = self if client_local else self.remote
client_tab_index = 2 if client_local else 1
access_code = host.Share()
self.assertTrue(access_code,
msg='Host attempted to share, but it failed. '
'No access code was found.')
if client_local:
client.LaunchApp(self._app)
self.assertTrue(client.Connect(access_code, True, client_tab_index),
msg='The client attempted to connect to the host, '
'but the chromoting session did not start.')
host.CancelShare()
client.Disconnect(client_tab_index)
if __name__ == '__main__':
pyauto_functional.Main()
...@@ -2,14 +2,7 @@ ...@@ -2,14 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
"""Includes different methods to drive chromoting UI."""
import os import os
import subprocess
import sys
import time
from pyauto_errors import JSONInterfaceError
class ChromotingMixIn(object): class ChromotingMixIn(object):
...@@ -28,52 +21,30 @@ class ChromotingMixIn(object): ...@@ -28,52 +21,30 @@ class ChromotingMixIn(object):
""" """
def _ExecuteJavascript(self, command, tab_index, windex): def _ExecuteJavascript(self, command, tab_index, windex):
"""Helper that returns immediately after running a Javascript command. """Helper that returns immediately after running a Javascript command."""
""" return self.ExecuteJavascript(
try:
self.ExecuteJavascript(
'%s; window.domAutomationController.send("done");' % command, '%s; window.domAutomationController.send("done");' % command,
tab_index, windex) tab_index, windex)
return True
except JSONInterfaceError:
print '_ExecuteJavascript threw JSONInterfaceError'
return False
def _WaitForJavascriptCondition(self, condition, tab_index, windex): def _WaitForJavascriptCondition(self, condition, tab_index, windex):
"""Waits until the Javascript condition is true. """Waits until the Javascript condition is true.
This is different from a naive self.WaitUntil(lambda: self.GetDOMValue()) This is different from a naive self.WaitUntil(lambda: self.GetDOMValue())
because it uses Javascript to check the condition instead of Python. because it uses Javascript to check the condition instead of Python.
Returns: True if condition is satisfied or otherwise False.
""" """
try:
return self.WaitUntil(lambda: self.GetDOMValue( return self.WaitUntil(lambda: self.GetDOMValue(
'(%s) ? "1" : ""' % condition, tab_index, windex)) '(%s) ? "1" : ""' % condition, tab_index, windex))
except JSONInterfaceError:
print '_WaitForJavascriptCondition threw JSONInterfaceError'
return False
def _ExecuteAndWaitForMode(self, command, mode, tab_index, windex): def _ExecuteAndWaitForMode(self, command, mode, tab_index, windex):
""" Executes JavaScript and wait for remoting app mode equal to self.assertTrue(self._ExecuteJavascript(command, tab_index, windex),
the given mode. 'Javascript command did not return anything.')
Returns: True if condition is satisfied or otherwise False.
"""
if not self._ExecuteJavascript(command, tab_index, windex):
return False
return self._WaitForJavascriptCondition( return self._WaitForJavascriptCondition(
'remoting.currentMode == remoting.AppMode.%s' % mode, 'remoting.currentMode == remoting.AppMode.%s' % mode,
tab_index, windex) tab_index, windex)
def _ExecuteAndWaitForMajorMode(self, command, mode, tab_index, windex): def _ExecuteAndWaitForMajorMode(self, command, mode, tab_index, windex):
""" Executes JavaScript and wait for remoting app major mode equal to self.assertTrue(self._ExecuteJavascript(command, tab_index, windex),
the given mode. 'Javascript command did not return anything.')
Returns: True if condition is satisfied or otherwise False.
"""
if not self._ExecuteJavascript(command, tab_index, windex):
return False
return self._WaitForJavascriptCondition( return self._WaitForJavascriptCondition(
'remoting.getMajorMode() == remoting.AppMode.%s' % mode, 'remoting.getMajorMode() == remoting.AppMode.%s' % mode,
tab_index, windex) tab_index, windex)
...@@ -85,44 +56,24 @@ class ChromotingMixIn(object): ...@@ -85,44 +56,24 @@ class ChromotingMixIn(object):
""" """
return os.path.join(self.BrowserPath(), 'remoting', 'remoting.webapp') return os.path.join(self.BrowserPath(), 'remoting', 'remoting.webapp')
def _GetHelperRunner(self): def Authenticate(self, email=None, password=None, otp=None,
"""Returns the python binary name that runs chromoting_helper.py.""" tab_index=1, windex=0):
if sys.platform.startswith('win'): """Logs a user in for Chromoting and accepts permissions for the app.
return 'python'
else:
return 'suid-python'
def _GetHelper(self):
"""Get chromoting_helper.py."""
return os.path.join('chrome', 'test', 'pyautolib', 'chromoting_helper.py')
def InstallHostDaemon(self):
"""Installs the host daemon."""
subprocess.call([self._GetHelperRunner(), self._GetHelper(),
'install', self.BrowserPath()])
def UninstallHostDaemon(self): PyAuto tests start with a clean profile, so Chromoting tests should call
"""Uninstalls the host daemon.""" this for every run after launching the app. If email or password is omitted,
subprocess.call([self._GetHelperRunner(), self._GetHelper(), the user can type it into the browser window manually.
'uninstall', self.BrowserPath()])
def ContinueAuth(self, tab_index=1, windex=0): Raises:
"""Starts authentication.""" AssertionError if the authentication flow changes or
the credentials are incorrect.
"""
self.assertTrue( self.assertTrue(
self._WaitForJavascriptCondition('window.remoting && remoting.oauth2', self._WaitForJavascriptCondition('window.remoting && remoting.oauth2',
tab_index, windex), tab_index, windex),
msg='Timed out while waiting for remoting app to finish loading.') msg='Timed out while waiting for remoting app to finish loading.')
self._ExecuteJavascript('remoting.oauth2.doAuthRedirect();', self._ExecuteJavascript('remoting.oauth2.doAuthRedirect();',
tab_index, windex) tab_index, windex)
def SignIn(self, email=None, password=None, otp=None,
tab_index=1, windex=0):
"""Logs a user in.
PyAuto tests start with a clean profile, so Chromoting tests should call
this for every run after launching the app. If email or password is
omitted, the user can type it into the browser window manually.
"""
self.assertTrue( self.assertTrue(
self._WaitForJavascriptCondition('document.getElementById("signIn")', self._WaitForJavascriptCondition('document.getElementById("signIn")',
tab_index, windex), tab_index, windex),
...@@ -144,10 +95,10 @@ class ChromotingMixIn(object): ...@@ -144,10 +95,10 @@ class ChromotingMixIn(object):
'document.getElementById("smsVerifyPin")', 'document.getElementById("smsVerifyPin")',
tab_index, windex), tab_index, windex),
msg='Invalid username or password.') msg='Invalid username or password.')
self._ExecuteJavascript( self._ExecuteJavascript('document.getElementById("smsUserPin").value = '
'document.getElementById("smsUserPin").value = "%s";' '"%s";'
'document.getElementById("smsVerifyPin").click();' % otp, 'document.getElementById("smsVerifyPin").click();'
tab_index, windex) % otp, tab_index, windex)
# If the account adder screen appears, then skip it. # If the account adder screen appears, then skip it.
self.assertTrue( self.assertTrue(
...@@ -161,14 +112,12 @@ class ChromotingMixIn(object): ...@@ -161,14 +112,12 @@ class ChromotingMixIn(object):
'{ document.getElementById("skip").click(); }', '{ document.getElementById("skip").click(); }',
tab_index, windex) tab_index, windex)
def AllowAccess(self, tab_index=1, windex=0):
"""Allows access to chromoting webapp."""
# Approve access. # Approve access.
self.assertTrue( self.assertTrue(
self._WaitForJavascriptCondition( self._WaitForJavascriptCondition(
'document.getElementById("submit_approve_access")', 'document.getElementById("submit_approve_access")',
tab_index, windex), tab_index, windex),
msg='Did not go to permission page.') msg='Authentication failed. The username, password, or otp is invalid.')
self._ExecuteJavascript( self._ExecuteJavascript(
'document.getElementById("submit_approve_access").click();', 'document.getElementById("submit_approve_access").click();',
tab_index, windex) tab_index, windex)
...@@ -186,37 +135,6 @@ class ChromotingMixIn(object): ...@@ -186,37 +135,6 @@ class ChromotingMixIn(object):
tab_index, windex), tab_index, windex),
msg='Chromoting app did not reload after authentication.') msg='Chromoting app did not reload after authentication.')
def DenyAccess(self, tab_index=1, windex=0):
"""Deny and then allow access to chromoting webapp."""
self.assertTrue(
self._WaitForJavascriptCondition(
'document.getElementById("submit_deny_access")',
tab_index, windex),
msg='Did not go to permission page.')
self._ExecuteJavascript(
'document.getElementById("submit_deny_access").click();',
tab_index, windex)
def SignOut(self, tab_index=1, windex=0):
"""Signs out from chromoting and signs back in."""
self._ExecuteAndWaitForMode(
'document.getElementById("sign-out").click();',
'UNAUTHENTICATED', tab_index, windex)
def Authenticate(self, tab_index=1, windex=0):
"""Finishes authentication flow for user."""
self.ContinueAuth(tab_index, windex)
account = self.GetPrivateInfo()['test_chromoting_account']
self.host.SignIn(account['username'], account['password'], None,
tab_index, windex)
self.host.AllowAccess(tab_index, windex)
def StartMe2Me(self, tab_index=1, windex=0):
"""Starts Me2Me. """
self._ExecuteJavascript(
'document.getElementById("get-started-me2me").click();',
tab_index, windex)
def Share(self, tab_index=1, windex=0): def Share(self, tab_index=1, windex=0):
"""Generates an access code and waits for incoming connections. """Generates an access code and waits for incoming connections.
...@@ -230,305 +148,40 @@ class ChromotingMixIn(object): ...@@ -230,305 +148,40 @@ class ChromotingMixIn(object):
'document.getElementById("access-code-display").innerText', 'document.getElementById("access-code-display").innerText',
tab_index, windex) tab_index, windex)
def CancelShare(self, tab_index=1, windex=0): def Connect(self, access_code, wait_for_frame, tab_index=1, windex=0):
"""Stops sharing the desktop on the host side.""" """Connects to a Chromoting host and starts the session.
self.assertTrue(
self._ExecuteAndWaitForMode(
'remoting.cancelShare();',
'HOST_SHARE_FINISHED', tab_index, windex),
msg='Stopping sharing from the host side failed')
def EnableConnectionsInstalled(self, pin_exercise=False,
tab_index=1, windex=0):
"""Enables the remote connections on the host side."""
subprocess.call([self._GetHelperRunner(), self._GetHelper(), 'enable'])
self.assertTrue(
self._ExecuteAndWaitForMode(
'document.getElementById("start-daemon").click();',
'HOST_SETUP_ASK_PIN', tab_index, windex),
msg='Cannot start host setup')
self.assertTrue(
self._WaitForJavascriptCondition(
'document.getElementById("ask-pin-form").hidden == false',
tab_index, windex),
msg='No ask pin dialog')
if pin_exercise:
# Cancels the pin prompt
self._ExecuteJavascript(
'document.getElementById("daemon-pin-cancel").click();',
tab_index, windex)
# Enables again
self.assertTrue(
self._ExecuteAndWaitForMode(
'document.getElementById("start-daemon").click();',
'HOST_SETUP_ASK_PIN', tab_index, windex),
msg='Cannot start host setup')
# Click ok without typing in pins
self._ExecuteJavascript(
'document.getElementById("daemon-pin-ok").click();',
tab_index, windex)
self.assertTrue(
self._WaitForJavascriptCondition(
'document.getElementById("daemon-pin-error-message")',
tab_index, windex),
msg='No pin error message')
# Mis-matching pins
self._ExecuteJavascript(
'document.getElementById("daemon-pin-entry").value = "111111";',
tab_index, windex)
self._ExecuteJavascript(
'document.getElementById("daemon-pin-confirm").value = "123456";',
tab_index, windex)
self.assertTrue(
self._WaitForJavascriptCondition(
'document.getElementById("daemon-pin-error-message")',
tab_index, windex),
msg='No pin error message')
# Types in correct pins
self._ExecuteJavascript(
'document.getElementById("daemon-pin-entry").value = "111111";',
tab_index, windex)
self._ExecuteJavascript(
'document.getElementById("daemon-pin-confirm").value = "111111";',
tab_index, windex)
self.assertTrue(
self._ExecuteAndWaitForMode(
'document.getElementById("daemon-pin-ok").click();',
'HOST_SETUP_PROCESSING', tab_index, windex),
msg='Host setup was not started')
# Handles preference panes
self.assertTrue(
self._WaitForJavascriptCondition(
'remoting.currentMode == remoting.AppMode.HOST_SETUP_DONE',
tab_index, windex),
msg='Host setup was not done')
# Dismisses the host config done dialog Returns:
self.assertTrue( True on success; False otherwise.
self._WaitForJavascriptCondition( """
'document.getElementById("host-setup-dialog")' if not self._ExecuteAndWaitForMode(
'.childNodes[5].hidden == false',
tab_index, windex),
msg='No host setup done dialog')
self.assertTrue(
self._ExecuteAndWaitForMode(
'document.getElementById("host-config-done-dismiss").click();',
'HOME', tab_index, windex),
msg='Failed to dismiss host setup confirmation dialog')
def EnableConnectionsUninstalledAndCancel(self, tab_index=1, windex=0):
"""Enables remote connections while host is not installed yet."""
self.assertTrue(
self._ExecuteAndWaitForMode(
'document.getElementById("start-daemon").click();',
'HOST_SETUP_INSTALL', tab_index, windex),
msg='Cannot start host install')
self.assertTrue(
self._ExecuteAndWaitForMode(
'document.getElementById("host-config-install-dismiss").click();',
'HOME', tab_index, windex),
msg='Failed to dismiss host install dialog')
def DisableConnections(self, tab_index=1, windex=0):
"""Disables the remote connections on the host side."""
subprocess.call([self._GetHelperRunner(), self._GetHelper(), 'disable'])
self._ExecuteJavascript(
'document.getElementById("stop-daemon").click();',
tab_index, windex)
self.assertTrue(
self._ExecuteAndWaitForMode(
'document.getElementById("host-config-done-dismiss").click();',
'HOME', tab_index, windex),
msg='Failed to dismiss host setup confirmation dialog')
def Connect(self, access_code, tab_index=1, windex=0):
"""Connects to a Chromoting host and starts the session."""
self.assertTrue(
self._ExecuteAndWaitForMode(
'document.getElementById("access-code-entry").value = "%s";' 'document.getElementById("access-code-entry").value = "%s";'
'remoting.connectIt2Me();' % access_code, 'remoting.connectIt2Me();' % access_code,
'IN_SESSION', tab_index, windex), 'IN_SESSION', tab_index, windex):
msg='Cannot connect it2me session') return False
def ChangePin(self, pin='222222', tab_index=1, windex=0):
"""Changes pin for enabled host."""
subprocess.call([self._GetHelperRunner(), self._GetHelper(), 'changepin'])
self.assertTrue(
self._ExecuteAndWaitForMode(
'document.getElementById("change-daemon-pin").click();',
'HOST_SETUP_ASK_PIN', tab_index, windex),
msg='Cannot change daemon pin')
self.assertTrue(
self._WaitForJavascriptCondition(
'document.getElementById("ask-pin-form").hidden == false',
tab_index, windex),
msg='No ask pin dialog')
self._ExecuteJavascript(
'document.getElementById("daemon-pin-entry").value = "' + pin + '";',
tab_index, windex)
self._ExecuteJavascript(
'document.getElementById("daemon-pin-confirm").value = "' +
pin + '";', tab_index, windex)
self.assertTrue(
self._ExecuteAndWaitForMode(
'document.getElementById("daemon-pin-ok").click();',
'HOST_SETUP_PROCESSING', tab_index, windex),
msg='Host setup was not started')
# Handles preference panes
self.assertTrue(
self._WaitForJavascriptCondition(
'remoting.currentMode == remoting.AppMode.HOST_SETUP_DONE',
tab_index, windex),
msg='Host setup was not done')
# Dismisses the host config done dialog
self.assertTrue(
self._WaitForJavascriptCondition(
'document.getElementById("host-setup-dialog")'
'.childNodes[5].hidden == false',
tab_index, windex),
msg='No host setup done dialog')
self.assertTrue(
self._ExecuteAndWaitForMode(
'document.getElementById("host-config-done-dismiss").click();',
'HOME', tab_index, windex),
msg='Failed to dismiss host setup confirmation dialog')
def ChangeName(self, new_name='Changed', tab_index=1, windex=0):
"""Changes the host name."""
self._ExecuteJavascript(
'document.getElementById("this-host-rename").click();',
tab_index, windex)
self._ExecuteJavascript(
'document.getElementById("this-host-name").childNodes[0].value = "' +
new_name + '";', tab_index, windex)
self._ExecuteJavascript(
'document.getElementById("this-host-rename").click();',
tab_index, windex)
def ConnectMe2Me(self, pin='111111', mode='IN_SESSION',
tab_index=1, windex=0):
"""Connects to a Chromoting host and starts the session."""
# There is delay from the enabling remote connections to the host
# showing up in the host list. We need to reload the web app to get
# the host to show up. We will repeat this a few times to make sure
# eventually host appears.
for _ in range(1, 3):
self._ExecuteJavascript(
'window.location.reload();',
tab_index, windex)
# pyauto _GetResultFromJSONRequest throws JSONInterfaceError after
# ~60 seconds if ExecuteJavascript is called right after reload.
# Waiting 2s here can avoid this. So instead of getting the error and
# wait ~60s, we wait 2s here. If the error still happens, the following
# retry will handle that.
time.sleep(2)
# If this-host-connect is still not enabled, let's retry 3 times here.
this_host_connect_enabled = False
for _ in range(1, 3):
this_host_connect_enabled = self._WaitForJavascriptCondition(
'document.getElementById("this-host-connect")'
'.getAttribute("data-daemon-state") == "enabled"',
tab_index, windex)
if this_host_connect_enabled:
break
if this_host_connect_enabled:
break;
# Clicking this-host-connect does work right after this-host-connect
# is enabled. Need to retry.
for _ in range(1, 3):
self._ExecuteJavascript(
'document.getElementById("this-host-connect").click();',
tab_index, windex)
# pyauto _GetResultFromJSONRequest throws JSONInterfaceError after if wait_for_frame and not self._WaitForJavascriptCondition(
# a long time out if WaitUntil is called right after click. 'remoting.clientSession && remoting.clientSession.hasReceivedFrame()',
# Waiting 2s here can avoid this. tab_index, windex):
time.sleep(2) return False
return True
# If cannot detect that pin-form appears, try 3 times. def CancelShare(self, tab_index=1, windex=0):
pin_form_exposed = False """Stops sharing the desktop on the host side.
for _ in range(1, 3):
pin_form_exposed = self._WaitForJavascriptCondition(
'document.getElementById("client-dialog")'
'.childNodes[9].hidden == false',
tab_index, windex)
if pin_form_exposed:
break
if pin_form_exposed:
break
self._ExecuteJavascript( Returns:
'document.getElementById("pin-entry").value = "' + pin + '";', True on success; False otherwise.
tab_index, windex) """
self.assertTrue( return self._ExecuteAndWaitForMode(
self._ExecuteAndWaitForMode( 'remoting.cancelShare();',
'document.getElementById("pin-form").childNodes[5].click();', 'HOST_SHARE_FINISHED', tab_index, windex)
mode, tab_index, windex),
msg='Session was not started')
def Disconnect(self, tab_index=1, windex=0): def Disconnect(self, tab_index=1, windex=0):
"""Disconnects from the Chromoting it2me session on the client side.""" """Disconnects from the Chromoting session on the client side.
self.assertTrue(
self._ExecuteAndWaitForMode(
'remoting.disconnect();',
'CLIENT_SESSION_FINISHED_IT2ME', tab_index, windex),
msg='Disconnecting it2me session from the client side failed')
def DisconnectMe2Me(self, confirmation=True, tab_index=1, windex=0): Returns:
"""Disconnects from the Chromoting me2me session on the client side.""" True on success; False otherwise.
self.assertTrue( """
self._ExecuteAndWaitForMode( return self._ExecuteAndWaitForMode(
'remoting.disconnect();', 'remoting.disconnect();',
'CLIENT_SESSION_FINISHED_ME2ME', tab_index, windex), 'CLIENT_SESSION_FINISHED_IT2ME', tab_index, windex)
msg='Disconnecting me2me session from the client side failed')
if confirmation:
self.assertTrue(
self._ExecuteAndWaitForMode(
'document.getElementById("client-finished-me2me-button")'
'.click();', 'HOME', tab_index, windex),
msg='Failed to dismiss session finished dialog')
def ReconnectMe2Me(self, pin='111111', tab_index=1, windex=0):
"""Reconnects the me2me session."""
self._ExecuteJavascript(
'document.getElementById("client-reconnect-button").click();',
tab_index, windex)
# pyauto _GetResultFromJSONRequest throws JSONInterfaceError after
# a long time out if WaitUntil is called right after click.
time.sleep(2)
# If cannot detect that pin-form appears, try 3 times.
for _ in range(1, 3):
pin_form_exposed = self._WaitForJavascriptCondition(
'document.getElementById("client-dialog")'
'.childNodes[9].hidden == false',
tab_index, windex)
if pin_form_exposed:
break
self._ExecuteJavascript(
'document.getElementById("pin-entry").value = "' + pin + '";',
tab_index, windex)
self.assertTrue(
self._ExecuteAndWaitForMode(
'document.getElementById("pin-form").childNodes[5].click();',
'IN_SESSION', tab_index, windex),
msg='Session was not started')
#!/usr/bin/env python
# Copyright (c) 2012 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.
"""Chromoting helper to install/uninstall host and replace pref pane."""
import abc
import os
import shutil
import sys
import subprocess
class ChromotingHelper(object):
"""Chromoting helper base class."""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def InstallHost(self, bin_dir):
"""Installs the chromoting host"""
return
@abc.abstractmethod
def UninstallHost(self, bin_dir):
"""Uninstalls the chromoting host"""
return
class ChromotingHelperMac(ChromotingHelper):
"""Chromoting Helper class for Mac.
Installs/uninstalls host and replace the pref pane for testing purpose.
"""
def InstallHost(self, bin_dir):
"""Installs host on Mac."""
assert os.geteuid() == 0, 'Need superuser privileges'
# Run most of the steps here with login user
login_uid = os.getuid()
os.seteuid(login_uid)
# Change the working dir to the dir that has the host zip file
current_dir = os.getcwd()
os.chdir(bin_dir)
host_dir = 'remoting-me2me-host-mac'
output_dir = os.path.join(host_dir, 'output')
# Remove remoting-me2me-host-mac dir just in case
shutil.rmtree(host_dir, True)
# Unzip the host archive and prepare the files/dirs
subprocess.call('unzip remoting-me2me-host-mac.zip', shell=True)
subprocess.call('mkdir ' + output_dir, shell=True)
# Prepare security identity for code signing purpose
os.seteuid(0)
key_chain = '/Library/Keychains/ChromotingTest'
password = '1111'
key = os.path.join(current_dir, 'chrome', 'test',
'pyautolib', 'chromoting_key.p12')
cert = os.path.join(current_dir, 'chrome', 'test',
'pyautolib', 'chromoting_cert.p12')
subprocess.call(['security', 'delete-keychain', key_chain])
subprocess.call(['security', 'create-keychain', '-p',
password, key_chain])
subprocess.call(['security', 'import', key,
'-k', key_chain, '-P', password, '-A'])
subprocess.call(['security', 'import', cert,
'-k', key_chain, '-P', password])
os.seteuid(login_uid)
# Sign the host
do_signing = os.path.join(host_dir, 'do_signing.sh')
subprocess.call(do_signing + ' ' + output_dir + ' ' + host_dir + ' ' +
key_chain + ' "Chromoting Test"', shell=True)
# Remove security identify
os.seteuid(0)
subprocess.call(['security', 'delete-keychain', key_chain])
os.seteuid(login_uid)
# Figure out the dmg name
version = ""
for output_file in os.listdir(output_dir):
if output_file.endswith('.dmg'):
version = os.path.basename(output_file)[len('ChromotingHost-'):-4]
# Mount before installation
dmg = os.path.join(output_dir, 'ChromotingHost-' + version + '.dmg')
subprocess.call('hdiutil' + ' mount ' + dmg, shell=True)
# Install host
os.seteuid(0)
mpkg = os.path.join('/Volumes', 'Chromoting Host ' + version,
'Chromoting Host.mpkg')
subprocess.call(['/usr/sbin/installer', '-pkg',
mpkg, '-target', '/'])
os.seteuid(login_uid)
# Unmount after installation
mounted = os.path.join('/Volumes', 'Chromoting Host ' + version)
subprocess.call('hdiutil unmount "' + mounted + '"', shell=True)
# Clean up remoting-me2me-host-mac dir
shutil.rmtree(host_dir, True)
# Resume the original working dir
os.chdir(current_dir)
def UninstallHost(self, bin_dir):
"""Uninstalls host on Mac."""
assert os.geteuid() == 0, 'Need superuser privileges'
uninstall_app = os.path.join('/', 'Applications',
'Chromoting Host Uninstaller.app')
subprocess.call(['open', '-a', uninstall_app])
def ReplacePrefPaneMac(self, operation):
"""Constructs mock pref pane to replace the actual pref pane on Mac."""
assert os.geteuid() == 0, 'Need superuser privileges'
pref_pane_dir = os.path.join('/Library', 'PreferencePanes')
mock_pref_pane = os.path.join(pref_pane_dir, 'mock_pref_pane')
pref_pane = os.path.join(pref_pane_dir, 'org.chromium.chromoting.prefPane')
mock_pref_pane_python = os.path.join(os.getcwd(), 'chrome', 'test',
'functional', 'chromoting',
'mock_pref_pane.py')
shutil.rmtree(mock_pref_pane, True)
mock_pref_pane_file = open(mock_pref_pane, 'w')
mock_pref_pane_file.write('#!/bin/bash\n')
mock_pref_pane_file.write('\n')
mock_pref_pane_file.write('suid-python' +
' ' + mock_pref_pane_python + ' ' + operation)
mock_pref_pane_file.close()
subprocess.call(['chmod', 'a+x', mock_pref_pane])
shutil.rmtree(pref_pane, True)
subprocess.call(['ln', '-s', mock_pref_pane, pref_pane])
class ChromotingHelperWindows(ChromotingHelper):
"""Chromoting Helper class for Windows for installing/uninstalling host."""
def InstallHost(self, bin_dir):
"""Installs host on Windows."""
host_msi = os.path.join(bin_dir, 'remoting-host.msi')
subprocess.Popen(['msiexec', '/i', host_msi, '/passive']).wait()
def UninstallHost(self, bin_dir):
"""Uninstalls host on Windows."""
host_msi = os.path.join(bin_dir, 'remoting-host.msi')
subprocess.Popen(['msiexec', '/x', host_msi, '/passive']).wait()
def Main():
"""Main function to dispatch operations."""
assert sys.platform.startswith('win') or \
sys.platform.startswith('darwin'), \
'Only support Windows and Mac'
if sys.platform.startswith('win'):
helper = ChromotingHelperWindows()
elif sys.platform.startswith('darwin'):
helper = ChromotingHelperMac()
if sys.argv[1] == 'install':
helper.InstallHost(sys.argv[2])
elif sys.argv[1] == 'uninstall':
helper.UninstallHost(sys.argv[2])
elif sys.argv[1] in ['enable', 'disable', 'changepin']:
assert sys.platform.startswith('darwin'), \
'Replacing pref pane is Mac specific'
helper.ReplacePrefPaneMac(sys.argv[1])
else:
print >>sys.stderr, 'Invalid syntax'
return 1
if __name__ == '__main__':
Main()
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment