From 5f43f18ba3383a6570a20bd019fb06e8cdb7bb36 Mon Sep 17 00:00:00 2001 From: Sam Grove Date: Fri, 29 Jul 2016 16:10:36 -0500 Subject: [PATCH] adding tests for net/IPV4 --- .../TESTS/mbedmicro-net/.mbedignore | 1 + .../host_tests/tcp_echo_client.py | 198 ++++++++++++++++++ .../host_tests/udp_echo_client.py | 125 +++++++++++ .../nist_internet_time_service/main.cpp | 65 ++++++ .../mbedmicro-net/tcp_client_echo/main.cpp | 77 +++++++ .../tcp_client_hello_world/main.cpp | 82 ++++++++ .../mbedmicro-net/udp_echo_client/main.cpp | 74 +++++++ 7 files changed, 622 insertions(+) create mode 100644 features/net/FEATURE_IPV4/TESTS/mbedmicro-net/.mbedignore create mode 100644 features/net/FEATURE_IPV4/TESTS/mbedmicro-net/host_tests/tcp_echo_client.py create mode 100644 features/net/FEATURE_IPV4/TESTS/mbedmicro-net/host_tests/udp_echo_client.py create mode 100644 features/net/FEATURE_IPV4/TESTS/mbedmicro-net/nist_internet_time_service/main.cpp create mode 100644 features/net/FEATURE_IPV4/TESTS/mbedmicro-net/tcp_client_echo/main.cpp create mode 100644 features/net/FEATURE_IPV4/TESTS/mbedmicro-net/tcp_client_hello_world/main.cpp create mode 100644 features/net/FEATURE_IPV4/TESTS/mbedmicro-net/udp_echo_client/main.cpp diff --git a/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/.mbedignore b/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/.mbedignore new file mode 100644 index 00000000000..709fc036051 --- /dev/null +++ b/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/.mbedignore @@ -0,0 +1 @@ +host_tests/* \ No newline at end of file diff --git a/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/host_tests/tcp_echo_client.py b/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/host_tests/tcp_echo_client.py new file mode 100644 index 00000000000..51a3c21c147 --- /dev/null +++ b/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/host_tests/tcp_echo_client.py @@ -0,0 +1,198 @@ +# Copyright 2015 ARM Limited, All rights reserved +# +# 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 sys +import select +import socket +import logging +from threading import Thread +from sys import stdout +from SocketServer import BaseRequestHandler, TCPServer +from mbed_host_tests import BaseHostTest, event_callback + + +class TCPEchoClientHandler(BaseRequestHandler): + def handle(self): + """ + Handles a connection. Test starts by client(i.e. mbed) connecting to server. + This connection handler receives data and echoes back to the client util + {{end}} is received. Then it sits on recv() for client to terminate the + connection. + + Note: reason for not echoing data back after receiving {{end}} is that send + fails raising a SocketError as client closes connection. + """ + print ("HOST: TCPEchoClient_Handler: Connection received...") + while self.server.isrunning(): + try: + data = self.recv() + if not data: break + except Exception as e: + print ('HOST: TCPEchoClient_Handler recv error: %s' % str(e)) + break + + print ('HOST: TCPEchoClient_Handler: Rx: \n%s\n' % data) + + try: + # echo data back to the client + self.send(data) + except Exception as e: + print ('HOST: TCPEchoClient_Handler send error: %s' % str(e)) + break + print 'Connection finished' + + def recv(self): + """ + Try to receive until server is shutdown + """ + while self.server.isrunning(): + rl, wl, xl = select.select([self.request], [], [], 1) + if len(rl): + return self.request.recv(1024) + + def send(self, data): + """ + Try to send until server is shutdown + """ + while self.server.isrunning(): + rl, wl, xl = select.select([], [self.request], [], 1) + if len(wl): + self.request.sendall(data) + break + + +class TCPServerWrapper(TCPServer): + """ + Wrapper over TCP server to implement server initiated shutdown. + Adds a flag:= running that a request handler can check and come out of + recv loop when shutdown is called. + """ + + def __init__(self, addr, request_handler): + # hmm, TCPServer is not sub-classed from object! + if issubclass(TCPServer, object): + super(TCPServerWrapper, self).__init__(addr, request_handler) + else: + TCPServer.__init__(self, addr, request_handler) + self.running = False + + def serve_forever(self): + self.running = True + if issubclass(TCPServer, object): + super(TCPServerWrapper, self).serve_forever() + else: + TCPServer.serve_forever(self) + + def shutdown(self): + self.running = False + if issubclass(TCPServer, object): + super(TCPServerWrapper, self).shutdown() + else: + TCPServer.shutdown(self) + + def isrunning(self): + return self.running + + +class TCPEchoClientTest(BaseHostTest): + + def __init__(self): + """ + Initialise test parameters. + + :return: + """ + BaseHostTest.__init__(self) + self.SERVER_IP = None # Will be determined after knowing the target IP + self.SERVER_PORT = 0 # Let TCPServer choose an arbitrary port + self.server = None + self.server_thread = None + self.target_ip = None + + @staticmethod + def find_interface_to_target_addr(target_ip): + """ + Finds IP address of the interface through which it is connected to the target. + + :return: + """ + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect((target_ip, 0)) # Target IP, Any port + ip = s.getsockname()[0] + s.close() + return ip + + def setup_tcp_server(self): + """ + sets up a TCP server for target to connect and send test data. + + :return: + """ + # !NOTE: There should mechanism to assert in the host test + if self.SERVER_IP is None: + self.log("setup_tcp_server() called before determining server IP!") + self.notify_complete(False) + + # Returning none will suppress host test from printing success code + self.server = TCPServerWrapper((self.SERVER_IP, self.SERVER_PORT), TCPEchoClientHandler) + ip, port = self.server.server_address + self.SERVER_PORT = port + self.server.allow_reuse_address = True + self.log("HOST: Listening for TCP connections: " + self.SERVER_IP + ":" + str(self.SERVER_PORT)) + self.server_thread = Thread(target=TCPEchoClientTest.server_thread_func, args=(self,)) + self.server_thread.start() + + @staticmethod + def server_thread_func(this): + """ + Thread function to run TCP server forever. + + :param this: + :return: + """ + this.server.serve_forever() + + @event_callback("target_ip") + def _callback_target_ip(self, key, value, timestamp): + """ + Callback to handle reception of target's IP address. + + :param key: + :param value: + :param timestamp: + :return: + """ + self.target_ip = value + self.SERVER_IP = self.find_interface_to_target_addr(self.target_ip) + self.setup_tcp_server() + + @event_callback("host_ip") + def _callback_host_ip(self, key, value, timestamp): + """ + Callback for request for host IP Addr + + """ + self.send_kv("host_ip", self.SERVER_IP) + + @event_callback("host_port") + def _callback_host_port(self, key, value, timestamp): + """ + Callback for request for host port + """ + self.send_kv("host_port", self.SERVER_PORT) + + def teardown(self): + if self.server: + self.server.shutdown() + self.server_thread.join() diff --git a/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/host_tests/udp_echo_client.py b/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/host_tests/udp_echo_client.py new file mode 100644 index 00000000000..a2882c6aa20 --- /dev/null +++ b/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/host_tests/udp_echo_client.py @@ -0,0 +1,125 @@ +""" +mbed SDK +Copyright (c) 2011-2013 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. +""" + +import sys +import socket +from sys import stdout +from threading import Thread +from SocketServer import BaseRequestHandler, UDPServer +from mbed_host_tests import BaseHostTest, event_callback + + +class UDPEchoClientHandler(BaseRequestHandler): + def handle(self): + """ UDP packet handler. Echoes data back to sender's address. + """ + data, sock = self.request + print ('HOST: UDPEchoClientHandler: Rx: \n%s\n' % data) + sock.sendto(data, self.client_address) + + +class UDPEchoClientTest(BaseHostTest): + + def __init__(self): + """ + Initialise test parameters. + + :return: + """ + BaseHostTest.__init__(self) + self.SERVER_IP = None # Will be determined after knowing the target IP + self.SERVER_PORT = 0 # Let TCPServer choose an arbitrary port + self.server = None + self.server_thread = None + self.target_ip = None + + @staticmethod + def find_interface_to_target_addr(target_ip): + """ + Finds IP address of the interface through which it is connected to the target. + + :return: + """ + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect((target_ip, 0)) # Target IP, Any port + ip = s.getsockname()[0] + s.close() + return ip + + def setup_udp_server(self): + """ + sets up a UDP server for target to connect and send test data. + + :return: + """ + # !NOTE: There should mechanism to assert in the host test + if self.SERVER_IP is None: + self.log("setup_udp_server() called before determining server IP!") + self.notify_complete(False) + + # Returning none will suppress host test from printing success code + self.server = UDPServer((self.SERVER_IP, self.SERVER_PORT), UDPEchoClientHandler) + ip, port = self.server.server_address + self.SERVER_PORT = port + self.server.allow_reuse_address = True + self.log("HOST: Listening for UDP packets: " + self.SERVER_IP + ":" + str(self.SERVER_PORT)) + self.server_thread = Thread(target=UDPEchoClientTest.server_thread_func, args=(self,)) + self.server_thread.start() + + @staticmethod + def server_thread_func(this): + """ + Thread function to run TCP server forever. + + :param this: + :return: + """ + this.server.serve_forever() + + @event_callback("target_ip") + def _callback_target_ip(self, key, value, timestamp): + """ + Callback to handle reception of target's IP address. + + :param key: + :param value: + :param timestamp: + :return: + """ + self.target_ip = value + self.SERVER_IP = self.find_interface_to_target_addr(self.target_ip) + self.setup_udp_server() + + @event_callback("host_ip") + def _callback_host_ip(self, key, value, timestamp): + """ + Callback for request for host IP Addr + + """ + self.send_kv("host_ip", self.SERVER_IP) + + @event_callback("host_port") + def _callback_host_port(self, key, value, timestamp): + """ + Callback for request for host port + """ + self.send_kv("host_port", self.SERVER_PORT) + + def teardown(self): + if self.server: + self.server.shutdown() + self.server_thread.join() diff --git a/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/nist_internet_time_service/main.cpp b/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/nist_internet_time_service/main.cpp new file mode 100644 index 00000000000..ae0639e3f79 --- /dev/null +++ b/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/nist_internet_time_service/main.cpp @@ -0,0 +1,65 @@ +#if !FEATURE_IPV4 + #error [NOT_SUPPORTED] IPV4 not supported for this target +#endif + +#include "mbed.h" +#include "EthernetInterface.h" +#include "UDPSocket.h" +#include "greentea-client/test_env.h" + +namespace { + //const char *HTTP_SERVER_NAME = "utcnist.colorado.edu"; + const char *HTTP_SERVER_NAME = "pool.ntp.org"; + const int HTTP_SERVER_PORT = 123; +} + + +int main() { + GREENTEA_SETUP(20, "default_auto"); + + bool result = false; + const time_t TIME1970 = 2208988800L; + int ntp_values[12] = {0}; + + EthernetInterface eth; + //eth.init(); //Use DHCP + eth.connect(); + printf("UDP client IP Address is %s\n", eth.get_ip_address()); + + UDPSocket sock; + sock.open(ð); + + SocketAddress nist(ð, HTTP_SERVER_NAME, HTTP_SERVER_PORT); + + printf("UDP: NIST server %s address: %s on port %d\r\n", HTTP_SERVER_NAME, nist.get_ip_address(), nist.get_port()); + + memset(ntp_values, 0x00, sizeof(ntp_values)); + ntp_values[0] = '\x1b'; + + int ret_send = sock.sendto(nist, (void*)ntp_values, sizeof(ntp_values)); + printf("UDP: Sent %d Bytes to NTP server \n", ret_send); + + const int n = sock.recvfrom(&nist, (void*)ntp_values, sizeof(ntp_values)); + + printf("UDP: Recved from NTP server %d Bytes \n", n); + + if (n > 0 ) { + result = true; + + printf("UDP: Values returned by NTP server: \n"); + for (size_t i=0; i < sizeof(ntp_values) / sizeof(ntp_values[0]); ++i) { + printf("\t[%02d] 0x%X", i, ntohl(ntp_values[i])); + + if (i == 10) { + time_t timestamp = ntohl(ntp_values[i]) - TIME1970; + printf("\tNTP timestamp is %s", ctime(×tamp)); + } else { + printf("\n"); + } + } + } + + sock.close(); + eth.disconnect(); + GREENTEA_TESTSUITE_RESULT(result); +} diff --git a/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/tcp_client_echo/main.cpp b/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/tcp_client_echo/main.cpp new file mode 100644 index 00000000000..788c77f3775 --- /dev/null +++ b/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/tcp_client_echo/main.cpp @@ -0,0 +1,77 @@ +#if !FEATURE_IPV4 + #error [NOT_SUPPORTED] IPV4 not supported for this target +#endif + +#include "mbed.h" +#include "EthernetInterface.h" +#include "TCPSocket.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" + + +#ifndef MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE +#define MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE 256 +#endif + +namespace { + char tx_buffer[MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE] = {0}; + char rx_buffer[MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE] = {0}; + const char ASCII_MAX = '~' - ' '; +} + +void prep_buffer(char *tx_buffer, size_t tx_size) { + for (size_t i=0; i +#include "mbed.h" +#include "EthernetInterface.h" +#include "TCPSocket.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" + +namespace { + // Test connection information + const char *HTTP_SERVER_NAME = "developer.mbed.org"; + const char *HTTP_SERVER_FILE_PATH = "/media/uploads/mbed_official/hello.txt"; + const int HTTP_SERVER_PORT = 80; +#if defined(TARGET_VK_RZ_A1H) + const int RECV_BUFFER_SIZE = 300; +#else + const int RECV_BUFFER_SIZE = 512; +#endif + // Test related data + const char *HTTP_OK_STR = "200 OK"; + const char *HTTP_HELLO_STR = "Hello world!"; + + // Test buffers + char buffer[RECV_BUFFER_SIZE] = {0}; +} + +bool find_substring(const char *first, const char *last, const char *s_first, const char *s_last) { + const char *f = std::search(first, last, s_first, s_last); + return (f != last); +} + +int main() { + GREENTEA_SETUP(20, "default_auto"); + + bool result = true; + EthernetInterface eth; + //eth.init(); //Use DHCP + eth.connect(); + printf("TCP client IP Address is %s\r\n", eth.get_ip_address()); + + TCPSocket sock(ð); + if (sock.connect(HTTP_SERVER_NAME, HTTP_SERVER_PORT) == 0) { + printf("HTTP: Connected to %s:%d\r\n", HTTP_SERVER_NAME, HTTP_SERVER_PORT); + + // We are constructing GET command like this: + // GET http://developer.mbed.org/media/uploads/mbed_official/hello.txt HTTP/1.0\n\n + strcpy(buffer, "GET http://"); + strcat(buffer, HTTP_SERVER_NAME); + strcat(buffer, HTTP_SERVER_FILE_PATH); + strcat(buffer, " HTTP/1.0\n\n"); + // Send GET command + sock.send(buffer, strlen(buffer)); + + // Server will respond with HTTP GET's success code + const int ret = sock.recv(buffer, sizeof(buffer) - 1); + buffer[ret] = '\0'; + + // Find 200 OK HTTP status in reply + bool found_200_ok = find_substring(buffer, buffer + ret, HTTP_OK_STR, HTTP_OK_STR + strlen(HTTP_OK_STR)); + // Find "Hello World!" string in reply + bool found_hello = find_substring(buffer, buffer + ret, HTTP_HELLO_STR, HTTP_HELLO_STR + strlen(HTTP_HELLO_STR)); + + TEST_ASSERT_TRUE(found_200_ok); + TEST_ASSERT_TRUE(found_hello); + + if (!found_200_ok) result = false; + if (!found_hello) result = false; + + printf("HTTP: Received %d chars from server\r\n", ret); + printf("HTTP: Received 200 OK status ... %s\r\n", found_200_ok ? "[OK]" : "[FAIL]"); + printf("HTTP: Received '%s' status ... %s\r\n", HTTP_HELLO_STR, found_hello ? "[OK]" : "[FAIL]"); + printf("HTTP: Received massage:\r\n\r\n"); + printf("%s", buffer); + } + + sock.close(); + eth.disconnect(); + GREENTEA_TESTSUITE_RESULT(result); +} diff --git a/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/udp_echo_client/main.cpp b/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/udp_echo_client/main.cpp new file mode 100644 index 00000000000..77911f20b2a --- /dev/null +++ b/features/net/FEATURE_IPV4/TESTS/mbedmicro-net/udp_echo_client/main.cpp @@ -0,0 +1,74 @@ +#if !FEATURE_IPV4 + #error [NOT_SUPPORTED] IPV4 not supported for this target +#endif + +#include "mbed.h" +#include "EthernetInterface.h" +#include "UDPSocket.h" +#include "greentea-client/test_env.h" + +#ifndef MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE +#define MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE 256 +#endif + +namespace { + char tx_buffer[MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE] = {0}; + char rx_buffer[MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE] = {0}; + const char ASCII_MAX = '~' - ' '; + const int ECHO_LOOPS = 16; +} + +void prep_buffer(char *tx_buffer, size_t tx_size) { + for (size_t i=0; i