Merge pull request #3265 from geky/lwip-tests

lwip - Add more network tests
pull/3547/head
Sam Grove 2017-01-09 10:08:17 -06:00 committed by GitHub
commit 6af7b2916f
16 changed files with 1690 additions and 101 deletions

View File

@ -0,0 +1,67 @@
#if !FEATURE_LWIP
#error [NOT_SUPPORTED] LWIP not supported for this target
#endif
#if DEVICE_EMAC
#error [NOT_SUPPORTED] Not supported for WiFi targets
#endif
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "EthernetInterface.h"
using namespace utest::v1;
// Bringing the network up and down
template <int COUNT>
void test_bring_up_down() {
EthernetInterface eth;
for (int i = 0; i < COUNT; i++) {
int err = eth.connect();
TEST_ASSERT_EQUAL(0, err);
printf("MBED: IP Address %s\r\n", eth.get_ip_address());
printf("MBED: Netmask %s\r\n", eth.get_netmask());
printf("MBED: Gateway %s\r\n", eth.get_gateway());
TEST_ASSERT(eth.get_ip_address());
TEST_ASSERT(eth.get_netmask());
TEST_ASSERT(eth.get_gateway());
UDPSocket udp;
err = udp.open(&eth);
TEST_ASSERT_EQUAL(0, err);
err = udp.close();
TEST_ASSERT_EQUAL(0, err);
TCPSocket tcp;
err = tcp.open(&eth);
TEST_ASSERT_EQUAL(0, err);
err = tcp.close();
TEST_ASSERT_EQUAL(0, err);
err = eth.disconnect();
TEST_ASSERT_EQUAL(0, err);
}
}
// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases) {
GREENTEA_SETUP(60, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("Testing bringing the network up and down", test_bring_up_down<1>),
Case("Testing bringing the network up and down twice", test_bring_up_down<2>),
};
Specification specification(test_setup, cases);
int main() {
return !Harness::run(specification);
}

View File

@ -1,3 +1,6 @@
#if !FEATURE_LWIP
#error [NOT_SUPPORTED] LWIP not supported for this target
#endif
#if DEVICE_EMAC
#error [NOT_SUPPORTED] Not supported for WiFi targets
#endif

View File

@ -33,24 +33,18 @@ class TCPEchoClientHandler(BaseRequestHandler):
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):
"""

View File

@ -28,7 +28,6 @@ class UDPEchoClientHandler(BaseRequestHandler):
""" 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)

View File

@ -0,0 +1,135 @@
"""
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
import json
import random
import itertools
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. Responds with multiple simultaneous packets
"""
data, sock = self.request
pattern = [ord(d) << 4 for d in data]
# Each byte in request indicates size of packet to recieve
# Each packet size is shifted over by 4 to fit in a byte, which
# avoids any issues with endianess or decoding
for packet in pattern:
data = [random.randint(0, 255) for _ in range(packet-1)]
data.append(reduce(lambda a,b: a^b, data))
data = ''.join(map(chr, 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()

View File

@ -1,79 +0,0 @@
#if !FEATURE_LWIP
#error [NOT_SUPPORTED] LWIP not supported for this target
#endif
#if DEVICE_EMAC
#error [NOT_SUPPORTED] Not supported for WiFi targets
#endif
#include "mbed.h"
#include "EthernetInterface.h"
#include "UDPSocket.h"
#include "greentea-client/test_env.h"
namespace {
const char *HTTP_SERVER_NAME = "pool.ntp.org";
const int HTTP_SERVER_PORT = 123;
}
int main() {
GREENTEA_SETUP(60, "default_auto");
bool result = false;
const time_t TIME1970 = 2208988800L;
int ntp_send_values[12] = {0};
int ntp_recv_values[12] = {0};
EthernetInterface eth;
eth.connect();
printf("UDP client IP Address is %s\n", eth.get_ip_address());
UDPSocket sock;
sock.open(&eth);
sock.set_timeout(15000);
SocketAddress nist(&eth, 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_send_values, 0x00, sizeof(ntp_send_values));
ntp_send_values[0] = '\x1b';
while(1) {
memset(ntp_recv_values, 0x00, sizeof(ntp_recv_values));
int ret_send = sock.sendto(nist, (void*)ntp_send_values, sizeof(ntp_send_values));
printf("UDP: Sent %d Bytes to NTP server \n", ret_send);
SocketAddress source;
const int n = sock.recvfrom(&source, (void*)ntp_recv_values, sizeof(ntp_recv_values));
printf("UDP: Recved from NTP server %d Bytes \n", n);
if (n > 0 && strcmp(source.get_ip_address(), nist.get_ip_address()) == 0) {
result = true;
printf("UDP: Values returned by NTP server: \n");
for (size_t i=0; i < sizeof(ntp_recv_values) / sizeof(ntp_recv_values[0]); ++i) {
printf("\t[%02d] 0x%X", i, ntohl(ntp_recv_values[i]));
if (i == 10) {
time_t timestamp = ntohl(ntp_recv_values[i]) - TIME1970;
printf("\tNTP timestamp is %s", ctime(&timestamp));
} else {
printf("\n");
}
}
break;
}
printf("Failed to receive data, retrying in 5 seconds...\n");
wait(5);
}
sock.close();
eth.disconnect();
GREENTEA_TESTSUITE_RESULT(result);
}

View File

@ -1,7 +1,6 @@
#if !FEATURE_LWIP
#error [NOT_SUPPORTED] LWIP not supported for this target
#endif
#if DEVICE_EMAC
#error [NOT_SUPPORTED] Not supported for WiFi targets
#endif
@ -30,7 +29,7 @@ void prep_buffer(char *tx_buffer, size_t tx_size) {
}
int main() {
GREENTEA_SETUP(20, "tcp_echo_client");
GREENTEA_SETUP(20, "tcp_echo");
EthernetInterface eth;
eth.connect();

View File

@ -0,0 +1,124 @@
#if !FEATURE_LWIP
#error [NOT_SUPPORTED] LWIP not supported for this target
#endif
#if DEVICE_EMAC
#error [NOT_SUPPORTED] Not supported for WiFi targets
#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 64
#endif
#ifndef MBED_CFG_TCP_CLIENT_ECHO_THREADS
#define MBED_CFG_TCP_CLIENT_ECHO_THREADS 3
#endif
EthernetInterface net;
SocketAddress tcp_addr;
Mutex iomutex;
void prep_buffer(char *tx_buffer, size_t tx_size) {
for (size_t i=0; i<tx_size; ++i) {
tx_buffer[i] = (rand() % 10) + '0';
}
}
// Each echo class is in charge of one parallel transaction
class Echo {
private:
char tx_buffer[MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE];
char rx_buffer[MBED_CFG_TCP_CLIENT_ECHO_BUFFER_SIZE];
TCPSocket sock;
Thread thread;
public:
void start() {
osStatus status = thread.start(callback(this, &Echo::echo));
TEST_ASSERT_EQUAL(osOK, status);
}
void join() {
osStatus status = thread.join();
TEST_ASSERT_EQUAL(osOK, status);
}
void echo() {
int err = sock.open(&net);
TEST_ASSERT_EQUAL(0, err);
err = sock.connect(tcp_addr);
TEST_ASSERT_EQUAL(0, err);
iomutex.lock();
printf("HTTP: Connected to %s:%d\r\n",
tcp_addr.get_ip_address(), tcp_addr.get_port());
printf("tx_buffer buffer size: %u\r\n", sizeof(tx_buffer));
printf("rx_buffer buffer size: %u\r\n", sizeof(rx_buffer));
iomutex.unlock();
prep_buffer(tx_buffer, sizeof(tx_buffer));
sock.send(tx_buffer, sizeof(tx_buffer));
// Server will respond with HTTP GET's success code
const int ret = sock.recv(rx_buffer, sizeof(rx_buffer));
bool result = !memcmp(tx_buffer, rx_buffer, sizeof(tx_buffer));
TEST_ASSERT_EQUAL(ret, sizeof(rx_buffer));
TEST_ASSERT_EQUAL(true, result);
err = sock.close();
TEST_ASSERT_EQUAL(0, err);
}
};
Echo echoers[MBED_CFG_TCP_CLIENT_ECHO_THREADS];
int main() {
GREENTEA_SETUP(20, "tcp_echo");
int err = net.connect();
TEST_ASSERT_EQUAL(0, err);
printf("MBED: TCPClient IP address is '%s'\n", net.get_ip_address());
printf("MBED: TCPClient waiting for server IP and port...\n");
greentea_send_kv("target_ip", net.get_ip_address());
char recv_key[] = "host_port";
char ipbuf[60] = {0};
char portbuf[16] = {0};
unsigned int port = 0;
greentea_send_kv("host_ip", " ");
greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf));
greentea_send_kv("host_port", " ");
greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf));
sscanf(portbuf, "%u", &port);
printf("MBED: Server IP address received: %s:%d \n", ipbuf, port);
tcp_addr.set_ip_address(ipbuf);
tcp_addr.set_port(port);
// Startup echo threads in parallel
for (int i = 0; i < MBED_CFG_TCP_CLIENT_ECHO_THREADS; i++) {
echoers[i].start();
}
for (int i = 0; i < MBED_CFG_TCP_CLIENT_ECHO_THREADS; i++) {
echoers[i].join();
}
net.disconnect();
GREENTEA_TESTSUITE_RESULT(true);
}

View File

@ -1,7 +1,6 @@
#if !FEATURE_LWIP
#error [NOT_SUPPORTED] LWIP not supported for this target
#endif
#if DEVICE_EMAC
#error [NOT_SUPPORTED] Not supported for WiFi targets
#endif

View File

@ -0,0 +1,227 @@
#if !FEATURE_LWIP
#error [NOT_SUPPORTED] LWIP not supported for this target
#endif
#if DEVICE_EMAC
#error [NOT_SUPPORTED] Not supported for WiFi targets
#endif
#ifndef MBED_EXTENDED_TESTS
#error [NOT_SUPPORTED] Pressure tests are not supported by default
#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_PACKET_PRESSURE_MIN
#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN 64
#endif
#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX
#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX 0x80000
#endif
#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_SEED
#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_SEED 0x6d626564
#endif
#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG
#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG false
#endif
// Simple xorshift pseudorandom number generator
class RandSeq {
private:
uint32_t x;
uint32_t y;
static const int A = 15;
static const int B = 18;
static const int C = 11;
public:
RandSeq(uint32_t seed=MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_SEED)
: x(seed), y(seed) {}
uint32_t next(void) {
x ^= x << A;
x ^= x >> B;
x ^= y ^ (y >> C);
return x + y;
}
void skip(size_t size) {
for (size_t i = 0; i < size; i++) {
next();
}
}
void buffer(uint8_t *buffer, size_t size) {
RandSeq lookahead = *this;
for (size_t i = 0; i < size; i++) {
buffer[i] = lookahead.next() & 0xff;
}
}
int cmp(uint8_t *buffer, size_t size) {
RandSeq lookahead = *this;
for (size_t i = 0; i < size; i++) {
int diff = buffer[i] - (lookahead.next() & 0xff);
if (diff != 0) {
return diff;
}
}
return 0;
}
};
// Shared buffer for network transactions
uint8_t *buffer;
size_t buffer_size;
// Tries to get the biggest buffer possible on the device. Exponentially
// grows a buffer until heap runs out of space, and uses half to leave
// space for the rest of the program
void generate_buffer(uint8_t **buffer, size_t *size, size_t min, size_t max) {
size_t i = min;
while (i < max) {
void *b = malloc(i);
if (!b) {
i /= 4;
if (i < min) {
i = min;
}
break;
}
free(b);
i *= 2;
}
*buffer = (uint8_t *)malloc(i);
*size = i;
TEST_ASSERT(buffer);
}
int main() {
GREENTEA_SETUP(60, "tcp_echo");
generate_buffer(&buffer, &buffer_size,
MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN,
MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX);
printf("MBED: Generated buffer %d\r\n", buffer_size);
EthernetInterface eth;
int err = eth.connect();
TEST_ASSERT_EQUAL(0, err);
printf("MBED: TCPClient IP address is '%s'\n", eth.get_ip_address());
printf("MBED: TCPClient waiting for server IP and port...\n");
greentea_send_kv("target_ip", eth.get_ip_address());
bool result = true;
char recv_key[] = "host_port";
char ipbuf[60] = {0};
char portbuf[16] = {0};
unsigned int port = 0;
greentea_send_kv("host_ip", " ");
greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf));
greentea_send_kv("host_port", " ");
greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf));
sscanf(portbuf, "%u", &port);
printf("MBED: Server IP address received: %s:%d \n", ipbuf, port);
TCPSocket sock;
SocketAddress tcp_addr(ipbuf, port);
Timer timer;
timer.start();
// Tests exponentially growing sequences
for (size_t size = MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN;
size < MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX;
size *= 2) {
err = sock.open(&eth);
TEST_ASSERT_EQUAL(0, err);
err = sock.connect(tcp_addr);
TEST_ASSERT_EQUAL(0, err);
printf("TCP: %s:%d streaming %d bytes\r\n", ipbuf, port, size);
sock.set_blocking(false);
// Loop to send/recv all data
RandSeq tx_seq;
RandSeq rx_seq;
size_t rx_count = 0;
size_t tx_count = 0;
size_t window = buffer_size;
while (tx_count < size || rx_count < size) {
// Send out data
if (tx_count < size) {
size_t chunk_size = size - tx_count;
if (chunk_size > window) {
chunk_size = window;
}
tx_seq.buffer(buffer, chunk_size);
int td = sock.send(buffer, chunk_size);
if (td > 0) {
if (MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG) {
printf("TCP: tx -> %d\r\n", td);
}
tx_seq.skip(td);
tx_count += td;
} else if (td != NSAPI_ERROR_WOULD_BLOCK) {
// We may fail to send because of buffering issues,
// cut buffer in half
if (window > MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN) {
window /= 2;
}
if (MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG) {
printf("TCP: Not sent (%d), window = %d\r\n", td, window);
}
}
}
// Verify recieved data
while (rx_count < size) {
int rd = sock.recv(buffer, buffer_size);
TEST_ASSERT(rd > 0 || rd == NSAPI_ERROR_WOULD_BLOCK);
if (rd > 0) {
if (MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG) {
printf("TCP: rx <- %d\r\n", rd);
}
int diff = rx_seq.cmp(buffer, rd);
TEST_ASSERT_EQUAL(0, diff);
rx_seq.skip(rd);
rx_count += rd;
} else if (rd == NSAPI_ERROR_WOULD_BLOCK) {
break;
}
}
}
err = sock.close();
TEST_ASSERT_EQUAL(0, err);
}
timer.stop();
printf("MBED: Time taken: %fs\r\n", timer.read());
printf("MBED: Speed: %.3fkb/s\r\n",
8*(2*MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX -
MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN) / (1000*timer.read()));
eth.disconnect();
GREENTEA_TESTSUITE_RESULT(result);
}

View File

@ -0,0 +1,290 @@
#if !FEATURE_LWIP
#error [NOT_SUPPORTED] LWIP not supported for this target
#endif
#if DEVICE_EMAC
#error [NOT_SUPPORTED] Not supported for WiFi targets
#endif
#ifndef MBED_EXTENDED_TESTS
#error [NOT_SUPPORTED] Parallel pressure tests are not supported by default
#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_PACKET_PRESSURE_MIN
#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN 64
#endif
#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX
#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX 0x80000
#endif
#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_SEED
#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_SEED 0x6d626564
#endif
#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS
#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS 3
#endif
#ifndef MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG
#define MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG false
#endif
// Simple xorshift pseudorandom number generator
class RandSeq {
private:
uint32_t x;
uint32_t y;
static const int A = 15;
static const int B = 18;
static const int C = 11;
public:
RandSeq(uint32_t seed=MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_SEED)
: x(seed), y(seed) {}
uint32_t next(void) {
x ^= x << A;
x ^= x >> B;
x ^= y ^ (y >> C);
return x + y;
}
void skip(size_t size) {
for (size_t i = 0; i < size; i++) {
next();
}
}
void buffer(uint8_t *buffer, size_t size) {
RandSeq lookahead = *this;
for (size_t i = 0; i < size; i++) {
buffer[i] = lookahead.next() & 0xff;
}
}
int cmp(uint8_t *buffer, size_t size) {
RandSeq lookahead = *this;
for (size_t i = 0; i < size; i++) {
int diff = buffer[i] - (lookahead.next() & 0xff);
if (diff != 0) {
return diff;
}
}
return 0;
}
};
// Tries to get the biggest buffer possible on the device. Exponentially
// grows a buffer until heap runs out of space, and uses half to leave
// space for the rest of the program
void generate_buffer(uint8_t **buffer, size_t *size, size_t min, size_t max) {
size_t i = min;
while (i < max) {
void *b = malloc(i);
if (!b) {
i /= 4;
if (i < min) {
i = min;
}
break;
}
free(b);
i *= 2;
}
*buffer = (uint8_t *)malloc(i);
*size = i;
TEST_ASSERT(buffer);
}
// Global variables shared between pressure tests
EthernetInterface net;
SocketAddress tcp_addr;
Timer timer;
Mutex iomutex;
// Single instance of a pressure test
class PressureTest {
private:
uint8_t *buffer;
size_t buffer_size;
TCPSocket sock;
Thread thread;
public:
PressureTest(uint8_t *buffer, size_t buffer_size)
: buffer(buffer), buffer_size(buffer_size) {
}
void start() {
osStatus status = thread.start(callback(this, &PressureTest::run));
TEST_ASSERT_EQUAL(osOK, status);
}
void join() {
osStatus status = thread.join();
TEST_ASSERT_EQUAL(osOK, status);
}
void run() {
// Tests exponentially growing sequences
for (size_t size = MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN;
size < MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX;
size *= 2) {
int err = sock.open(&net);
TEST_ASSERT_EQUAL(0, err);
err = sock.connect(tcp_addr);
TEST_ASSERT_EQUAL(0, err);
iomutex.lock();
printf("TCP: %s:%d streaming %d bytes\r\n",
tcp_addr.get_ip_address(), tcp_addr.get_port(), size);
iomutex.unlock();
sock.set_blocking(false);
// Loop to send/recv all data
RandSeq tx_seq;
RandSeq rx_seq;
size_t rx_count = 0;
size_t tx_count = 0;
size_t window = buffer_size;
while (tx_count < size || rx_count < size) {
// Send out data
if (tx_count < size) {
size_t chunk_size = size - tx_count;
if (chunk_size > window) {
chunk_size = window;
}
tx_seq.buffer(buffer, chunk_size);
int td = sock.send(buffer, chunk_size);
if (td > 0) {
if (MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG) {
iomutex.lock();
printf("TCP: tx -> %d\r\n", td);
iomutex.unlock();
}
tx_seq.skip(td);
tx_count += td;
} else if (td != NSAPI_ERROR_WOULD_BLOCK) {
// We may fail to send because of buffering issues,
// cut buffer in half
if (window > MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN) {
window /= 2;
}
if (MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG) {
iomutex.lock();
printf("TCP: Not sent (%d), window = %d\r\n", td, window);
iomutex.unlock();
}
}
}
// Verify recieved data
while (rx_count < size) {
int rd = sock.recv(buffer, buffer_size);
TEST_ASSERT(rd > 0 || rd == NSAPI_ERROR_WOULD_BLOCK);
if (rd > 0) {
if (MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_DEBUG) {
iomutex.lock();
printf("TCP: rx <- %d\r\n", rd);
iomutex.unlock();
}
int diff = rx_seq.cmp(buffer, rd);
TEST_ASSERT_EQUAL(0, diff);
rx_seq.skip(rd);
rx_count += rd;
} else if (rd == NSAPI_ERROR_WOULD_BLOCK) {
break;
}
}
}
err = sock.close();
TEST_ASSERT_EQUAL(0, err);
}
}
};
PressureTest *pressure_tests[MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS];
int main() {
GREENTEA_SETUP(2*60, "tcp_echo");
uint8_t *buffer;
size_t buffer_size;
generate_buffer(&buffer, &buffer_size,
MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN,
MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX);
size_t buffer_subsize = buffer_size / MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS;
printf("MBED: Generated buffer %d\r\n", buffer_size);
printf("MBED: Split into %d buffers %d\r\n",
MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS,
buffer_subsize);
int err = net.connect();
TEST_ASSERT_EQUAL(0, err);
printf("MBED: TCPClient IP address is '%s'\n", net.get_ip_address());
printf("MBED: TCPClient waiting for server IP and port...\n");
greentea_send_kv("target_ip", net.get_ip_address());
bool result = true;
char recv_key[] = "host_port";
char ipbuf[60] = {0};
char portbuf[16] = {0};
unsigned int port = 0;
greentea_send_kv("host_ip", " ");
greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf));
greentea_send_kv("host_port", " ");
greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf));
sscanf(portbuf, "%u", &port);
printf("MBED: Server IP address received: %s:%d \n", ipbuf, port);
tcp_addr.set_ip_address(ipbuf);
tcp_addr.set_port(port);
timer.start();
// Startup pressure tests in parallel
for (int i = 0; i < MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS; i++) {
pressure_tests[i] = new PressureTest(&buffer[i*buffer_subsize], buffer_subsize);
pressure_tests[i]->start();
}
for (int i = 0; i < MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS; i++) {
pressure_tests[i]->join();
delete pressure_tests[i];
}
timer.stop();
printf("MBED: Time taken: %fs\r\n", timer.read());
printf("MBED: Speed: %.3fkb/s\r\n",
MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_THREADS*
8*(2*MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MAX -
MBED_CFG_TCP_CLIENT_PACKET_PRESSURE_MIN) / (1000*timer.read()));
net.disconnect();
GREENTEA_TESTSUITE_RESULT(result);
}

View File

@ -0,0 +1,126 @@
#if !FEATURE_LWIP
#error [NOT_SUPPORTED] LWIP not supported for this target
#endif
#if DEVICE_EMAC
#error [NOT_SUPPORTED] Not supported for WiFi targets
#endif
#include "mbed.h"
#include "EthernetInterface.h"
#include "UDPSocket.h"
#include "greentea-client/test_env.h"
#include "unity/unity.h"
#ifndef MBED_CFG_UDP_DTLS_HANDSHAKE_BUFFER_SIZE
#define MBED_CFG_UDP_DTLS_HANDSHAKE_BUFFER_SIZE 512
#endif
#ifndef MBED_CFG_UDP_DTLS_HANDSHAKE_RETRIES
#define MBED_CFG_UDP_DTLS_HANDSHAKE_RETRIES 16
#endif
#ifndef MBED_CFG_UDP_DTLS_HANDSHAKE_PATTERN
#define MBED_CFG_UDP_DTLS_HANDSHAKE_PATTERN 112, 384, 200, 219, 25
#endif
uint8_t buffer[MBED_CFG_UDP_DTLS_HANDSHAKE_BUFFER_SIZE] = {0};
int udp_dtls_handshake_pattern[] = {MBED_CFG_UDP_DTLS_HANDSHAKE_PATTERN};
const int udp_dtls_handshake_count = sizeof(udp_dtls_handshake_pattern) / sizeof(int);
int main() {
GREENTEA_SETUP(20, "udp_shotgun");
EthernetInterface eth;
int err = eth.connect();
TEST_ASSERT_EQUAL(0, err);
printf("MBED: UDPClient IP address is '%s'\n", eth.get_ip_address());
printf("MBED: UDPClient waiting for server IP and port...\n");
greentea_send_kv("target_ip", eth.get_ip_address());
bool result = false;
char recv_key[] = "host_port";
char ipbuf[60] = {0};
char portbuf[16] = {0};
unsigned int port = 0;
greentea_send_kv("host_ip", " ");
greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf));
greentea_send_kv("host_port", " ");
greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf));
sscanf(portbuf, "%u", &port);
printf("MBED: UDP Server IP address received: %s:%d \n", ipbuf, port);
// align each size to 4-bits
for (int i = 0; i < udp_dtls_handshake_count; i++) {
udp_dtls_handshake_pattern[i] = (~0xf & udp_dtls_handshake_pattern[i]) + 0x10;
}
printf("MBED: DTLS pattern [");
for (int i = 0; i < udp_dtls_handshake_count; i++) {
printf("%d", udp_dtls_handshake_pattern[i]);
if (i != udp_dtls_handshake_count-1) {
printf(", ");
}
}
printf("]\r\n");
UDPSocket sock;
SocketAddress udp_addr(ipbuf, port);
for (int attempt = 0; attempt < MBED_CFG_UDP_DTLS_HANDSHAKE_RETRIES; attempt++) {
err = sock.open(&eth);
TEST_ASSERT_EQUAL(0, err);
for (int i = 0; i < udp_dtls_handshake_count; i++) {
buffer[i] = udp_dtls_handshake_pattern[i] >> 4;
}
err = sock.sendto(udp_addr, buffer, udp_dtls_handshake_count);
printf("UDP: tx -> %d\r\n", err);
TEST_ASSERT_EQUAL(udp_dtls_handshake_count, err);
int step = 0;
while (step < udp_dtls_handshake_count) {
err = sock.recvfrom(NULL, buffer, sizeof(buffer));
printf("UDP: rx <- %d ", err);
// check length
if (err != udp_dtls_handshake_pattern[step]) {
printf("x (expected %d)\r\n", udp_dtls_handshake_pattern[step]);
break;
}
// check quick xor of packet
uint8_t check = 0;
for (int j = 0; j < udp_dtls_handshake_pattern[step]; j++) {
check ^= buffer[j];
}
if (check != 0) {
printf("x (checksum 0x%02x)\r\n", check);
break;
}
// successfully got a packet
printf("\r\n");
step += 1;
}
err = sock.close();
TEST_ASSERT_EQUAL(0, err);
// got through all steps, test passed
if (step == udp_dtls_handshake_count) {
result = true;
break;
}
}
eth.disconnect();
GREENTEA_TESTSUITE_RESULT(result);
}

View File

@ -1,7 +1,6 @@
#if !FEATURE_LWIP
#error [NOT_SUPPORTED] LWIP not supported for this target
#endif
#if DEVICE_EMAC
#error [NOT_SUPPORTED] Not supported for WiFi targets
#endif
@ -10,11 +9,17 @@
#include "EthernetInterface.h"
#include "UDPSocket.h"
#include "greentea-client/test_env.h"
#include "unity/unity.h"
#ifndef MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE
#define MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE 256
#endif
#ifndef MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT
#define MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT 500
#endif
namespace {
char tx_buffer[MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE] = {0};
char rx_buffer[MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE] = {0};
@ -29,7 +34,7 @@ void prep_buffer(char *tx_buffer, size_t tx_size) {
}
int main() {
GREENTEA_SETUP(20, "udp_echo_client");
GREENTEA_SETUP(20, "udp_echo");
EthernetInterface eth;
eth.connect();
@ -37,8 +42,6 @@ int main() {
greentea_send_kv("target_ip", eth.get_ip_address());
bool result = false;
char recv_key[] = "host_port";
char ipbuf[60] = {0};
char portbuf[16] = {0};
@ -46,6 +49,7 @@ int main() {
UDPSocket sock;
sock.open(&eth);
sock.set_timeout(MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT);
greentea_send_kv("host_ip", " ");
greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf));
@ -55,25 +59,28 @@ int main() {
sscanf(portbuf, "%u", &port);
printf("MBED: UDP Server IP address received: %s:%d \n", ipbuf, port);
SocketAddress udp_addr(ipbuf, port);
SocketAddress addr(ipbuf, port);
int success = 0;
for (int i=0; i < ECHO_LOOPS; ++i) {
prep_buffer(tx_buffer, sizeof(tx_buffer));
const int ret = sock.sendto(addr, tx_buffer, sizeof(tx_buffer));
const int ret = sock.sendto(udp_addr, tx_buffer, sizeof(tx_buffer));
printf("[%02d] sent...%d Bytes \n", i, ret);
const int n = sock.recvfrom(&addr, rx_buffer, sizeof(rx_buffer));
SocketAddress temp_addr;
const int n = sock.recvfrom(&temp_addr, rx_buffer, sizeof(rx_buffer));
printf("[%02d] recv...%d Bytes \n", i, n);
if (memcmp(rx_buffer, tx_buffer, sizeof(rx_buffer))) {
result = false;
break;
if ((temp_addr == udp_addr &&
n == sizeof(tx_buffer) &&
memcmp(rx_buffer, tx_buffer, sizeof(rx_buffer)) == 0)) {
success += 1;
}
result = true;
}
bool result = (success > 3*ECHO_LOOPS/4);
sock.close();
eth.disconnect();
GREENTEA_TESTSUITE_RESULT(result);

View File

@ -0,0 +1,133 @@
#if !FEATURE_LWIP
#error [NOT_SUPPORTED] LWIP not supported for this target
#endif
#if DEVICE_EMAC
#error [NOT_SUPPORTED] Not supported for WiFi targets
#endif
#include "mbed.h"
#include "EthernetInterface.h"
#include "UDPSocket.h"
#include "greentea-client/test_env.h"
#include "unity/unity.h"
#ifndef MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE
#define MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE 64
#endif
#ifndef MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT
#define MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT 500
#endif
#ifndef MBED_CFG_UDP_CLIENT_ECHO_THREADS
#define MBED_CFG_UDP_CLIENT_ECHO_THREADS 3
#endif
const int ECHO_LOOPS = 16;
EthernetInterface net;
SocketAddress udp_addr;
Mutex iomutex;
void prep_buffer(char *tx_buffer, size_t tx_size) {
for (size_t i=0; i<tx_size; ++i) {
tx_buffer[i] = (rand() % 10) + '0';
}
}
// Each echo class is in charge of one parallel transaction
class Echo {
private:
char tx_buffer[MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE];
char rx_buffer[MBED_CFG_UDP_CLIENT_ECHO_BUFFER_SIZE];
UDPSocket sock;
Thread thread;
public:
void start() {
osStatus status = thread.start(callback(this, &Echo::echo));
TEST_ASSERT_EQUAL(osOK, status);
}
void join() {
osStatus status = thread.join();
TEST_ASSERT_EQUAL(osOK, status);
}
void echo() {
int success = 0;
int err = sock.open(&net);
TEST_ASSERT_EQUAL(0, err);
sock.set_timeout(MBED_CFG_UDP_CLIENT_ECHO_TIMEOUT);
for (int i = 0; i < ECHO_LOOPS; i++) {
prep_buffer(tx_buffer, sizeof(tx_buffer));
const int ret = sock.sendto(udp_addr, tx_buffer, sizeof(tx_buffer));
iomutex.lock();
printf("[%02d] sent...%d Bytes \n", i, ret);
iomutex.unlock();
SocketAddress temp_addr;
const int n = sock.recvfrom(&temp_addr, rx_buffer, sizeof(rx_buffer));
iomutex.lock();
printf("[%02d] recv...%d Bytes \n", i, n);
iomutex.unlock();
if ((temp_addr == udp_addr &&
n == sizeof(tx_buffer) &&
memcmp(rx_buffer, tx_buffer, sizeof(rx_buffer)) == 0)) {
success += 1;
}
}
err = sock.close();
TEST_ASSERT_EQUAL(0, err);
TEST_ASSERT(success > 3*ECHO_LOOPS/4);
}
};
Echo echoers[MBED_CFG_UDP_CLIENT_ECHO_THREADS];
int main() {
GREENTEA_SETUP(20, "udp_echo");
int err = net.connect();
TEST_ASSERT_EQUAL(0, err);
printf("UDP client IP Address is %s\n", net.get_ip_address());
greentea_send_kv("target_ip", net.get_ip_address());
char recv_key[] = "host_port";
char ipbuf[60] = {0};
char portbuf[16] = {0};
unsigned int port = 0;
greentea_send_kv("host_ip", " ");
greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf));
greentea_send_kv("host_port", " ");
greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf));
sscanf(portbuf, "%u", &port);
printf("MBED: UDP Server IP address received: %s:%d \n", ipbuf, port);
udp_addr.set_ip_address(ipbuf);
udp_addr.set_port(port);
// Startup echo threads in parallel
for (int i = 0; i < MBED_CFG_UDP_CLIENT_ECHO_THREADS; i++) {
echoers[i].start();
}
for (int i = 0; i < MBED_CFG_UDP_CLIENT_ECHO_THREADS; i++) {
echoers[i].join();
}
net.disconnect();
GREENTEA_TESTSUITE_RESULT(true);
}

View File

@ -0,0 +1,250 @@
#if !FEATURE_LWIP
#error [NOT_SUPPORTED] LWIP not supported for this target
#endif
#if DEVICE_EMAC
#error [NOT_SUPPORTED] Not supported for WiFi targets
#endif
#ifndef MBED_EXTENDED_TESTS
#error [NOT_SUPPORTED] Pressure tests are not supported by default
#endif
#include "mbed.h"
#include "EthernetInterface.h"
#include "UDPSocket.h"
#include "greentea-client/test_env.h"
#include "unity/unity.h"
#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN
#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN 64
#endif
#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX
#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX 0x80000
#endif
#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT
#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT 100
#endif
#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED
#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED 0x6d626564
#endif
#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG
#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG false
#endif
// Simple xorshift pseudorandom number generator
class RandSeq {
private:
uint32_t x;
uint32_t y;
static const int A = 15;
static const int B = 18;
static const int C = 11;
public:
RandSeq(uint32_t seed=MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED)
: x(seed), y(seed) {}
uint32_t next(void) {
x ^= x << A;
x ^= x >> B;
x ^= y ^ (y >> C);
return x + y;
}
void skip(size_t size) {
for (size_t i = 0; i < size; i++) {
next();
}
}
void buffer(uint8_t *buffer, size_t size) {
RandSeq lookahead = *this;
for (size_t i = 0; i < size; i++) {
buffer[i] = lookahead.next() & 0xff;
}
}
int cmp(uint8_t *buffer, size_t size) {
RandSeq lookahead = *this;
for (size_t i = 0; i < size; i++) {
int diff = buffer[i] - (lookahead.next() & 0xff);
if (diff != 0) {
return diff;
}
}
return 0;
}
};
// Shared buffer for network transactions
uint8_t *buffer;
size_t buffer_size;
// Tries to get the biggest buffer possible on the device. Exponentially
// grows a buffer until heap runs out of space, and uses half to leave
// space for the rest of the program
void generate_buffer(uint8_t **buffer, size_t *size, size_t min, size_t max) {
size_t i = min;
while (i < max) {
void *b = malloc(i);
if (!b) {
i /= 4;
if (i < min) {
i = min;
}
break;
}
free(b);
i *= 2;
}
*buffer = (uint8_t *)malloc(i);
*size = i;
TEST_ASSERT(buffer);
}
int main() {
GREENTEA_SETUP(60, "udp_echo");
generate_buffer(&buffer, &buffer_size,
MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN,
MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX);
printf("MBED: Generated buffer %d\r\n", buffer_size);
EthernetInterface eth;
int err = eth.connect();
TEST_ASSERT_EQUAL(0, err);
printf("MBED: UDPClient IP address is '%s'\n", eth.get_ip_address());
printf("MBED: UDPClient waiting for server IP and port...\n");
greentea_send_kv("target_ip", eth.get_ip_address());
bool result = true;
char recv_key[] = "host_port";
char ipbuf[60] = {0};
char portbuf[16] = {0};
unsigned int port = 0;
greentea_send_kv("host_ip", " ");
greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf));
greentea_send_kv("host_port", " ");
greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf));
sscanf(portbuf, "%u", &port);
printf("MBED: Server IP address received: %s:%d \n", ipbuf, port);
UDPSocket sock;
SocketAddress udp_addr(ipbuf, port);
Timer timer;
timer.start();
// Tests exponentially growing sequences
for (size_t size = MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN;
size < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX;
size *= 2) {
err = sock.open(&eth);
TEST_ASSERT_EQUAL(0, err);
printf("UDP: %s:%d streaming %d bytes\r\n", ipbuf, port, size);
sock.set_blocking(false);
// Loop to send/recv all data
RandSeq tx_seq;
RandSeq rx_seq;
size_t rx_count = 0;
size_t tx_count = 0;
int known_time = timer.read_ms();
size_t window = buffer_size;
while (tx_count < size || rx_count < size) {
// Send out packets
if (tx_count < size) {
size_t chunk_size = size - tx_count;
if (chunk_size > window) {
chunk_size = window;
}
tx_seq.buffer(buffer, chunk_size);
int td = sock.sendto(udp_addr, buffer, chunk_size);
if (td > 0) {
if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) {
printf("UDP: tx -> %d\r\n", td);
}
tx_seq.skip(td);
tx_count += td;
} else if (td != NSAPI_ERROR_WOULD_BLOCK) {
// We may fail to send because of buffering issues, revert to
// last good sequence and cut buffer in half
if (window > MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) {
window /= 2;
}
if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) {
printf("UDP: Not sent (%d), window = %d\r\n", td, window);
}
}
}
// Prioritize recieving over sending packets to avoid flooding
// the network while handling erronous packets
while (rx_count < size) {
int rd = sock.recvfrom(NULL, buffer, buffer_size);
TEST_ASSERT(rd > 0 || rd == NSAPI_ERROR_WOULD_BLOCK);
if (rd > 0) {
if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) {
printf("UDP: rx <- %d\r\n", rd);
}
if (rx_seq.cmp(buffer, rd) == 0) {
rx_seq.skip(rd);
rx_count += rd;
known_time = timer.read_ms();
if (window < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX) {
window += MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN;
}
}
} else if (timer.read_ms() - known_time >
MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT) {
// Dropped packet or out of order, revert to last good sequence
// and cut buffer in half
tx_seq = rx_seq;
tx_count = rx_count;
known_time = timer.read_ms();
if (window > MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) {
window /= 2;
}
if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) {
printf("UDP: Dropped, window = %d\r\n", window);
}
} else if (rd == NSAPI_ERROR_WOULD_BLOCK) {
break;
}
}
}
err = sock.close();
TEST_ASSERT_EQUAL(0, err);
}
timer.stop();
printf("MBED: Time taken: %fs\r\n", timer.read());
printf("MBED: Speed: %.3fkb/s\r\n",
8*(2*MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX -
MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) / (1000*timer.read()));
eth.disconnect();
GREENTEA_TESTSUITE_RESULT(result);
}

View File

@ -0,0 +1,315 @@
#if !FEATURE_LWIP
#error [NOT_SUPPORTED] LWIP not supported for this target
#endif
#if DEVICE_EMAC
#error [NOT_SUPPORTED] Not supported for WiFi targets
#endif
#ifndef MBED_EXTENDED_TESTS
#error [NOT_SUPPORTED] Parallel pressure tests are not supported by default
#endif
#include "mbed.h"
#include "EthernetInterface.h"
#include "UDPSocket.h"
#include "greentea-client/test_env.h"
#include "unity/unity.h"
#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN
#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN 64
#endif
#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX
#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX 0x80000
#endif
#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT
#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT 100
#endif
#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED
#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED 0x6d626564
#endif
#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS
#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS 3
#endif
#ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG
#define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG false
#endif
// Simple xorshift pseudorandom number generator
class RandSeq {
private:
uint32_t x;
uint32_t y;
static const int A = 15;
static const int B = 18;
static const int C = 11;
public:
RandSeq(uint32_t seed=MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED)
: x(seed), y(seed) {}
uint32_t next(void) {
x ^= x << A;
x ^= x >> B;
x ^= y ^ (y >> C);
return x + y;
}
void skip(size_t size) {
for (size_t i = 0; i < size; i++) {
next();
}
}
void buffer(uint8_t *buffer, size_t size) {
RandSeq lookahead = *this;
for (size_t i = 0; i < size; i++) {
buffer[i] = lookahead.next() & 0xff;
}
}
int cmp(uint8_t *buffer, size_t size) {
RandSeq lookahead = *this;
for (size_t i = 0; i < size; i++) {
int diff = buffer[i] - (lookahead.next() & 0xff);
if (diff != 0) {
return diff;
}
}
return 0;
}
};
// Tries to get the biggest buffer possible on the device. Exponentially
// grows a buffer until heap runs out of space, and uses half to leave
// space for the rest of the program
void generate_buffer(uint8_t **buffer, size_t *size, size_t min, size_t max) {
size_t i = min;
while (i < max) {
void *b = malloc(i);
if (!b) {
i /= 8;
if (i < min) {
i = min;
}
break;
}
free(b);
i *= 2;
}
*buffer = (uint8_t *)malloc(i);
*size = i;
TEST_ASSERT(buffer);
}
// Global variables shared between pressure tests
EthernetInterface net;
SocketAddress udp_addr;
Timer timer;
Mutex iomutex;
// Single instance of a pressure test
class PressureTest {
private:
uint8_t *buffer;
size_t buffer_size;
UDPSocket sock;
Thread thread;
public:
PressureTest(uint8_t *buffer, size_t buffer_size)
: buffer(buffer), buffer_size(buffer_size) {
}
void start() {
osStatus status = thread.start(callback(this, &PressureTest::run));
TEST_ASSERT_EQUAL(osOK, status);
}
void join() {
osStatus status = thread.join();
TEST_ASSERT_EQUAL(osOK, status);
}
void run() {
// Tests exponentially growing sequences
for (size_t size = MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN;
size < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX;
size *= 2) {
int err = sock.open(&net);
TEST_ASSERT_EQUAL(0, err);
iomutex.lock();
printf("UDP: %s:%d streaming %d bytes\r\n",
udp_addr.get_ip_address(), udp_addr.get_port(), size);
iomutex.unlock();
sock.set_blocking(false);
// Loop to send/recv all data
RandSeq tx_seq;
RandSeq rx_seq;
size_t rx_count = 0;
size_t tx_count = 0;
int known_time = timer.read_ms();
size_t window = buffer_size;
while (tx_count < size || rx_count < size) {
// Send out packets
if (tx_count < size) {
size_t chunk_size = size - tx_count;
if (chunk_size > window) {
chunk_size = window;
}
tx_seq.buffer(buffer, chunk_size);
int td = sock.sendto(udp_addr, buffer, chunk_size);
if (td > 0) {
if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) {
iomutex.lock();
printf("UDP: tx -> %d\r\n", td);
iomutex.unlock();
}
tx_seq.skip(td);
tx_count += td;
} else if (td != NSAPI_ERROR_WOULD_BLOCK) {
// We may fail to send because of buffering issues, revert to
// last good sequence and cut buffer in half
if (window > MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) {
window /= 2;
}
if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) {
iomutex.lock();
printf("UDP: Not sent (%d), window = %d\r\n", td, window);
iomutex.unlock();
}
}
}
// Prioritize recieving over sending packets to avoid flooding
// the network while handling erronous packets
while (rx_count < size) {
int rd = sock.recvfrom(NULL, buffer, buffer_size);
TEST_ASSERT(rd > 0 || rd == NSAPI_ERROR_WOULD_BLOCK);
if (rd > 0) {
if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) {
iomutex.lock();
printf("UDP: rx <- %d\r\n", rd);
iomutex.unlock();
}
if (rx_seq.cmp(buffer, rd) == 0) {
rx_seq.skip(rd);
rx_count += rd;
known_time = timer.read_ms();
if (window < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX) {
window += MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN;
}
}
} else if (timer.read_ms() - known_time >
MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT) {
// Dropped packet or out of order, revert to last good sequence
// and cut buffer in half
tx_seq = rx_seq;
tx_count = rx_count;
known_time = timer.read_ms();
if (window > MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) {
window /= 2;
}
if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) {
iomutex.lock();
printf("UDP: Dropped, window = %d\r\n", window);
iomutex.unlock();
}
} else if (rd == NSAPI_ERROR_WOULD_BLOCK) {
break;
}
}
}
err = sock.close();
TEST_ASSERT_EQUAL(0, err);
}
}
};
PressureTest *pressure_tests[MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS];
int main() {
GREENTEA_SETUP(2*60, "udp_echo");
uint8_t *buffer;
size_t buffer_size;
generate_buffer(&buffer, &buffer_size,
MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN,
MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX);
size_t buffer_subsize = buffer_size / MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS;
printf("MBED: Generated buffer %d\r\n", buffer_size);
printf("MBED: Split into %d buffers %d\r\n",
MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS,
buffer_subsize);
int err = net.connect();
TEST_ASSERT_EQUAL(0, err);
printf("MBED: UDPClient IP address is '%s'\n", net.get_ip_address());
printf("MBED: UDPClient waiting for server IP and port...\n");
greentea_send_kv("target_ip", net.get_ip_address());
bool result = true;
char recv_key[] = "host_port";
char ipbuf[60] = {0};
char portbuf[16] = {0};
unsigned int port = 0;
greentea_send_kv("host_ip", " ");
greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf));
greentea_send_kv("host_port", " ");
greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf));
sscanf(portbuf, "%u", &port);
printf("MBED: Server IP address received: %s:%d \n", ipbuf, port);
udp_addr.set_ip_address(ipbuf);
udp_addr.set_port(port);
timer.start();
// Startup pressure tests in parallel
for (int i = 0; i < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS; i++) {
pressure_tests[i] = new PressureTest(&buffer[i*buffer_subsize], buffer_subsize);
pressure_tests[i]->start();
}
for (int i = 0; i < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS; i++) {
pressure_tests[i]->join();
delete pressure_tests[i];
}
timer.stop();
printf("MBED: Time taken: %fs\r\n", timer.read());
printf("MBED: Speed: %.3fkb/s\r\n",
MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS*
8*(2*MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX -
MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) / (1000*timer.read()));
net.disconnect();
GREENTEA_TESTSUITE_RESULT(result);
}