From 3425b8e1fc8663b2ec36a8f47593140f54824d0a Mon Sep 17 00:00:00 2001 From: "YARB(Cypress)" Date: Fri, 10 Jan 2020 19:58:40 +0200 Subject: [PATCH 1/2] Cypress: Initial commit of SoftAP host tests --- .../TESTS/host_tests/softap_basic.py | 231 ++++++++++++++++++ .../host_tests/softap_wpa_supplicant.conf | 8 + .../TARGET_PSOC6/TESTS/softap/ap/main.cpp | 85 +++++++ 3 files changed, 324 insertions(+) create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/TESTS/host_tests/softap_basic.py create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/TESTS/host_tests/softap_wpa_supplicant.conf create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/TESTS/softap/ap/main.cpp diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/TESTS/host_tests/softap_basic.py b/targets/TARGET_Cypress/TARGET_PSOC6/TESTS/host_tests/softap_basic.py new file mode 100644 index 0000000000..0d8e700a4f --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/TESTS/host_tests/softap_basic.py @@ -0,0 +1,231 @@ +""" +mbed SDK +Copyright (c) 2011-2019 ARM Limited + +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. +""" + + +from mbed_host_tests import BaseHostTest +from mbed_host_tests.host_tests_logger import HtrunLogger +import platform +import time, re, os +import subprocess + + +class SoftAPBasicHostTests(BaseHostTest): + """ Basic SoftAP test with host as STA + + [Following configuration is reqired before test] + 1. Identify and update the host wifi device name self.wifi_dev + 2. Add user as sudoer without password to allow script to run sudo command. Restrict the sudo command pattern to enhance security + ### sudoer config begin ### + # Allow members of group sudo to execute any command + %sudo ALL=(ALL:ALL) ALL + ALL=(ALL) NOPASSWD: /usr/sbin/rfkill unblock wifi, /sbin/wpa_supplicant -B -i * -c *, /usr/bin/killall wpa_supplicant, /sbin/wpa_cli -i * status, /sbin/dhclient -r *, /sbin/dhclient -nw -1 * + ### sudoer config end ### + + [Test behavior] + 1. Host wait for DUT SoftAP to start + 2. Upon receive ('softap', 'xxx') message from DUT SoftAP to perform corresponding test + """ + # Class constants + HOST_WIFI_CONNECTION_WAIT_SEC = 8 + HOST_WIFI_DHCP_GET_IP_WAIT_SEC = 3 + HOST_WIFI_INTER_CMDS_WAIT_SEC = 1 + HOST_WIFI_SEND_PING_ECHO_CNT = 1 + HOST_WPA_STATUS_REGEX = re.compile("^wpa_state=(\w+)$", re.MULTILINE) + HOST_WPA_SSID_REGEX = re.compile("^ssid=(\w+)$", re.MULTILINE) + HOST_PING_SOFTAP_REGEX = re.compile("^(\d+) packets transmitted, (\d+) received", re.MULTILINE) + + def __init__(self): + super(SoftAPBasicHostTests, self).__init__() + self.logger = HtrunLogger('TEST') + self.log = self.logger.prn_inf + + self.__result = False + self.wifi_dev = 'wlan0' + + # The SoftAP config must be consisten with client side + self.softap_ssid = 'MBEDSoftAPTest' + self.softap_password = 'abcd1234' + self.softap_ip_address = '192.168.10.1' + + self.softap_event_action_table = { + 'up' : self.host_wifi_softap_up_test + } + + def _callback_os_type(self, key, value, timestamp): + ''' Get host OS type + ''' + system_name = platform.system() + self.log('os_type = {}'.format(system_name)) + self.send_kv("os_type", system_name) + self.__result = True + + def _callback_softap_events(self, key, value, timestamp): + ''' DUT SoftAP event handler + Root handler of all 'softap' events, dispatch secondary handler based on "value" + ''' + self.log('Get ({}, {}) from DUT'.format(key, value)) + + if not value in self.softap_event_action_table: + self.log('Unknown softap event {}'.format(value)) + self.report_error('Unknow softap event') + return None + + if not self.softap_event_action_table[value](): + self.report_error('Response to ({}, {}) FAILED!'.format(key, value)) + else: + self.report_success() + self.__result = True + return None + + def setup(self): + # all functions that can be called from the client + self.register_callback('softap', self._callback_softap_events) + self.register_callback('get_os_type', self._callback_os_type) + try: + cmd = ['sudo', 'rfkill', 'unblock', 'wifi'] + output = subprocess.check_output(cmd, universal_newlines=True) + self.log('SETUP: {} => {}'.format(cmd, output)) + time.sleep(SoftAPBasicHostTests.HOST_WIFI_INTER_CMDS_WAIT_SEC) + except subprocess.CalledProcessError: + self.log ('WARNING: Setup commands may not all scceeed.') + + + def result(self): + return self.__result + + def teardown(self): + # release existing dhclient + try: + cmd = ['sudo', 'dhclient', '-r', self.wifi_dev] + output = subprocess.check_output(cmd, universal_newlines=True) + self.log('TEARDOWN: {} => {}'.format(cmd, output)) + time.sleep(SoftAPBasicHostTests.HOST_WIFI_INTER_CMDS_WAIT_SEC) + + cmd = ['sudo', 'killall', 'wpa_supplicant'] + output = subprocess.check_output(cmd, universal_newlines=True) + self.log('TEARDOWN: {} => {}'.format(cmd, output)) + except subprocess.CalledProcessError: + self.log ('WARNING: TearDown commands may not all scceeed.') + + def host_wifi_softap_up_test(self): + ''' Run host side test for ('softap', 'up') event + ''' + if not self.host_wifi_connect_softap_linux(): + return False + if not self.host_wifi_restart_dhcp_linux(): + return False + if not self.host_wifi_ping_softap(): + return False + return True + + def host_wifi_connect_softap_linux(self): + ''' Host Linux as STA to connect to DUT SoftAP + Test will run after receiving ('softap', 'up') message from DUT SoftAP + ''' + # killall is expected to fail when the host does NOT run wpa_supplicant by default + try: + cmd = ['sudo', 'killall', 'wpa_supplicant'] + output = subprocess.check_output(cmd, universal_newlines=True) + self.log('kill existing wpa_supplicant: {}'.format(cmd, universal_newlines=True)) + except subprocess.CalledProcessError: + self.log('INFO: no prior wpa_supplicant found') + + try: + dir_path = os.path.dirname(os.path.realpath(__file__)) + time.sleep(SoftAPBasicHostTests.HOST_WIFI_INTER_CMDS_WAIT_SEC) + cmd = ['sudo', 'wpa_supplicant', '-B', '-i', self.wifi_dev, '-c', \ + dir_path+'/softap_wpa_supplicant.conf'] + output = subprocess.check_output(cmd, universal_newlines=True) + self.log('start wpa_supplicant: {} => {}'.format(cmd, output)) + + self.log('Wait {} sec to connecct to {} ...'.format(\ + SoftAPBasicHostTests.HOST_WIFI_CONNECTION_WAIT_SEC, self.softap_ssid)) + time.sleep(SoftAPBasicHostTests.HOST_WIFI_CONNECTION_WAIT_SEC) + cmd = ['sudo', 'wpa_cli', '-i', self.wifi_dev, 'status'] + output = subprocess.check_output(cmd, universal_newlines=True) + self.log('{}: {}'.format(cmd, output)) + except subprocess.CalledProcessError as error: + self.log("ERROR: {} => {}".format(error.cmd, error.output)) + return False + + match = SoftAPBasicHostTests.HOST_WPA_STATUS_REGEX.search(output) + if match is None or (match.group(1) != 'COMPLETED' and match.group(1) != 'ASSOCIATED'): + self.log('ERROR: status => {}'.format(output)) + return False + + match = SoftAPBasicHostTests.HOST_WPA_SSID_REGEX.search(output) + if match is None or match.group(1) != self.softap_ssid: + self.log('ERROR: status => {}'.format(output)) + return False + self.log('SoftAP join PASS') + return True + + def host_wifi_restart_dhcp_linux(self): + ''' Restart host dhcp client to obtain IP + ''' + try: + # release existing dhclient + cmd = ['sudo', 'dhclient', '-r', self.wifi_dev] + output = subprocess.check_output(cmd, universal_newlines=True) + self.log('{} => {}'.format(cmd, output)) + time.sleep(SoftAPBasicHostTests.HOST_WIFI_INTER_CMDS_WAIT_SEC) + + cmd = ['sudo', 'dhclient', '-nw', '-1', self.wifi_dev] + output = subprocess.check_output(cmd, universal_newlines=True) + self.log('{} => {}'.format(cmd, output)) + self.log('Wait {} sec for dhcp to get IP from SoftAP'.format( \ + SoftAPBasicHostTests.HOST_WIFI_DHCP_GET_IP_WAIT_SEC)) + time.sleep(SoftAPBasicHostTests.HOST_WIFI_DHCP_GET_IP_WAIT_SEC) + except subprocess.CalledProcessError as error: + self.log("ERROR: {} => {}".format(error.cmd, error.output)) + return False + return True + + def host_wifi_ping_softap(self): + ''' Ping SoftAP with self.softap_ip_address + ''' + try: + cmd = ['ping', '-c', str(SoftAPBasicHostTests.HOST_WIFI_SEND_PING_ECHO_CNT), \ + self.softap_ip_address] + output = subprocess.check_output(cmd, universal_newlines=True) + self.log('{} => {}'.format(cmd, output)) + except subprocess.CalledProcessError as error: + self.log("ERROR: {} => {}".format(error.cmd, error.output)) + return False + + match = SoftAPBasicHostTests.HOST_PING_SOFTAP_REGEX.search(output) + if match is None or match.group(2) == '0': + self.log('Ping ERROR: {} => {}'.format(cmd, output)) + return False + self.log('Ping PASS: {}/{} echo received'.format(match.group(2), match.group(1))) + return True + + def get_wpa_supplicant_conf_path(self): + ''' Report host side test success to DUT + ''' + self.send_kv("passed", "0") + + def report_success(self): + ''' Report host side test success to DUT + ''' + self.send_kv("passed", "0") + + def report_error(self, msg): + ''' Report host side test error to DUT + ''' + self.log('{} failed !!!'.format(msg)) + self.send_kv("failed", "0") diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/TESTS/host_tests/softap_wpa_supplicant.conf b/targets/TARGET_Cypress/TARGET_PSOC6/TESTS/host_tests/softap_wpa_supplicant.conf new file mode 100644 index 0000000000..2eeea015f4 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/TESTS/host_tests/softap_wpa_supplicant.conf @@ -0,0 +1,8 @@ +ctrl_interface=/run/wpa_supplicant +update_config=1 + +network={ + ssid="MBEDSoftAPTest" + #psk="abcd1234" + psk=a2a7f7f2d84390b6c6ba644a3b5f5fbe2113c18734dd992dc0d976529ba684c4 +} diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/TESTS/softap/ap/main.cpp b/targets/TARGET_Cypress/TARGET_PSOC6/TESTS/softap/ap/main.cpp new file mode 100644 index 0000000000..560e7b12c9 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/TESTS/softap/ap/main.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017-2019, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#if !defined(MBED_CONF_APP_SOFTAP_SSID) || \ + !defined(MBED_CONF_APP_SOFTAP_CHANNEL) || \ + !defined(MBED_CONF_APP_SOFTAP_PASSWORD) + +#error [NOT_SUPPORTED] Requires parameters from mbed_app.json +#endif + +#define SOFTAP 1 + +#if MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE != SOFTAP + +#error [NOT_SUPPORTED] SoftAP testing need to be enabled +#endif + +#if !defined(TARGET_PSOC6) +#error [NOT_SUPPORTED] Wifi tests are not valid for the target +#endif + +#define SOFTAP_IP_ADDRESS "192.168.10.1" +#define NETMASK "255.255.255.0" +#define GATEWAY "192.168.10.1" + +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +#include "WhdSoftAPInterface.h" + +using namespace utest::v1; + +static char _key[256] = { 0 }; +static char _value[128] = { 0 }; + +void host_os_type_verification() { + greentea_send_kv("get_os_type", "host"); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("Linux", _value); +} + +void softap_test() { + WhdSoftAPInterface *softap = WhdSoftAPInterface::get_default_instance(); + TEST_ASSERT_TRUE(softap != NULL); + + softap->set_network(SOFTAP_IP_ADDRESS, NETMASK, GATEWAY); + int ret = softap->start(MBED_CONF_APP_SOFTAP_SSID, MBED_CONF_APP_SOFTAP_PASSWORD, NSAPI_SECURITY_WPA_WPA2, + MBED_CONF_APP_SOFTAP_CHANNEL); + TEST_ASSERT_EQUAL_INT(0, ret); + greentea_send_kv("softap", "up"); + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + TEST_ASSERT_EQUAL_STRING("passed", _key); +} + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) { + // here, we specify the timeout (150) and the host runner (the name of our Python file) + GREENTEA_SETUP(150, "softap_basic"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Host OS Type Verification", host_os_type_verification), + Case("SoftAP test", softap_test) +}; + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() { + return Harness::run(specification); +} From c53d2f119800b87a4e9997ac0b55f575689bef8b Mon Sep 17 00:00:00 2001 From: "YARB(Cypress)" Date: Mon, 13 Jan 2020 14:46:32 +0200 Subject: [PATCH 2/2] Addressed comments --- .../TARGET_PSOC6/TESTS/host_tests/softap_basic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/TESTS/host_tests/softap_basic.py b/targets/TARGET_Cypress/TARGET_PSOC6/TESTS/host_tests/softap_basic.py index 0d8e700a4f..305e5d920e 100644 --- a/targets/TARGET_Cypress/TARGET_PSOC6/TESTS/host_tests/softap_basic.py +++ b/targets/TARGET_Cypress/TARGET_PSOC6/TESTS/host_tests/softap_basic.py @@ -1,6 +1,6 @@ """ -mbed SDK -Copyright (c) 2011-2019 ARM Limited +Copyright (c) 2011-2020 ARM Limited +SPDX-License-Identifier: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import subprocess class SoftAPBasicHostTests(BaseHostTest): """ Basic SoftAP test with host as STA - [Following configuration is reqired before test] + [Following configuration is required before test] 1. Identify and update the host wifi device name self.wifi_dev 2. Add user as sudoer without password to allow script to run sudo command. Restrict the sudo command pattern to enhance security ### sudoer config begin ###