Add Beacon Loss tests

Observe that DUT marks the current service as "idle" after we
"pull the plug" on the AP.  To do this, we discover what drivers
the AP uses for WiFi and reload them.

BUG=chromium-os:15981  (this CL expresses that bug)
TEST=Run test

Change-Id: I37b5f15658dec410c53e3dc1a5f8f7f1d0f34602
Reviewed-on: http://gerrit.chromium.org/gerrit/1891
Reviewed-by: Sam Leffler <sleffler@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/server/site_linux_router.py b/server/site_linux_router.py
index 60f8822..ce1051d 100644
--- a/server/site_linux_router.py
+++ b/server/site_linux_router.py
@@ -415,6 +415,11 @@
 
         # Taking down hostapd takes wlan0 and mon.wlan0 down.
         if self.hostapd['configured']:
+            if 'silent' in params:
+                # Deconfigure without notifying DUT.  Remove the monitor
+                # interface hostapd uses to send beacon and DEAUTH packets
+                self._remove_interfaces()
+
             self.router.run("pkill hostapd >/dev/null 2>&1", ignore_status=True)
 #           self.router.run("rm -f %s" % self.hostapd['file'])
             self.router.get_file(self.hostapd['log'],
diff --git a/server/site_linux_system.py b/server/site_linux_system.py
index 612b070..526d011 100644
--- a/server/site_linux_system.py
+++ b/server/site_linux_system.py
@@ -34,7 +34,6 @@
 
         # Network interfaces.
         self.frequency_support = {}
-        self.wlanifs = {}
 
         # Parse the output of 'iw phy' and find a device for each frequency
         output = host.run("%s list" % self.cmd_iw).stdout
@@ -57,15 +56,20 @@
         self.phydev2 = params.get('phydev2', None)
         self.phydev5 = params.get('phydev5', None)
 
+        self._remove_interfaces()
+
+
+    def _remove_interfaces(self):
+        self.wlanifs = {}
         # Remove all wifi devices.
-        output = host.run("%s dev" % self.cmd_iw).stdout
+        output = self.host.run("%s dev" % self.cmd_iw).stdout
         test = re.compile("[\s]*Interface (.*)")
         for line in output.splitlines():
             m = test.match(line)
             if m:
                 device = m.group(1)
-                host.run("%s link set %s down" % (self.cmd_ip, device))
-                host.run("%s dev %s del" % (self.cmd_iw, device))
+                self.host.run("%s link set %s down" % (self.cmd_ip, device))
+                self.host.run("%s dev %s del" % (self.cmd_iw, device))
 
 
     def start_capture(self, params):
diff --git a/server/site_tests/network_WiFiRoaming/006BeaconLoss b/server/site_tests/network_WiFiRoaming/006BeaconLoss
new file mode 100644
index 0000000..d216a3e
--- /dev/null
+++ b/server/site_tests/network_WiFiRoaming/006BeaconLoss
@@ -0,0 +1,35 @@
+# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This tests how fast the DUT responds to beacon loss when AP completely
+# disappears
+{ "name":"BeaconLoss",
+  "steps":[
+    [ "create",               { "type":"hostap" } ],
+    [ "config",               { "channel":"2412", "mode":"11b" } ],
+    [ "connect",              { "security":"none" } ],
+    [ "client_ping",          { "count":"10" } ],
+    [ "sleep",                { "time":"5" } ],
+    [ "deconfig",             { "silent":None } ],  # Exit AP without DEAUTH
+    [ "wait_service",         { "run_timeout":10,   # Timeout is 10 seconds
+                                "debug":True,       # Print state transitions
+                                "states":[
+                                  (None, 'idle')    # Wait for transition to
+                                ] } ],              # 'idle' state.
+
+    # Do the test again, this time forcing a scan on the DUT just before the
+    # AP disappears.
+    [ "config",               { "channel":"2412", "mode":"11b" } ],
+    [ "connect",              { "security":"none" } ],
+    [ "client_ping",          { "count":"10" } ],
+    [ "scan" ],
+    [ "deconfig",             { "silent":None } ],  # Exit AP without DEAUTH
+    [ "wait_service",         { "run_timeout":10,   # Timeout is 10 seconds
+                                "debug":True,       # Print state transitions
+                                "states":[
+                                  (None, 'idle')    # Wait for transition to
+                                ] } ],              # 'idle' state.
+    [ "destroy" ],
+  ],
+}
diff --git a/server/site_wifitest.py b/server/site_wifitest.py
index 9e81efc..b9bee30 100644
--- a/server/site_wifitest.py
+++ b/server/site_wifitest.py
@@ -1279,6 +1279,11 @@
         self.bgscan_set({'method' : 'default'})
 
 
+    def scan(self, params):
+        self.client.run("%s dev %s scan" %
+                        (self.client_cmd_iw, self.client_wlanif))
+
+
     def time_sync(self, params):
         for name in params or ['client', 'server', 'router']:
             system = { 'client': self.client,