diff options
author | dtu@chromium.org <dtu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-06 22:07:15 +0000 |
---|---|---|
committer | dtu@chromium.org <dtu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-06 22:07:15 +0000 |
commit | 2831638398f9ab2b991009c1e380395758c98e60 (patch) | |
tree | 327d974ef60426b8182c52e3d434b5e34b89bf4a /chrome/test | |
parent | 9cb31511cb8ba57c36157c5a509ffd3fe4568437 (diff) | |
download | chromium_src-2831638398f9ab2b991009c1e380395758c98e60.zip chromium_src-2831638398f9ab2b991009c1e380395758c98e60.tar.gz chromium_src-2831638398f9ab2b991009c1e380395758c98e60.tar.bz2 |
Initial inclusion of power_strip code.
Including WifiRouterStrip code that is used for power_strips that exclusively have Wifi routers attached to them.
Added first connect test case using the power strip.
BUG=
TEST=run chromeos_wifi.py
Review URL: http://codereview.chromium.org/6764022
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80704 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/test')
-rw-r--r-- | chrome/test/functional/chromeos_wifi.py | 10 | ||||
-rw-r--r-- | chrome/test/functional/chromeos_wifi_compliance.py | 53 | ||||
-rw-r--r-- | chrome/test/pyautolib/chromeos/__init__.py | 0 | ||||
-rw-r--r-- | chrome/test/pyautolib/chromeos/power_strip.py | 78 | ||||
-rw-r--r-- | chrome/test/pyautolib/chromeos_network.py | 196 | ||||
-rw-r--r-- | chrome/test/pyautolib/pyauto.py | 22 |
6 files changed, 342 insertions, 17 deletions
diff --git a/chrome/test/functional/chromeos_wifi.py b/chrome/test/functional/chromeos_wifi.py index 0019003..131f4a6 100644 --- a/chrome/test/functional/chromeos_wifi.py +++ b/chrome/test/functional/chromeos_wifi.py @@ -12,22 +12,18 @@ import chromeos_network # pyauto_functional must come before chromeos_network class ChromeosWifi(chromeos_network.PyNetworkUITest): """Tests for ChromeOS wifi.""" - def testNetworkInfo(self): + def testNetworkInfoAndScan(self): """Get basic info on networks.""" - result = self.GetNetworkInfo() + # NetworkScan will also call GetNetworkInfo and return the results. + result = self.NetworkScan() self.assertTrue(result) logging.debug(result) - def testNetworkScan(self): - """Basic check to ensure that a network scan doesn't throw errors.""" - self.NetworkScan() - def testGetProxySettings(self): """Print some information about proxy settings.""" result = self.GetProxySettingsOnChromeOS() self.assertTrue(result) logging.debug(result) - if __name__ == '__main__': pyauto_functional.Main() diff --git a/chrome/test/functional/chromeos_wifi_compliance.py b/chrome/test/functional/chromeos_wifi_compliance.py new file mode 100644 index 0000000..b66be87 --- /dev/null +++ b/chrome/test/functional/chromeos_wifi_compliance.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +# Copyright (c) 2011 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 pyauto_functional +import chromeos_network # pyauto_functional must come before chromeos_network + + +class ChromeosWifiCompliance(chromeos_network.PyNetworkUITest): + """Tests for ChromeOS wifi complaince. + + These tests should be run within vacinity of the power strip where the wifi + routers are attached. + """ + + def _BasicConnectRouterCompliance(self, router_name): + """Generic basic test routine for connecting to a router. + + Args: + router_name: The name of the router. + """ + self.InitWifiPowerStrip() + router = self.GetRouterConfig(router_name) + self.RouterPower(router_name, True) + + self.assertTrue(self.WaitUntilWifiNetworkAvailable(router['ssid']), + 'Wifi network %s never showed up.' % router['ssid']) + + # Verify connect did not have any errors. + error = self.ConnectToWifiRouter(router_name) + self.assertFalse(error, 'Failed to connect to wifi network %s. ' + 'Reason: %s.' % (router['ssid'], error)) + + # Verify the network we connected to. + ssid = self.GetConnectedWifi() + self.assertEqual(ssid, router['ssid'], + 'Did not successfully connect to wifi network %s.' % ssid) + + self.DisconnectFromWifiNetwork() + self.RouterPower(router_name, False) + + def testConnectBelkinG(self): + """Test connecting to the Belkin G router.""" + self._BasicConnectRouterCompliance('Belkin_G') + + def testConnectLinksysWRT54G2(self): + """Test connecting to the Linksys WRT54G2 router.""" + self._BasicConnectRouterCompliance('Linksys_WRT54G2') + + +if __name__ == '__main__': + pyauto_functional.Main() diff --git a/chrome/test/pyautolib/chromeos/__init__.py b/chrome/test/pyautolib/chromeos/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/test/pyautolib/chromeos/__init__.py diff --git a/chrome/test/pyautolib/chromeos/power_strip.py b/chrome/test/pyautolib/chromeos/power_strip.py new file mode 100644 index 0000000..50d7b19 --- /dev/null +++ b/chrome/test/pyautolib/chromeos/power_strip.py @@ -0,0 +1,78 @@ +# Copyright (c) 2011 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 telnetlib + + +class PowerStrip(object): + """Controls Server Technology CW-16V1-C20M switched CDUs. + (Cabinet Power Distribution Unit) + + This class is used to control the CW-16V1-C20M unit which + is a 16 port remote power strip. The strip supports AC devices + using 100-120V 50/60Hz input voltages. The commands in this + class are supported by switches that use Sentry Switched CDU Version 6.0g. + + Opens a new connection for every command. + """ + + def __init__(self, host, user='admn', password='admn'): + self._host = host + self._user = user + self._password = password + + def PowerOff(self, outlet): + """Powers off the device that is plugged into the specified outlet. + + Args: + outlet: The outlet ID defined on the switch (eg. .a14). + """ + self._DoCommand('off', outlet) + + def PowerOn(self, outlet): + """Powers on the device that is plugged into the specified outlet. + + Args: + outlet: The outlet ID defined on the switch (eg. .a14). + """ + self._DoCommand('on', outlet) + + def _DoCommand(self, command, outlet): + """Performs power strip commands on the specified outlet. + + Sample telnet interaction: + Escape character is '^]'. + + Sentry Switched CDU Version 6.0g + + Username: admn + Password: < password hidden from view > + + Location: + + Switched CDU: on .a1 + + Outlet Outlet Outlet Control + ID Name Status State + + .A1 TowerA_Outlet1 On On + + Command successful + + Switched CDU: < cdu cmd > + + Args: + command: A valid CW-16V1-C20M command that follows the format + <command> <outlet>. + outlet: The outlet ID defined on the switch (eg. .a14). + """ + tn = telnetlib.Telnet(self._host) + tn.read_until('Username: ') + tn.write(self._user + '\n') + tn.read_until('Password: ') + tn.write(self._password + '\n') + tn.read_until('Switched CDU: ') + tn.write('%s %s\n' % (command, outlet)) + tn.read_some() + tn.close() diff --git a/chrome/test/pyautolib/chromeos_network.py b/chrome/test/pyautolib/chromeos_network.py index aaeed55..9146be0 100644 --- a/chrome/test/pyautolib/chromeos_network.py +++ b/chrome/test/pyautolib/chromeos_network.py @@ -4,10 +4,104 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import copy import dbus +import logging +import os +import time + +from chromeos.power_strip import PowerStrip import pyauto +class WifiPowerStrip(PowerStrip): + """Manages the power state of wifi routers connected to a power strip. + + This class provides additional functionality over PowerStrip by providing + a timeout feature for wifi routers connected to the strip. This is to prevent + repeated on/off calls to the same router which may put the router in an + undesired state. + """ + + def __init__ (self, host, routers): + """Initializes a WifiPowerStrip object. + + Args: + host: IP of the switch that the routers are attached to. + routers: Dictionary of wifi routers in the following format: + { + '< router name >': { + 'strip_id' : '.aX' # where X is the port number + < additional fields may be added here for each router > + } + } + """ + self._router_dict = routers + + # Each router will have a timestamp associated to it regarding whether + # or not an action can be performed on it yet. This is to prevent + # the spamming of power on/off calls on a particular router. + # The WifiPowerStrip_UsableTime field specifies the earliest time + # after which the router may be used. We will initialize it to now + # since they should all be usable at init. + for router_info in self._router_dict.values(): + router_info['WifiPowerStrip_UsableTime'] = time.time() + + PowerStrip.__init__(self, host) + + def GetRouterConfig(self, router_name): + """Returns the configuration for the specified router. + + Args: + router_name: A string specifying the router. + + Returns: + The config dictionary for the given router if the router is defined. + None otherwise. + """ + return copy.deepcopy(self._router_dict.get(router_name)) + + def RouterPower(self, router_name, power_state, pause_after=5): + """Executes PowerStrip commands. + + Args: + router_name: The name of the router to perform the action on. + power_state: A boolean value where True represents turning the router on + and False represents turning the router off. + pause_after: Specified in seconds, and specifies the time to sleep + after a command is run. This is to prevent spamming of + power on/off of the same router which has put the router + in an undesirable state. + + Raises: + Exception if router_name is not a valid router. + """ + router = self.GetRouterConfig(router_name) + if not router: raise Exception('Invalid router name \'%s\'.' % router_name) + + sleep_time = router['WifiPowerStrip_UsableTime'] - time.time() + if sleep_time > 0: + sleep(sleep_time) + + if power_state: + logging.debug('Turning on router %s:%s.' % + (router['strip_id'], router_name)) + self.PowerOn(router['strip_id']) + else: + logging.debug('Turning off router %s:%s.' % + (router['strip_id'], router_name)) + self.PowerOff(router['strip_id']) + + # Set the Usable time of the particular router to pause_after + # seconds after the current time. + router['WifiPowerStrip_UsableTime'] = time.time() + pause_after + + def TurnOffAllRouters(self): + """Turns off all the routers.""" + for router in self._router_dict: + self.RouterPower(router, False, pause_after=0) + + class PyNetworkUITest(pyauto.PyUITest): """A subclass of PyUITest for Chrome OS network tests. @@ -15,6 +109,9 @@ class PyNetworkUITest(pyauto.PyUITest): priorities to put wifi connections first before starting tests. This is for convenience when writing wifi tests. """ + _ROUTER_CONFIG_FILE = os.path.join(pyauto.PyUITest.DataDir(), + 'pyauto_private', 'chromeos', 'network', + 'wifi_testbed_config') _FLIMFLAM_PATH = 'org.chromium.flimflam' _proxy = dbus.SystemBus().get_object(_FLIMFLAM_PATH, '/') @@ -26,11 +123,17 @@ class PyNetworkUITest(pyauto.PyUITest): # test harness might be using and putting wifi ahead. self._PushServiceOrder('vpn,bluetooth,wifi,wimax,cellular,ethernet') pyauto.PyUITest.setUp(self) + self._wifi_power_strip = None def tearDown(self): pyauto.PyUITest.tearDown(self) self._PopServiceOrder() + # If the test case used the WifiPowerStrip, make sure + # everything is clean by turning all the routers off. + if self._wifi_power_strip: + self._wifi_power_strip.TurnOffAllRouters() + def _SetServiceOrder(self, service_order): self._manager.SetServiceOrder(service_order) self._manager.DisableTechnology('wifi') @@ -40,9 +143,98 @@ class PyNetworkUITest(pyauto.PyUITest): self._old_service_order = self._manager.GetServiceOrder() self._SetServiceOrder(service_order) assert service_order == self._manager.GetServiceOrder(), \ - 'Flimflam service order not set properly.' + 'Flimflam service order not set properly. %s != %s' % \ + (self._old_service_order, self._manager.GetServiceOrder()) def _PopServiceOrder(self): self._SetServiceOrder(self._old_service_order) assert self._old_service_order == self._manager.GetServiceOrder(), \ - 'Flimflam service order not reset properly.' + 'Flimflam service order not reset properly. %s != %s' % \ + (self._old_service_order, self._manager.GetServiceOrder()) + + def InitWifiPowerStrip(self): + """Initializes the router controller using the specified config file.""" + + assert os.path.exists(PyNetworkUITest._ROUTER_CONFIG_FILE), \ + 'Router configuration file does not exist.' + + config = pyauto.PyUITest.EvalDataFrom(self._ROUTER_CONFIG_FILE) + strip_ip, routers = config['strip_ip'], config['routers'] + + self._wifi_power_strip = WifiPowerStrip(strip_ip, routers) + + self.RouterPower = self._wifi_power_strip.RouterPower + self.TurnOffAllRouters = self._wifi_power_strip.TurnOffAllRouters + self.GetRouterConfig = self._wifi_power_strip.GetRouterConfig + + def WaitUntilWifiNetworkAvailable(self, ssid, timeout=60): + """Waits until the given network is available. + + Routers that are just turned on may take up to 1 minute upon turning them + on to broadcast their SSID. + + Args: + ssid: SSID of the service we want to connect to. + timeout: timeout (in seconds) + + Raises: + Exception if timeout duration has been hit before wifi router is seen. + + Returns: + True, when the wifi network is seen within the timout period. + False, otherwise. + """ + def _GotWifiNetwork(): + # Returns non-empty array if desired SSID is available. + return [wifi for wifi in + self.NetworkScan().get('wifi_networks', {}).values() + if wifi.get('name') == ssid] + + return self.WaitUntil(_GotWifiNetwork, timeout=timeout, retry_sleep=1) + + def GetConnectedWifi(self): + """Returns the SSID of the currently connected wifi network. + + Returns: + The SSID of the connected network or None if we're not connected. + """ + service_list = self.GetNetworkInfo() + connected_service_path = service_list.get('connected_wifi') + if 'wifi_networks' in service_list and \ + connected_service_path in service_list['wifi_networks']: + return service_list['wifi_networks'][connected_service_path]['name'] + + def GetServicePath(self, ssid): + """Returns the service path associated with an SSID. + + Args: + ssid: String defining the SSID we are searching for. + + Returns: + The service path or None if SSID does not exist. + """ + service_list = self.GetNetworkInfo() + service_list = service_list.get('wifi_networks', []) + for service_path, service_obj in service_list.iteritems(): + if service_obj['name'] == ssid: + return service_path + return None + + def ConnectToWifiRouter(self, router_name): + """Connects to a router by name. + + Args: + router_name: The name of the router that is specified in the + configuration file. + """ + router = self._wifi_power_strip.GetRouterConfig(router_name) + assert router, 'Router with name %s is not defined ' \ + 'in the router configuration.' % router_name + + service_path = self.GetServicePath(router['ssid']) + assert service_path, 'Service with SSID %s is not present.' % router['ssid'] + + passphrase = router.get('passphrase', '') + + logging.debug('Connecting to router %s.' % router_name) + return self.ConnectToWifiNetwork(service_path, passphrase) diff --git a/chrome/test/pyautolib/pyauto.py b/chrome/test/pyautolib/pyauto.py index 46ff17c..c091c59 100644 --- a/chrome/test/pyautolib/pyauto.py +++ b/chrome/test/pyautolib/pyauto.py @@ -2504,11 +2504,15 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): Blocks until scanning is complete. + Returns: + The new list of networks obtained from GetNetworkInfo(). + Raises: pyauto_errors.JSONInterfaceError if the automation call returns an error. """ cmd_dict = { 'command': 'NetworkScan' } self._GetResultFromJSONRequest(cmd_dict, windex=-1) + return self.GetNetworkInfo() PROXY_TYPE_DIRECT = 1 PROXY_TYPE_MANUAL = 2 @@ -2609,10 +2613,15 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): Blocks until connection succeeds or fails. + Args: + service_path: Flimflam path that defines the wifi network. + password: Passphrase for connecting to the wifi network. + identity: Identity for 802.11x networks. + certpath: Certificate path for 802.11x networks. + Returns: - A tuple. - The first element is True on success and False on failure. - The second element is None on success or an error string on failure. + An error string if an error occured. + None otherwise. Raises: pyauto_errors.JSONInterfaceError if the automation call returns an error. @@ -2625,13 +2634,10 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): 'certpath': certpath, } result = self._GetResultFromJSONRequest(cmd_dict, windex=-1) - if result.has_key('error_code'): - return (False, result['error_code']) - else: - return (True, None) + return result.get('error_code') def DisconnectFromWifiNetwork(self): - """Disconnect from a wifi network by its service path. + """Disconnect from the connected wifi network. Blocks until disconnect is complete. |