12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# TODO(nduca): Rewrite what some of these tests to use mocks instead of
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# actually talking to the device. This would improve our coverage quite
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# a bit.
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import unittest
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import socket
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import sys
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from telemetry.core import util
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from telemetry.core.chrome import cros_browser_backend
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from telemetry.core.chrome import cros_interface
15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)from telemetry.unittest import options_for_unittests
16868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)from telemetry.unittest import RequiresBrowserOfType
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class CrOSInterfaceTest(unittest.TestCase):
19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  @RequiresBrowserOfType('cros-chrome')
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def testPushContents(self):
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    remote = options_for_unittests.GetCopy().cros_remote
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cri = cros_interface.CrOSInterface(
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      remote,
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      options_for_unittests.GetCopy().cros_ssh_identity)
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cri.RunCmdOnDevice(['rm', '-rf', '/tmp/testPushContents'])
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cri.PushContents('hello world', '/tmp/testPushContents')
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    contents = cri.GetFileContents('/tmp/testPushContents')
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertEquals(contents, 'hello world')
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
30868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  @RequiresBrowserOfType('cros-chrome')
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def testExists(self):
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    remote = options_for_unittests.GetCopy().cros_remote
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cri = cros_interface.CrOSInterface(
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      remote,
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      options_for_unittests.GetCopy().cros_ssh_identity)
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertTrue(cri.FileExistsOnDevice('/proc/cpuinfo'))
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertTrue(cri.FileExistsOnDevice('/etc/passwd'))
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertFalse(cri.FileExistsOnDevice('/etc/sdlfsdjflskfjsflj'))
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def testExistsLocal(self):
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not sys.platform.startswith('linux'):
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cri = cros_interface.CrOSInterface()
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertTrue(cri.FileExistsOnDevice('/proc/cpuinfo'))
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertTrue(cri.FileExistsOnDevice('/etc/passwd'))
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertFalse(cri.FileExistsOnDevice('/etc/sdlfsdjflskfjsflj'))
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
49868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  @RequiresBrowserOfType('cros-chrome')
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def testGetFileContents(self): # pylint: disable=R0201
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    remote = options_for_unittests.GetCopy().cros_remote
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cri = cros_interface.CrOSInterface(
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      remote,
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      options_for_unittests.GetCopy().cros_ssh_identity)
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    hosts = cri.GetFileContents('/etc/hosts')
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    assert hosts.startswith('# /etc/hosts')
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
58868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  @RequiresBrowserOfType('cros-chrome')
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def testGetFileContentsForSomethingThatDoesntExist(self):
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    remote = options_for_unittests.GetCopy().cros_remote
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cri = cros_interface.CrOSInterface(
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      remote,
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      options_for_unittests.GetCopy().cros_ssh_identity)
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertRaises(
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      OSError,
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      lambda: cri.GetFileContents('/tmp/209fuslfskjf/dfsfsf'))
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
68868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  @RequiresBrowserOfType('cros-chrome')
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def testIsServiceRunning(self):
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    remote = options_for_unittests.GetCopy().cros_remote
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cri = cros_interface.CrOSInterface(
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      remote,
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      options_for_unittests.GetCopy().cros_ssh_identity)
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertTrue(cri.IsServiceRunning('openssh-server'))
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def testIsServiceRunningLocal(self):
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not sys.platform.startswith('linux'):
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cri = cros_interface.CrOSInterface()
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertTrue(cri.IsServiceRunning('dbus'))
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
83868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  @RequiresBrowserOfType('cros-chrome')
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def testGetRemotePortAndIsHTTPServerRunningOnPort(self):
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    remote = options_for_unittests.GetCopy().cros_remote
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cri = cros_interface.CrOSInterface(
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      remote,
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      options_for_unittests.GetCopy().cros_ssh_identity)
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Create local server.
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sock = socket.socket()
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sock.bind(('', 0))
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    port = sock.getsockname()[1]
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sock.listen(0)
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Get remote port and ensure that it was unused.
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    remote_port = cri.GetRemotePort()
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertFalse(cri.IsHTTPServerRunningOnPort(remote_port))
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Forward local server's port to remote device's remote_port.
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    forwarder = cros_browser_backend.SSHForwarder(
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        cri, 'R', util.PortPair(port, remote_port))
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # At this point, remote device should be able to connect to local server.
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertTrue(cri.IsHTTPServerRunningOnPort(remote_port))
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Next remote port shouldn't be the same as remote_port, since remote_port
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # is now in use.
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertTrue(cri.GetRemotePort() != remote_port)
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Close forwarder and local server ports.
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    forwarder.Close()
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sock.close()
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Device should no longer be able to connect to remote_port since it is no
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # longer in use.
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertFalse(cri.IsHTTPServerRunningOnPort(remote_port))
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
119868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  @RequiresBrowserOfType('cros-chrome')
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def testGetRemotePortReservedPorts(self):
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    remote = options_for_unittests.GetCopy().cros_remote
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    cri = cros_interface.CrOSInterface(
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      remote,
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      options_for_unittests.GetCopy().cros_ssh_identity)
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Should return 2 separate ports even though the first one isn't technically
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # being used yet.
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    remote_port_1 = cri.GetRemotePort()
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    remote_port_2 = cri.GetRemotePort()
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.assertTrue(remote_port_1 != remote_port_2)
13290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
13390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  # TODO(tengs): It would be best if we can filter this test and other tests
134868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  # that need to be run locally based on the platform of the system browser.
13590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  def testEscapeCmdArguments(self):
13690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    ''' Commands and their arguments that are executed through the cros
13790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    interface should follow bash syntax. This test needs to run on remotely
13890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    and locally on the device to check for consistency.
13990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    '''
14090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if not sys.platform.startswith('linux'):
14190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      return
14290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
14390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    cri = cros_interface.CrOSInterface(
14490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      options_for_unittests.GetCopy().cros_remote,
14590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      options_for_unittests.GetCopy().cros_ssh_identity)
14690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
14790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    # Check arguments with no special characters
14890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    stdout, _ = cri.RunCmdOnDevice(['echo', '--arg1=value1', '--arg2=value2',
14990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        '--arg3="value3"'])
15090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    assert(stdout.strip() == '--arg1=value1 --arg2=value2 --arg3=value3')
15190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
15290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    # Check argument with special characters escaped
15390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    stdout, _ = cri.RunCmdOnDevice(['echo', '--arg=A\\; echo \\"B\\"'])
15490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    assert(stdout.strip() == '--arg=A; echo "B"')
15590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
15690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    # Check argument with special characters in quotes
15790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    stdout, _ = cri.RunCmdOnDevice(['echo', "--arg='$HOME;;$PATH'"])
15890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    assert(stdout.strip() == "--arg=$HOME;;$PATH")
159