diff --git a/module.json b/module.json index 5cd39a3..b3f1b7d 100644 --- a/module.json +++ b/module.json @@ -21,7 +21,9 @@ "minar": "^1.0.0" }, "testDependencies": { - "mbed-drivers": "~0.11.1" + "mbed-drivers": "~0.12.1", + "unity": "^2.0.1", + "greentea-client": "^0.1.4" }, "scripts": { "testReporter": [ diff --git a/test/echo-tcp-client/main.cpp b/test/echo-tcp-client/main.cpp index ea13b6e..50277a1 100644 --- a/test/echo-tcp-client/main.cpp +++ b/test/echo-tcp-client/main.cpp @@ -15,22 +15,16 @@ * limitations under the License. */ #include "mbed-drivers/mbed.h" -#include "mbed-drivers/test_env.h" #include "sockets/TCPStream.h" -#include "sal/test/ctest_env.h" #include "sal-stack-lwip/lwipv4_init.h" #include "sal-iface-eth/EthernetInterface.h" #include "minar/minar.h" #include "core-util/FunctionPointer.h" +#include "greentea-client/test_env.h" +#include "utest/utest.h" +#include "unity/unity.h" -struct s_ip_address { - int ip_1; - int ip_2; - int ip_3; - int ip_4; -}; - - +using namespace utest::v1; using namespace mbed::Sockets::v0; EthernetInterface eth; @@ -41,6 +35,7 @@ void terminate(bool status, TCPEchoClient* client); char out_buffer[] = "Hello World\n"; char buffer[256]; +char out_success[] = "{{success}}\n{{end}}\n"; TCPEchoClient *client; int port; @@ -51,22 +46,28 @@ class TCPEchoClient { { _stream.setOnError(TCPStream::ErrorHandler_t(this, &TCPEchoClient::onError)); } + ~TCPEchoClient(){ + if (_stream.isConnected()) + _stream.close(); + } void onError(Socket *s, socket_error_t err) { (void) s; + TEST_ASSERT_NOT_EQUAL_MESSAGE(SOCKET_ERROR_NONE, err, socket_strerror(err)); printf("MBED: Socket Error: %s (%d)\r\n", socket_strerror(err), err); _done = true; - minar::Scheduler::postCallback(fpterminate_t(terminate).bind(TEST_RESULT(),this)); + minar::Scheduler::postCallback(fpterminate_t(terminate).bind(false,this)); } void start_test(char * host_addr, uint16_t port) { + printf("Trying to resolve address %s" NL, host_addr); _port = port; _done = false; _disconnected = true; - socket_error_t err = _stream.resolve(host_addr,TCPStream::DNSHandler_t(this, &TCPEchoClient::onDNS)); - if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { - printf("MBED: TCPClient unable to connect to %s:%d" NL, buffer, port); - onError(&_stream, err); - } + socket_error_t err = _stream.open(SOCKET_AF_INET4); + TEST_ASSERT_EQUAL_MESSAGE(SOCKET_ERROR_NONE, err, "MBED: Failed to open socket!"); + + err = _stream.resolve(host_addr,TCPStream::DNSHandler_t(this, &TCPEchoClient::onDNS)); + TEST_ASSERT_EQUAL_MESSAGE(SOCKET_ERROR_NONE, err, "MBED: Failed to resolve host address!"); } void onDNS(Socket *s, struct socket_addr sa, const char* domain) { @@ -76,65 +77,54 @@ class TCPEchoClient { /* Open the socket */ _resolvedAddr.fmtIPv6(buffer, sizeof(buffer)); printf("MBED: Resolved %s to %s\r\n", domain, buffer); - socket_error_t err = _stream.open(SOCKET_AF_INET4); - TEST_EQ(err, SOCKET_ERROR_NONE); /* Register the read handler */ _stream.setOnReadable(TCPStream::ReadableHandler_t(this, &TCPEchoClient::onRx)); _stream.setOnSent(TCPStream::SentHandler_t(this, &TCPEchoClient::onSent)); _stream.setOnDisconnect(TCPStream::DisconnectHandler_t(this, &TCPEchoClient::onDisconnect)); /* Send the query packet to the remote host */ - err = _stream.connect(_resolvedAddr, _port, TCPStream::ConnectHandler_t(this,&TCPEchoClient::onConnect)); - if(!TEST_EQ(err, SOCKET_ERROR_NONE)) { - printf("MBED: Expected %d, got %d (%s)\r\n", SOCKET_ERROR_NONE, err, socket_strerror(err)); - onError(&_stream, err); - } + socket_error_t err = _stream.connect(_resolvedAddr, _port, TCPStream::ConnectHandler_t(this,&TCPEchoClient::onConnect)); + TEST_ASSERT_EQUAL_MESSAGE(SOCKET_ERROR_NONE, err, "MBED: Failed to connect host server!"); } void onConnect(TCPStream *s) { (void) s; _disconnected = false; - _unacked += sizeof(out_buffer) - 1; + _unacked = sizeof(out_buffer) - 1; + printf ("MBED: Sending (%d bytes) to host: %s" NL, _unacked, out_buffer); socket_error_t err = _stream.send(out_buffer, sizeof(out_buffer) - 1); - - TEST_EQ(err, SOCKET_ERROR_NONE); + TEST_ASSERT_EQUAL_MESSAGE(SOCKET_ERROR_NONE, err, "MBED: TCPClient failed to send data!"); } void onRx(Socket* s) { (void) s; size_t n = sizeof(buffer)-1; socket_error_t err = _stream.recv(buffer, &n); - TEST_EQ(err, SOCKET_ERROR_NONE); + TEST_ASSERT_EQUAL_MESSAGE(SOCKET_ERROR_NONE, err, "MBED: TCPClient failed to recv data!"); + buffer[n] = 0; - char out_success[] = "{{success}}\n{{end}}\n"; - char out_failure[] = "{{failure}}\n{{end}}\n"; - int rc; - if (n > 0) + printf ("MBED: Rx (%d bytes) from host: %s" NL, n, buffer); + if (!_done && n > 0) { - buffer[n] = '\0'; - printf("%s\r\n", buffer); - rc = strncmp(out_buffer, buffer, sizeof(out_buffer) - 1); - if (TEST_EQ(rc, 0)) { - _unacked += sizeof(out_success) - 1; - err = _stream.send(out_success, sizeof(out_success) - 1); - _done = true; - TEST_EQ(err, SOCKET_ERROR_NONE); - minar::Scheduler::postCallback(fpterminate_t(terminate).bind(TEST_RESULT(),this)); - } + TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(out_buffer, buffer, n, "MBED: TCPClient round trip data validation failed!"); + + _unacked += sizeof(out_success) - 1; + printf ("MBED: Sending (%d bytes) to host: %s" NL, _unacked, out_success); + err = _stream.send(out_success, sizeof(out_success) - 1); + _done = true; + TEST_ASSERT_EQUAL_MESSAGE(SOCKET_ERROR_NONE, err, "MBED: TCPClient failed to send data!"); } if (!_done) { - _unacked += sizeof(out_failure) - 1; - err = _stream.send(out_failure, sizeof(out_failure) - 1); - _done = true; - TEST_EQ(err, SOCKET_ERROR_NONE); - minar::Scheduler::postCallback(fpterminate_t(terminate).bind(TEST_RESULT(),this)); + // Failed to validate rceived data. Terminating... + minar::Scheduler::postCallback(fpterminate_t(terminate).bind(false,this)); } } void onSent(Socket *s, uint16_t nbytes) { (void) s; _unacked -= nbytes; + printf ("MBED: Sent %d bytes" NL, nbytes); if (_done && (_unacked == 0)) { - _stream.close(); + minar::Scheduler::postCallback(fpterminate_t(terminate).bind(true,this)); } } void onDisconnect(TCPStream *s) @@ -151,40 +141,72 @@ class TCPEchoClient { volatile size_t _unacked; }; -void terminate(bool status, TCPEchoClient* client) +void terminate(bool status, TCPEchoClient* ) { - delete client; - eth.disconnect(); - MBED_HOSTTEST_RESULT(status); + if (client) { + printf("MBED: Test finished!"); + delete client; + client = NULL; + eth.disconnect(); + TEST_ASSERT_TRUE_MESSAGE(status, "MBED: test failed!"); + Harness::validate_callback(); + } } - -void app_start(int argc, char *argv[]) { - (void)argc; - (void)argv; - MBED_HOSTTEST_TIMEOUT(20); - MBED_HOSTTEST_SELECT(tcpecho_client_auto); - MBED_HOSTTEST_DESCRIPTION(TCP echo client); - MBED_HOSTTEST_START("NET_4"); +control_t test_echo_tcp_client() +{ socket_error_t err = lwipv4_socket_init(); - TEST_EQ(err, SOCKET_ERROR_NONE); + TEST_ASSERT_EQUAL(SOCKET_ERROR_NONE, err); + + TEST_ASSERT_EQUAL(0, eth.init()); //Use DHCP + eth.connect(); + printf("TCPClient IP Address is %s" NL, eth.getIPAddress()); + greentea_send_kv("target_ip", eth.getIPAddress()); + memset(buffer, 0, sizeof(buffer)); port = 0; - s_ip_address ip_addr = {0, 0, 0, 0}; + printf("TCPClient waiting for server IP and port..." NL); - scanf("%d.%d.%d.%d:%d", &ip_addr.ip_1, &ip_addr.ip_2, &ip_addr.ip_3, &ip_addr.ip_4, &port); - printf("Address received:%d.%d.%d.%d:%d" NL, ip_addr.ip_1, ip_addr.ip_2, ip_addr.ip_3, ip_addr.ip_4, port); - - eth.init(); //Use DHCP - eth.connect(); - - printf("TCPClient IP Address is %s" NL, eth.getIPAddress()); - sprintf(buffer, "%d.%d.%d.%d", ip_addr.ip_1, ip_addr.ip_2, ip_addr.ip_3, ip_addr.ip_4); + char recv_key[] = "host_port"; + char port_value[] = "65325"; + + greentea_send_kv("host_ip", " "); + TEST_ASSERT_NOT_EQUAL_MESSAGE(0, greentea_parse_kv(recv_key, buffer, sizeof(recv_key), sizeof(buffer)), "MBED: Failed to recv/parse key value from host test!"); + + greentea_send_kv("host_port", " "); + TEST_ASSERT_NOT_EQUAL_MESSAGE(0, greentea_parse_kv(recv_key, port_value, sizeof(recv_key), sizeof(port_value)), "MBED: Failed to recv/parse key value from host test!"); + + sscanf(port_value, "%d", &port); + + client = new TCPEchoClient(SOCKET_STACK_LWIP_IPV4); { mbed::util::FunctionPointer2 fp(client, &TCPEchoClient::start_test); minar::Scheduler::postCallback(fp.bind(buffer, port)); } + + return CaseTimeout(15000); +} + +// Cases -------------------------------------------------------------------------------------------------------------- +Case cases[] = { + Case("Test Echo TCP Client", test_echo_tcp_client), +}; + +status_t greentea_setup(const size_t number_of_cases) +{ + // Handshake with greentea + // Host test timeout should be more than target utest timeout to let target cleanup the test and send test summary. + GREENTEA_SETUP(20, "tcpecho_client_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_setup, cases); + +void app_start(int, char*[]) +{ + Harness::run(specification); } + diff --git a/test/echo-udp-client/main.cpp b/test/echo-udp-client/main.cpp index ac80cc7..1de697e 100644 --- a/test/echo-udp-client/main.cpp +++ b/test/echo-udp-client/main.cpp @@ -15,8 +15,6 @@ * limitations under the License. */ #include "mbed-drivers/mbed.h" -#include "mbed-drivers/test_env.h" -#include "sal/test/ctest_env.h" #include "sal/socket_api.h" #include #include "sockets/UDPSocket.h" @@ -24,22 +22,20 @@ #include "sal-stack-lwip/lwipv4_init.h" #include "minar/minar.h" #include "core-util/FunctionPointer.h" +#include "greentea-client/test_env.h" +#include "utest/utest.h" +#include "unity/unity.h" #define CHECK(RC, STEP) if (RC < 0) error(STEP": %d\r\n", RC) +using namespace utest::v1; using namespace mbed::Sockets::v0; + namespace { const int BUFFER_SIZE = 64; const int MAX_ECHO_LOOPS = 100; const char ASCII_MAX = '~' - ' '; - - struct s_ip_address { - int ip_1; - int ip_2; - int ip_3; - int ip_4; - }; } char char_rand() { @@ -57,7 +53,7 @@ void terminate(bool status, UDPEchoClient* client); char buffer[BUFFER_SIZE] = {0}; int port = 0; -UDPEchoClient *ec; +UDPEchoClient *client; EthernetInterface eth; class UDPEchoClient { @@ -67,20 +63,26 @@ class UDPEchoClient { { _usock.setOnError(UDPSocket::ErrorHandler_t(this, &UDPEchoClient::onError)); } + ~UDPEchoClient(){ + if (_usock.isConnected()) + _usock.close(); + } void start_test(char * host_addr, uint16_t port) { loop_ctr = 0; _port = port; - socket_error_t err = _usock.resolve(host_addr,UDPSocket::DNSHandler_t(this, &UDPEchoClient::onDNS)); - if(!TEST_EQ(err, SOCKET_ERROR_NONE)) { - printf("MBED: Failed to resolve %s\r\n", host_addr); - onError(&_usock, err); - } + + socket_error_t err = _usock.open(SOCKET_AF_INET4); + TEST_ASSERT_EQUAL_MESSAGE(SOCKET_ERROR_NONE, err, "MBED: UDPClient unable to open socket" NL); + printf("MBED: Trying to resolve address %s" NL, host_addr); + err = _usock.resolve(host_addr,UDPSocket::DNSHandler_t(this, &UDPEchoClient::onDNS)); + TEST_ASSERT_EQUAL_MESSAGE(SOCKET_ERROR_NONE, err, "MBED: UDPClient failed to resolve host server address" NL); } void onError(Socket *s, socket_error_t err) { (void) s; + TEST_ASSERT_NOT_EQUAL(SOCKET_ERROR_NONE, err); printf("MBED: Socket Error: %s (%d)\r\n", socket_strerror(err), err); - minar::Scheduler::postCallback(fpterminate_t(terminate).bind(TEST_RESULT(),this)); + minar::Scheduler::postCallback(fpterminate_t(terminate).bind(false,this)); } void onDNS(Socket *s, struct socket_addr sa, const char * domain) { @@ -92,49 +94,39 @@ class UDPEchoClient { /* TODO: add support for getting AF from addr */ socket_error_t err = _usock.open(SOCKET_AF_INET4); - TEST_EQ(err, SOCKET_ERROR_NONE); + TEST_ASSERT_EQUAL_MESSAGE(SOCKET_ERROR_NONE, err, "MBED: UDPClient failed to open socket!" NL); + /* Register the read handler */ _usock.setOnReadable(UDPSocket::ReadableHandler_t(this, &UDPEchoClient::onRx)); /* Send the query packet to the remote host */ - err = send_test(); - TEST_EQ(err, SOCKET_ERROR_NONE); + send_test(); } void onRx(Socket *s) { (void) s; unsigned int n = sizeof(buffer); socket_error_t err = _usock.recv(buffer, &n); - TEST_EQ(err, SOCKET_ERROR_NONE); + TEST_ASSERT_EQUAL_MESSAGE(SOCKET_ERROR_NONE, err, "MBED: UDPClient failed to recv data!" NL); int rc = memcmp(buffer, out_buffer, min(BUFFER_SIZE,n)); - TEST_EQ(rc, 0); + TEST_ASSERT_EQUAL_MESSAGE(0, rc, "MBED: UDPClient round trip data validation error!" NL); loop_ctr++; - if (!rc && (loop_ctr < MAX_ECHO_LOOPS)) { - err = send_test(); - TEST_EQ(err, SOCKET_ERROR_NONE); + if (loop_ctr < MAX_ECHO_LOOPS) { + send_test(); } - if ((rc == 0) && (loop_ctr >= MAX_ECHO_LOOPS)) { - if (notify_completion_str(TEST_RESULT(), buffer)) { - _usock.send_to(buffer, strlen(buffer), &_resolvedAddr, _port); - _usock.close(); - minar::Scheduler::postCallback(fpterminate_t(terminate).bind(TEST_RESULT(),this)); - } + if (loop_ctr >= MAX_ECHO_LOOPS) { + _usock.send_to(buffer, strlen(buffer), &_resolvedAddr, _port); + minar::Scheduler::postCallback(fpterminate_t(terminate).bind(true,this)); } } - bool isDone() { - return done; - } - bool isError() { - return !TEST_RESULT(); - } protected: - socket_error_t send_test() { + void send_test() { std::generate(out_buffer, out_buffer + BUFFER_SIZE, char_rand); socket_error_t err = _usock.send_to(out_buffer, sizeof(BUFFER_SIZE), &_resolvedAddr, _port); - return err; + TEST_ASSERT_EQUAL_MESSAGE(SOCKET_ERROR_NONE, err, "MBED: UDPClient failed to send data!" NL); } protected: UDPSocket _usock; @@ -146,44 +138,82 @@ class UDPEchoClient { volatile bool done; }; -void terminate(bool status, UDPEchoClient* client) +void terminate(bool status, UDPEchoClient* ) { - delete client; - eth.disconnect(); - MBED_HOSTTEST_RESULT(status); + if (client) { + printf("MBED: Test finished!"); + delete client; + client = NULL; + eth.disconnect(); + TEST_ASSERT_TRUE_MESSAGE(status, "MBED: test failed!"); + Harness::validate_callback(); + } } -void app_start(int argc, char *argv[]) { - (void) argc; - (void) argv; - MBED_HOSTTEST_TIMEOUT(20); - MBED_HOSTTEST_SELECT(udpecho_client_auto); - MBED_HOSTTEST_DESCRIPTION(UDP echo client); - MBED_HOSTTEST_START("NET_6"); + +control_t test_echo_udp_client() +{ socket_error_t err = lwipv4_socket_init(); - TEST_EQ(err, SOCKET_ERROR_NONE); + TEST_ASSERT_EQUAL_MESSAGE(SOCKET_ERROR_NONE, err, "Failed to init LWIPv4 socket!"); + + printf("MBED: Initializing ethernet connection." NL); + //Use DHCP + TEST_ASSERT_EQUAL_MESSAGE(0, eth.init(), "Failed to init LWIPv4 socket!"); + eth.connect(); + + printf("MBED: IP Address is %s" NL, eth.getIPAddress()); + greentea_send_kv("target_ip", eth.getIPAddress()); + + memset(buffer, 0, sizeof(buffer)); + port = 0; + + printf("UDPClient waiting for server IP and port..." NL); + char recv_key[] = "host_port"; + char port_value[] = "65325"; + + greentea_send_kv("host_ip", " "); + TEST_ASSERT_NOT_EQUAL_MESSAGE(0, greentea_parse_kv(recv_key, buffer, sizeof(recv_key), sizeof(buffer)), "MBED: Failed to recv/parse key value from host!"); + + greentea_send_kv("host_port", " "); + TEST_ASSERT_NOT_EQUAL_MESSAGE(0, greentea_parse_kv(recv_key, port_value, sizeof(recv_key), sizeof(port_value)), "MBED: Failed to recv/parse key value from host!"); + + sscanf(port_value, "%d", &port); + + + client = new UDPEchoClient(SOCKET_STACK_LWIP_IPV4); - s_ip_address ip_addr = {0, 0, 0, 0}; + { + mbed::util::FunctionPointer2 fp(client, &UDPEchoClient::start_test); + minar::Scheduler::postCallback(fp.bind(buffer, port)); + } + return CaseTimeout(25000); +} - printf("MBED: UDPCllient waiting for server IP and port...\r\n"); - scanf("%d.%d.%d.%d:%d", &ip_addr.ip_1, &ip_addr.ip_2, &ip_addr.ip_3, &ip_addr.ip_4, &port); - printf("MBED: Address received: %d.%d.%d.%d:%d\r\n", ip_addr.ip_1, ip_addr.ip_2, ip_addr.ip_3, ip_addr.ip_4, port); - int rc = eth.init(); //Use DHCP - CHECK(rc, "eth init"); +// Cases -------------------------------------------------------------------------------------------------------------- +Case cases[] = { + Case("Test Echo UDP Client", test_echo_udp_client), +}; - rc = eth.connect(); - CHECK(rc, "connect"); - printf("IP: %s\n", eth.getIPAddress()); +status_t greentea_setup(const size_t number_of_cases) +{ + // Handshake with greentea + // Host test timeout should be more than target utest timeout to let target cleanup the test and send test summary. + GREENTEA_SETUP(30, "udpecho_client_auto"); + return greentea_test_setup_handler(number_of_cases); +} - ec = new UDPEchoClient(SOCKET_STACK_LWIP_IPV4); +void greentea_teardown(const size_t passed, const size_t failed, const failure_t failure) +{ + greentea_test_teardown_handler(passed, failed, failure); + GREENTEA_TESTSUITE_RESULT(failed == 0); +} - printf("MBED: UDPClient IP Address is %s\r\n", eth.getIPAddress()); - sprintf(buffer, "%d.%d.%d.%d", ip_addr.ip_1, ip_addr.ip_2, ip_addr.ip_3, ip_addr.ip_4); +Specification specification(greentea_setup, cases, greentea_teardown); - { - mbed::util::FunctionPointer2 fp(ec, &UDPEchoClient::start_test); - minar::Scheduler::postCallback(fp.bind(buffer, port)); - } +void app_start(int, char*[]) +{ + Harness::run(specification); } + diff --git a/test/host_tests/tcpecho_client_auto.py b/test/host_tests/tcpecho_client_auto.py new file mode 100644 index 0000000..c1eb3ab --- /dev/null +++ b/test/host_tests/tcpecho_client_auto.py @@ -0,0 +1,197 @@ +# 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 + print ('HOST: TCPEchoClient_Handler: \n%s\n' % data) + + # If client finishes, sit on recv and terminate + # after client closes connection. + if '{{end}}' in data: continue + + # echo data back to the client + self.send(data) + except Exception as e: + print ('HOST: TCPEchoClient_Handler: %s' % str(e)) + break + print 'Connection finshed' + + 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: + """ + super(BaseHostTest, self).__init__() + 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/test/host_tests/udpecho_client_auto.py b/test/host_tests/udpecho_client_auto.py new file mode 100644 index 0000000..b6e637f --- /dev/null +++ b/test/host_tests/udpecho_client_auto.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 data + sock.sendto(data, self.client_address) + + +class UDPEchoClientTest(BaseHostTest): + + def __init__(self): + """ + Initialise test parameters. + + :return: + """ + super(BaseHostTest, self).__init__() + 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()