1 # Copyright (c) 2013 The Chromium OS 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. 4 5 from autotest_lib.client.common_lib import error 6 from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes 7 from autotest_lib.client.common_lib.cros.network import xmlrpc_security_types 8 from autotest_lib.server.cros.network import hostap_config 9 from autotest_lib.server.cros.network import wifi_cell_test_base 10 11 12 class ProfileRemovingContext(object): 13 """Creates and pushes a profile that is guaranteed to be removed.""" 14 15 @property 16 def profile_name(self): 17 """@return string: name of profile created and pushed.""" 18 return self._profile_name 19 20 21 def __init__(self, wifi_client, profile_name='always_removed'): 22 self._wifi_client = wifi_client 23 self._profile_name = profile_name 24 25 26 def __enter__(self): 27 if not all([self._wifi_client.shill.create_profile(self.profile_name), 28 self._wifi_client.shill.push_profile(self.profile_name)]): 29 raise error.TestFail('Failed to create/push profile %s' % 30 self.profile_name) 31 return self 32 33 34 def __exit__(self, exc_type, exc_value, traceback): 35 # Ignore pop errors in case the test popped it on its own 36 self._wifi_client.shill.pop_profile(self.profile_name) 37 if not self._wifi_client.shill.remove_profile(self.profile_name): 38 raise error.TestFail('Failed to remove profile %s.' % 39 self.profile_name) 40 41 42 class network_WiFi_ProfileBasic(wifi_cell_test_base.WiFiCellTestBase): 43 """Tests that credentials are stored in profiles.""" 44 45 version = 1 46 47 CHANNEL_NUMBER = 1 48 STATE_TRANSITION_TIMEOUT_SECONDS = 20 49 50 51 def _assert_state_transition(self, ssid, states): 52 """Raise an error if a WiFi service doesn't transition to |states|. 53 54 @param ssid: string ssid of service. 55 @param states: list of string states to wait for. 56 57 """ 58 result = self.context.client.wait_for_service_states( 59 ssid, states, 60 timeout_seconds=self.STATE_TRANSITION_TIMEOUT_SECONDS) 61 62 success, state, duration_seconds = result 63 if not success: 64 raise error.TestFail('Timed out waiting for states: %r in %f ' 65 'seconds. Ended in %s' % 66 (states, duration_seconds, state)) 67 68 69 def run_once(self): 70 """Body of the test.""" 71 self.context.client.shill.clean_profiles() 72 wep_config = xmlrpc_security_types.WEPConfig( 73 wep_keys=['abcde', 'fghij', 'klmno', 'pqrst']) 74 ap_config0 = hostap_config.HostapConfig( 75 channel=self.CHANNEL_NUMBER, security_config=wep_config) 76 ap_config1 = hostap_config.HostapConfig( 77 channel=self.CHANNEL_NUMBER, security_config=wep_config) 78 with ProfileRemovingContext(self.context.client, 79 profile_name='bottom') as bottom: 80 self.context.configure(ap_config0) 81 client_config0 = xmlrpc_datatypes.AssociationParameters( 82 security_config=ap_config0.security_config, 83 ssid=self.context.router.get_ssid()) 84 self.context.assert_connect_wifi(client_config0) 85 self.context.assert_ping_from_dut(ap_num=0) 86 # Check that popping a profile causes a loss of credentials and a 87 # disconnect. 88 if not self.context.client.shill.pop_profile(bottom.profile_name): 89 raise error.TestFail('Failed to pop profile %s.' % 90 bottom.profile_name) 91 92 self._assert_state_transition(client_config0.ssid, ['idle']) 93 # Check that pushing a profile causes credentials to reappear. 94 if not self.context.client.shill.push_profile(bottom.profile_name): 95 raise error.TestFail('Failed to push profile %s.' % 96 bottom.profile_name) 97 98 self._assert_state_transition(client_config0.ssid, 99 ['ready', 'portal', 'online']) 100 101 # Explicitly disconnect from the AP. 102 self.context.client.shill.disconnect(client_config0.ssid) 103 self._assert_state_transition(client_config0.ssid, ['idle']) 104 105 with ProfileRemovingContext(self.context.client, 106 profile_name='top') as top: 107 # Changes to the profile stack should clear the "explicitly 108 # disconnected" flag on all services. This should cause shill 109 # to re-connect to the AP. 110 self._assert_state_transition(client_config0.ssid, 111 ['ready', 'portal', 'online']) 112 113 self.context.configure(ap_config1, multi_interface=True) 114 client_config1 = xmlrpc_datatypes.AssociationParameters( 115 security_config=ap_config1.security_config, 116 ssid=self.context.router.get_ssid(instance=1)) 117 self.context.assert_connect_wifi(client_config1) 118 self.context.assert_ping_from_dut(ap_num=1) 119 # Check that deleting an entry also causes a disconnect and 120 # autoconect to a previously remembered service. 121 if not self.context.client.shill.delete_entries_for_ssid( 122 client_config1.ssid): 123 raise error.TestFail('Failed to delete profile entry for ' 124 '%s' % client_config1.ssid) 125 126 self._assert_state_transition(client_config1.ssid, ['idle']) 127 self._assert_state_transition(client_config0.ssid, 128 ['ready', 'portal', 'online']) 129 # Verify that the same sort of thing happens when we pop 130 # a profile on top of another one. 131 self.context.assert_connect_wifi(client_config1) 132 self.context.assert_ping_from_dut(ap_num=1) 133 if not self.context.client.shill.pop_profile(top.profile_name): 134 raise error.TestFail('Failed to pop profile %s.' % 135 top.profile_name) 136 self._assert_state_transition(client_config1.ssid, ['idle']) 137 self._assert_state_transition(client_config0.ssid, 138 ['ready', 'portal', 'online']) 139 140 # Re-push the top profile. 141 if not self.context.client.shill.push_profile(top.profile_name): 142 raise error.TestFail('Failed to push profile %s.' % 143 top.profile_name) 144 145 # Explicitly disconnect from the AP. 146 self.context.client.shill.disconnect(client_config0.ssid) 147 self._assert_state_transition(client_config0.ssid, ['idle']) 148 149 # Verify that popping a profile -- even one which does not 150 # affect the service profile -- returns explicitly disconnected 151 # services back into the pool of connectable services. 152 if not self.context.client.shill.pop_profile(top.profile_name): 153 raise error.TestFail('Failed to pop profile %s.' % 154 top.profile_name) 155 156 # A change to the profile stack should have caused us to 157 # reconnect to the service, since the "explicitly disconnected" 158 # flag will be removed. 159 self._assert_state_transition(client_config0.ssid, 160 ['ready', 'portal', 'online']) 161