Revert "Revert "Re-structure ACTS to Match New Projects""

This reverts commit 842ad4b8bc3e0aeda7117f1242902922feb16821.

Change-Id: I3007e53cb4955a80f2fbd3d416bce8e45101a8f3
diff --git a/acts/tests/google/wifi/WifiEnterpriseTest.py b/acts/tests/google/wifi/WifiEnterpriseTest.py
new file mode 100755
index 0000000..c0f8d9f
--- /dev/null
+++ b/acts/tests/google/wifi/WifiEnterpriseTest.py
@@ -0,0 +1,447 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2015 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import pprint
+import time
+
+from queue import Empty
+from random import shuffle
+
+from acts.base_test import BaseTestClass
+from acts.test_utils.wifi_test_utils import eap_connect
+from acts.test_utils.wifi_test_utils import expand_enterprise_config_by_phase2
+from acts.test_utils.wifi_test_utils import reset_droid_wifi
+from acts.test_utils.wifi_test_utils import start_wifi_connection_scan
+from acts.test_utils.wifi_test_utils import wifi_test_device_init
+from acts.test_utils.wifi_test_utils import WifiEnums
+from acts.test_utils.wifi_test_utils import WifiEventNames
+
+# EAP Macros
+EAP = WifiEnums.Eap
+EapPhase2 = WifiEnums.EapPhase2
+
+# Enterprise Config Macros
+Ent = WifiEnums.Enterprise
+# Event name macros
+Event = WifiEventNames
+
+
+class WifiEnterpriseTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+            "test_eap_connect",
+            "test_eap_connect_negative",
+            # "test_passpoint_connect",
+            # "test_passpoint_connect_negative"
+        )
+
+    def setup_class(self):
+        self.dut = self.android_devices[0]
+        wifi_test_device_init(self.dut)
+        required_userparam_names = (
+            "ca_cert",
+            "client_cert",
+            "client_key",
+            "passpoint_ca_cert",
+            "passpoint_client_cert",
+            "passpoint_client_key",
+            "eap_identity",
+            "eap_password",
+            "invalid_ca_cert",
+            "invalid_client_cert",
+            "invalid_client_key",
+            "fqdn",
+            "provider_friendly_name",
+            "realm",
+            "ssid_peap",
+            "ssid_tls",
+            "ssid_ttls",
+            "ssid_sim",
+            "ssid_passpoint",
+            "device_password",
+            "ping_addr"
+        )
+        optional_userparam_names = (
+            "roaming_consortium_ids",
+            "plmn"
+        )
+        assert self.unpack_userparams(required_userparam_names,
+                    opt_param_names = optional_userparam_names)
+        # Default configs for EAP networks.
+        self.config_peap = {
+            Ent.EAP: EAP.PEAP,
+            Ent.CA_CERT: self.ca_cert,
+            Ent.IDENTITY: self.eap_identity,
+            Ent.PASSWORD: self.eap_password,
+            Ent.PHASE2: EapPhase2.MSCHAPV2,
+            WifiEnums.SSID_KEY: self.ssid_peap
+        }
+        self.config_tls = {
+            Ent.EAP: EAP.TLS,
+            Ent.CA_CERT: self.ca_cert,
+            WifiEnums.SSID_KEY: self.ssid_tls,
+            Ent.CLIENT_CERT: self.client_cert,
+            Ent.PRIVATE_KEY_ID: self.client_key,
+            Ent.IDENTITY: self.eap_identity,
+        }
+        self.config_ttls = {
+            Ent.EAP: EAP.TTLS,
+            Ent.CA_CERT: self.ca_cert,
+            Ent.IDENTITY: self.eap_identity,
+            Ent.PASSWORD: self.eap_password,
+            Ent.PHASE2: EapPhase2.MSCHAPV2,
+            WifiEnums.SSID_KEY: self.ssid_ttls
+        }
+        self.config_sim = {
+            Ent.EAP: EAP.SIM,
+            WifiEnums.SSID_KEY: self.ssid_sim,
+        }
+
+        # Base config for passpoint networks.
+        self.config_passpoint = {
+            Ent.FQDN: self.fqdn,
+            Ent.FRIENDLY_NAME: self.provider_friendly_name,
+            Ent.REALM: self.realm,
+            Ent.CA_CERT: self.passpoint_ca_cert
+        }
+        if hasattr(self, "plmn"):
+            self.config_passpoint[Ent.PLMN] = self.plmn
+        if hasattr(self, "roaming_consortium_ids"):
+            self.config_passpoint[Ent.ROAMING_IDS] = self.roaming_consortium_ids
+
+        # Default configs for passpoint networks.
+        self.config_passpoint_tls = dict(self.config_tls)
+        self.config_passpoint_tls.update(self.config_passpoint)
+        self.config_passpoint_tls[Ent.CLIENT_CERT] = self.passpoint_client_cert
+        self.config_passpoint_tls[Ent.PRIVATE_KEY_ID] = self.passpoint_client_key
+        del self.config_passpoint_tls[WifiEnums.SSID_KEY]
+        self.config_passpoint_ttls = dict(self.config_ttls)
+        self.config_passpoint_ttls.update(self.config_passpoint)
+        del self.config_passpoint_ttls[WifiEnums.SSID_KEY]
+        # Set screen lock password so ConfigStore is unlocked.
+        self.droid.setDevicePassword(self.device_password)
+        return True
+
+    def teardown_class(self):
+        reset_droid_wifi(self.droid, self.ed)
+        self.droid.disableDevicePassword()
+        self.ed.clear_all_events()
+
+    def setup_test(self):
+        self.droid.wifiStartTrackingStateChange()
+        self.droid.wakeLockAcquireBright()
+        self.droid.wakeUpNow()
+        reset_droid_wifi(self.droid, self.ed)
+        self.ed.clear_all_events()
+        return True
+
+    def teardown_test(self):
+        self.droid.wakeLockRelease()
+        self.droid.goToSleepNow()
+        self.droid.wifiStopTrackingStateChange()
+
+    """Helper Functions"""
+
+    def eap_negative_connect_logic(self, config, ad):
+        """Tries to connect to an enterprise network with invalid credentials
+        and expect a failure.
+
+        Args:
+            config: A dict representing an invalid EAP credential.
+
+        Returns:
+            True if connection failed as expected, False otherwise.
+        """
+        verdict = eap_connect(config, ad)
+        self.assert_true(not verdict, "Connection should have failed.")
+        self.log.info("Connection failed as expected.")
+        return True
+
+    def expand_config_by_phase2(self, config):
+        """Take an enterprise config and generate a list of configs, each with
+        a different phase2 auth type.
+
+        Args:
+            config: A dict representing enterprise config.
+
+        Returns
+            A list of enterprise configs.
+        """
+        results = []
+        for phase2_type in EapPhase2:
+            # Skip a special case for passpoint TTLS.
+            if Ent.FQDN in config and phase2_type == EapPhase2.GTC:
+                continue
+            c = dict(config)
+            c[Ent.PHASE2] = phase2_type
+            results.append(c)
+        return results
+
+    def gen_eap_configs(self):
+        """Generates configurations for different EAP authentication types.
+
+        Returns:
+            A list of dicts each representing an EAP configuration.
+        """
+        configs = [self.config_tls]
+                   # self.config_sim
+        configs += expand_enterprise_config_by_phase2(self.config_ttls)
+        configs += expand_enterprise_config_by_phase2(self.config_peap)
+        return configs
+
+    def gen_passpoint_configs(self):
+        """Generates passpoint configurations for different EAP authentication
+        types.
+
+        Returns:
+            A list of dicts each representing an EAP configuration for
+            passpoint networks.
+        """
+        configs = [self.config_passpoint_tls]
+        configs += expand_enterprise_config_by_phase2(self.config_passpoint_ttls)
+        return configs
+
+    def gen_negative_configs(self, configs, neg_params):
+        """Generic function used to generate negative configs.
+
+        For all the valid configurations, if a param in the neg_params also
+        exists in a config, a copy of the config is made with an invalid value
+        of the param.
+
+        Args:
+            configs: A list of valid configurations.
+            neg_params: A dict that has all the invalid values.
+
+        Returns:
+            A list of invalid configurations generated based on the valid
+            configurations. Each invalid configuration has a different invalid
+            field.
+        """
+        results = []
+        for c in configs:
+            for k, v in neg_params.items():
+                # Skip negative test for TLS's identity field since it's not
+                # used for auth.
+                if c[Ent.EAP] == EAP.TLS and k == Ent.IDENTITY:
+                    continue
+                if k in c:
+                    nc = dict(c)
+                    nc[k] = v
+                    nc["invalid_field"] = k
+                    results.append(nc)
+        return results
+
+    def gen_negative_eap_configs(self):
+        """Generates invalid configurations for different EAP authentication
+        types.
+
+        For all the valid EAP configurations, if a param that is part of the
+        authentication info exists in a config, a copy of the config is made
+        with an invalid value of the param.
+
+        Returns:
+            A list of dicts each representing an invalid EAP configuration.
+        """
+        neg_params = {
+            Ent.CLIENT_CERT: self.invalid_client_cert,
+            Ent.CA_CERT: self.invalid_ca_cert,
+            Ent.PRIVATE_KEY_ID: self.invalid_client_key,
+            Ent.IDENTITY: "fake_identity",
+            Ent.PASSWORD: "wrong_password"
+        }
+        configs = self.gen_eap_configs()
+        return self.gen_negative_configs(configs, neg_params)
+
+    def gen_negative_passpoint_configs(self):
+        """Generates invalid configurations for different EAP authentication
+        types with passpoint support.
+
+        Returns:
+            A list of dicts each representing an invalid EAP configuration
+            with passpoint fields.
+        """
+        neg_params = {
+            Ent.CLIENT_CERT: self.invalid_client_cert,
+            Ent.CA_CERT: self.invalid_ca_cert,
+            Ent.PRIVATE_KEY_ID: self.invalid_client_key,
+            Ent.IDENTITY: "fake_identity",
+            Ent.PASSWORD: "wrong_password",
+            Ent.FQDN: "fake_fqdn",
+            Ent.REALM: "where_no_one_has_gone_before",
+            Ent.PLMN: "fake_plmn",
+            Ent.ROAMING_IDS: [1234567890, 9876543210]
+        }
+        configs = self.gen_passpoint_configs()
+        return self.gen_negative_configs(configs, neg_params)
+
+    def gen_eap_test_name(self, config, ad):
+        """Generates a test case name based on an EAP configuration.
+
+        Args:
+            config: A dict representing an EAP credential.
+            ad: Discarded. This is here because name function signature needs
+                to be consistent with logic function signature for generated
+                test cases.
+
+        Returns:
+            A string representing the name of a generated EAP test case.
+        """
+        name = "test_connect-%s" % config[Ent.EAP].name
+        if Ent.PHASE2 in config:
+            name += "-{}".format(config[Ent.PHASE2].name)
+        return name
+
+    def gen_passpoint_test_name(self, config, ad):
+        """Generates a test case name based on an EAP passpoint configuration.
+
+        Args:
+            config: A dict representing an EAP passpoint credential.
+            ad: Discarded. This is here because name function signature needs
+                to be consistent with logic function signature for generated
+                test cases.
+
+        Returns:
+            A string representing the name of a generated EAP passpoint connect
+            test case.
+        """
+        name = self.gen_eap_test_name(config, ad)
+        name = name.replace("connect", "passpoint_connect")
+        return name
+
+    """Tests"""
+    def test_eap_connect(self):
+        """Test connecting to enterprise networks of different authentication
+        types.
+
+        The authentication types tested are:
+            EAP-TLS
+            EAP-PEAP with different phase2 types.
+            EAP-TTLS with different phase2 types.
+
+        Procedures:
+            For each enterprise wifi network
+            1. Connect to the network.
+            2. Send a GET request to a website and check response.
+
+        Expect:
+            Successful connection and Internet access through the enterprise
+            networks.
+        """
+        eap_configs = self.gen_eap_configs()
+        self.log.info("Testing %d different configs." % len(eap_configs))
+        shuffle(eap_configs)
+        failed = self.run_generated_testcases(
+            eap_connect,
+            eap_configs,
+            self.dut,
+            name_func=self.gen_eap_test_name)
+        msg = ("The following configs failed EAP connect test: %s" %
+               pprint.pformat(failed))
+        self.assert_true(len(failed) == 0, msg)
+        return True
+
+    def test_eap_connect_negative(self):
+        """Test connecting to enterprise networks.
+
+        Procedures:
+            For each enterprise wifi network
+            1. Connect to the network with invalid credentials.
+
+        Expect:
+            Fail to establish connection.
+        """
+        neg_eap_configs = self.gen_negative_eap_configs()
+        self.log.info("Testing %d different configs." % len(neg_eap_configs))
+        shuffle(neg_eap_configs)
+        def name_gen(config, ad):
+            name = self.gen_eap_test_name(config, ad)
+            name += "-with_wrong-{}".format(config["invalid_field"])
+            return name
+        failed = self.run_generated_testcases(
+            self.eap_negative_connect_logic,
+            neg_eap_configs,
+            self.dut,
+            name_func=name_gen)
+        msg = ("The following configs failed negative EAP connect test: %s" %
+               pprint.pformat(failed))
+        self.assert_true(len(failed) == 0, msg)
+        return True
+
+    def test_passpoint_connect(self):
+        """Test connecting to enterprise networks of different authentication
+        types with passpoint support.
+
+        The authentication types tested are:
+            EAP-TLS
+            EAP-TTLS with MSCHAPV2 as phase2.
+
+        Procedures:
+            For each enterprise wifi network
+            1. Connect to the network.
+            2. Send a GET request to a website and check response.
+
+        Expect:
+            Successful connection and Internet access through the enterprise
+            networks with passpoint support.
+        """
+        self.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+            "Passpoint is not supported on device %s" % self.dut.model)
+        passpoint_configs = self.gen_passpoint_configs()
+        self.log.info("Testing %d different configs." % len(passpoint_configs))
+        shuffle(passpoint_configs)
+        failed = self.run_generated_testcases(
+            eap_connect,
+            passpoint_configs,
+            self.dut,
+            name_func=self.gen_passpoint_test_name)
+        msg = ("The following configs failed passpoint connect test: %s" %
+               pprint.pformat(failed))
+        self.assert_true(len(failed) == 0, msg)
+        return True
+
+    def test_passpoint_connect_negative(self):
+        """Test connecting to enterprise networks.
+
+        Procedures:
+            For each enterprise wifi network
+            1. Connect to the network with invalid credentials.
+
+        Expect:
+            Fail to establish connection.
+        """
+        self.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+            "Passpoint is not supported on device %s" % self.dut.model)
+        neg_passpoint_configs = self.gen_negative_passpoint_configs()
+        self.log.info("Testing %d different configs." % len(neg_passpoint_configs))
+        shuffle(neg_passpoint_configs)
+        def name_gen(config, ad):
+            name = self.gen_passpoint_test_name(config, ad)
+            name += "-with_wrong-{}".format(config["invalid_field"])
+            return name
+        failed = self.run_generated_testcases(
+            self.eap_negative_connect_logic,
+            neg_passpoint_configs,
+            self.dut,
+            name_func=name_gen)
+        msg = ("The following configs failed negative passpoint connect test: "
+               "%s") % pprint.pformat(failed)
+        self.assert_true(len(failed) == 0, msg)
+        return True
+
+if __name__ == "__main__":
+    pass