Merge pull request #7745 from OPpuolitaival/icetea

Icetea support
pull/7955/head
Martin Kojtal 2018-09-02 07:19:22 +02:00 committed by GitHub
commit 1eab0723ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 5689 additions and 66 deletions

6
.gitignore vendored
View File

@ -88,3 +88,9 @@ tags
.vscode/
features/FEATURE_BLE/targets/TARGET_CORDIO/stack_backup/
.pytest_cache
log
# Icetea related file
test_suite.json

0
TEST_APPS/__init__.py Normal file
View File

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2018 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.
*/
#include <stdio.h>
#include <stdarg.h>
#include "mbed.h"
#include "mbed-client-cli/ns_cmdline.h"
#ifndef ICETEA_EXAMPLE_ENABLED
#error [NOT_SUPPORTED] Skipping example application.
#endif
/**
* Macros for setting console flow control.
*/
#define CONSOLE_FLOWCONTROL_RTS 1
#define CONSOLE_FLOWCONTROL_CTS 2
#define CONSOLE_FLOWCONTROL_RTSCTS 3
#define mbed_console_concat_(x) CONSOLE_FLOWCONTROL_##x
#define mbed_console_concat(x) mbed_console_concat_(x)
#define CONSOLE_FLOWCONTROL mbed_console_concat(MBED_CONF_TARGET_CONSOLE_UART_FLOW_CONTROL)
#define SERIAL_CONSOLE_BAUD_RATE 115200
void cmd_ready_cb(int retcode)
{
cmd_next(retcode);
}
void wrap_printf(const char *f, va_list a)
{
vprintf(f, a);
}
int main()
{
cmd_init(&wrap_printf);
int c;
while ((c = getchar()) != EOF) {
cmd_char_input(c);
}
return 0;
}
FileHandle *mbed::mbed_override_console(int)
{
static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, SERIAL_CONSOLE_BAUD_RATE);
#if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS
console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC);
#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_CTS
console.set_flow_control(SerialBase::CTS, NC, STDIO_UART_CTS);
#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTSCTS
console.set_flow_control(SerialBase::RTSCTS, STDIO_UART_RTS, STDIO_UART_CTS);
#endif
return &console;
}

View File

@ -0,0 +1,158 @@
/*
* Copyright (c) 2018 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.
*/
#include "NetworkStack.h"
#include "NetworkInterface.h"
#include "mbed-client-cli/ns_cmdline.h"
#include "mbed-trace/mbed_trace.h"
#include "ip4string.h"
#define WIFI 2
#if !defined(MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE) || \
(MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE == WIFI && !defined(MBED_CONF_NSAPI_DEFAULT_WIFI_SSID))
#error [NOT_SUPPORTED] No network configuration found for this target.
#endif
#include <string.h>
#define TRACE_GROUP "Aifc"
NetworkInterface *net;
NetworkInterface *get_interface(void)
{
return net;
}
int cmd_ifup(int argc, char *argv[]);
int cmd_ifdown(int argc, char *argv[]);
int cmd_ifconfig(int argc, char *argv[]);
const char *MAN_IFCONFIG = " ifup interface up\r\n"\
" ifdown interface down\r\n";
static void ifconfig_print()
{
if (!net) {
cmd_printf("No interface configured\r\n");
return;
}
const char *str = net->get_ip_address();
if (str) {
uint8_t buf[4];
if (stoip4(str, strlen(str), buf)) {
cmd_printf("IPv4 if addr: %s\r\n", str);
} else {
cmd_printf("IPv6 if addr:\r\n [0]: %s\r\n", str);
}
} else {
cmd_printf("No IP address\r\n");
}
str = net->get_mac_address();
if (str) {
cmd_printf("MAC-48: %s\r\n", str);
} else {
cmd_printf("MAC-48: unknown\r\n");
}
}
void cmd_ifconfig_init(void)
{
cmd_add("ifup", cmd_ifup, "ifconfig up", MAN_IFCONFIG);
cmd_add("ifdown", cmd_ifdown, "ifconfig down", MAN_IFCONFIG);
cmd_add("ifconfig", cmd_ifconfig, "ifconfig", MAN_IFCONFIG);
}
int cmd_ifconfig(int argc, char *argv[])
{
ifconfig_print();
return CMDLINE_RETCODE_SUCCESS;
}
int cmd_ifup(int argc, char *argv[])
{
if (!net) {
net = NetworkInterface::get_default_instance();
}
int err = net->connect();
if (err != NSAPI_ERROR_OK) {
return CMDLINE_RETCODE_FAIL;
}
ifconfig_print();
return CMDLINE_RETCODE_SUCCESS;
}
int cmd_ifdown(int argc, char *argv[])
{
if (!net) {
return CMDLINE_RETCODE_FAIL;
}
int err = net->disconnect();
if (err != NSAPI_ERROR_OK) {
return CMDLINE_RETCODE_FAIL;
}
return CMDLINE_RETCODE_SUCCESS;
}
const char *networkstack_error_to_str(int errorcode)
{
switch (errorcode) {
case NSAPI_ERROR_OK:
return "NSAPI_ERROR_OK";
case NSAPI_ERROR_WOULD_BLOCK:
return "NSAPI_ERROR_WOULD_BLOCK";
case NSAPI_ERROR_UNSUPPORTED:
return "NSAPI_ERROR_UNSUPPORTED";
case NSAPI_ERROR_PARAMETER:
return "NSAPI_ERROR_PARAMETER";
case NSAPI_ERROR_NO_CONNECTION:
return "NSAPI_ERROR_NO_CONNECTION";
case NSAPI_ERROR_NO_SOCKET:
return "NSAPI_ERROR_NO_SOCKET";
case NSAPI_ERROR_NO_ADDRESS:
return "NSAPI_ERROR_NO_ADDRESS";
case NSAPI_ERROR_NO_MEMORY:
return "NSAPI_ERROR_NO_MEMORY";
case NSAPI_ERROR_NO_SSID:
return "NSAPI_ERROR_NO_SSID";
case NSAPI_ERROR_DNS_FAILURE:
return "NSAPI_ERROR_DNS_FAILURE";
case NSAPI_ERROR_DHCP_FAILURE:
return "NSAPI_ERROR_DHCP_FAILURE";
case NSAPI_ERROR_AUTH_FAILURE:
return "NSAPI_ERROR_AUTH_FAILURE";
case NSAPI_ERROR_DEVICE_ERROR:
return "NSAPI_ERROR_DEVICE_ERROR";
case NSAPI_ERROR_IN_PROGRESS:
return "NSAPI_ERROR_IN_PROGRESS";
case NSAPI_ERROR_ALREADY:
return "NSAPI_ERROR_ALREADY";
case NSAPI_ERROR_IS_CONNECTED:
return "NSAPI_ERROR_IS_CONNECTED";
case NSAPI_ERROR_CONNECTION_LOST:
return "NSAPI_ERROR_CONNECTION_LOST";
case NSAPI_ERROR_CONNECTION_TIMEOUT:
return "NSAPI_ERROR_CONNECTION_TIMEOUT";
default:
return "unknown error code";
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef CMD_IFCONFIG_H
#define CMD_IFCONFIG_H
#include "NetworkInterface.h"
#include "NetworkStack.h"
/** Get a pointer to a network interface instance
*
* Allowed interface types (depend on application configurations):
* cell0, wlan0, eth0, mesh0
*
* @return pointer to the network interface, or NULL if unrecognized or ambiguous
*/
NetworkInterface *get_interface(void);
void cmd_ifconfig_init(void);
const char *networkstack_error_to_str(int errorcode);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef CMD_SOCKET_H_
#define CMD_SOCKET_H_
#include "nsapi_types.h"
int handle_nsapi_error(const char *function, nsapi_error_t ret);
int handle_nsapi_size_or_error(const char *function, nsapi_size_or_error_t ret);
void cmd_socket_init(void);
#endif

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2018 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.
*/
#include <stdio.h>
#include <stdarg.h>
#include "mbed.h"
#include "mbed-client-cli/ns_cmdline.h"
#include "cmd_ifconfig.h"
#include "cmd_socket.h"
/**
* Macros for setting console flow control.
*/
#define CONSOLE_FLOWCONTROL_RTS 1
#define CONSOLE_FLOWCONTROL_CTS 2
#define CONSOLE_FLOWCONTROL_RTSCTS 3
#define mbed_console_concat_(x) CONSOLE_FLOWCONTROL_##x
#define mbed_console_concat(x) mbed_console_concat_(x)
#define CONSOLE_FLOWCONTROL mbed_console_concat(MBED_CONF_TARGET_CONSOLE_UART_FLOW_CONTROL)
#define SERIAL_CONSOLE_BAUD_RATE 115200
void cmd_ready_cb(int retcode)
{
cmd_next(retcode);
}
void wrap_printf(const char *f, va_list a)
{
vprintf(f, a);
}
int main()
{
cmd_init(&wrap_printf);
cmd_ifconfig_init();
cmd_socket_init();
int c;
while ((c = getchar()) != EOF) {
cmd_char_input(c);
}
return 0;
}
FileHandle *mbed::mbed_override_console(int)
{
static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, SERIAL_CONSOLE_BAUD_RATE);
#if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS
console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC);
#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_CTS
console.set_flow_control(SerialBase::CTS, NC, STDIO_UART_CTS);
#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTSCTS
console.set_flow_control(SerialBase::RTSCTS, STDIO_UART_RTS, STDIO_UART_CTS);
#endif
return &console;
}

View File

@ -0,0 +1,22 @@
{
"macros": [
"MEM_ALLOC=malloc",
"MEM_FREE=free",
"MBED_HEAP_STATS_ENABLED=1",
"MBED_MEM_TRACING_ENABLED"
],
"target_overrides": {
"*": {
"target.features_add": ["LWIP", "COMMON_PAL"],
"mbed-trace.enable": 1,
"platform.stdio-baud-rate": 115200,
"platform.stdio-convert-newlines": true,
"platform.stdio-buffered-serial": true,
"platform.stdio-flush-at-exit": true,
"drivers.uart-serial-rxbuf-size": 768
},
"UBLOX_EVK_ODIN_W2" : {
"target.device_has_remove": ["EMAC"]
}
}
}

View File

@ -0,0 +1,242 @@
/*
* Copyright (c) 2018 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.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "ip6string.h"
#include "strconv.h"
int8_t strtohex(uint8_t *out, const char *str, int8_t max_length)
{
int8_t i = 0;
char *rd = (char *)str;
uint8_t *wr = out;
while (*rd != 0) {
if (i > max_length) {
break;
}
*wr++ = strtoul(rd, &rd, 16);
while (!isxdigit((int)*rd) && *rd != 0) {
rd++; //skip some invalid characters
}
i++;
}
return i;
}
int hexstr_to_bytes_inplace(char *str)
{
int16_t len, i, j;
if (str == NULL) {
return -1;
}
len = strlen(str);
if (len < 2 || (len + 1) % 3 != 0) {
return -1;
}
for (i = 0, j = 0; i < len; i += 3, ++j) {
str[j] = (char)strtol(str + i, 0, 16);
}
return j;
}
// convert hex string (eg. "76 ab ff") to binary array
int string_to_bytes(const char *str, uint8_t *buf, int bytes)
{
int len = strlen(str);
if (len <= (3 * bytes - 1)) {
int i;
for (i = 0; i < bytes; i++) {
if (i * 3 < len) {
buf[i] = (uint8_t)strtoul(str + i * 3, 0, 16);
} else {
buf[i] = 0;
}
}
return 0;
}
return -1;
}
int16_t hextostr(const uint8_t *buf, uint16_t buf_length, char *out, int16_t out_length, char delimiter)
{
int16_t outLeft = out_length;
int16_t arrLeft = buf_length;
const uint8_t *rd = buf;
int retcode = 0;
char *wr = out;
while (arrLeft > 0 && outLeft > 0) {
retcode = snprintf(wr, outLeft, "%02x", *rd);
if (retcode <= 0 || retcode >= outLeft) {
break;
}
outLeft -= retcode;
wr += retcode;
arrLeft --;
rd++;
if (delimiter && arrLeft > 0 && outLeft > 0) {
*wr++ = delimiter;
outLeft--;
*wr = 0;
}
}
return (int16_t)(wr - out);
}
int replace_hexdata(char *str)
{
char *ptr = str;
if (str == NULL) {
return 0;
}
while (*ptr) {
if (ptr[0] == '\\') {
if (ptr[1] == 'n') {
ptr[0] = 0x0a;
memmove(ptr + 1, ptr + 2, strlen(ptr + 2) + 1);
} else if (ptr[1] == 'r') {
ptr[0] = 0x0d;
memmove(ptr + 1, ptr + 2, strlen(ptr + 2) + 1);
} else if (ptr[1] == 'x') {
char *end;
ptr[0] = (char)strtoul(ptr + 2, &end, 16);
memmove(ptr + 1, end, strlen(end) + 1);
} else if (isdigit((int)ptr[1])) {
char *end;
ptr[0] = strtoul(ptr + 1, &end, 10);
memmove(ptr + 1, end, strlen(end) + 1);
}
}
ptr++;
}
return ptr - str;
}
bool is_visible(uint8_t *buf, int len)
{
while (len--) {
if (!isalnum(*buf) && *buf != ' ') {
return false;
}
buf++;
}
return true;
}
char *strdupl(const char *str)
{
if (!str) {
return NULL;
}
char *p = malloc(strlen(str) + 1);
if (!p) {
return p;
}
strcpy(p, str);
return p;
}
int strnlen_(const char *s, int n)
{
char *end = memchr(s, 0, n);
return end ? end - s : n;
}
char *strndupl(const char *s, int n)
{
char *p = NULL;
int len = strnlen_(s, n);
p = malloc(len + 1);
if (!p) {
return p;
}
p[len] = 0;
return memcpy(p, s, len);
}
int strnicmp_(char const *a, char const *b, int n)
{
for (; (n > 0 && *a != 0 && *b != 0) ; a++, b++, n--) {
int d = tolower((int) * a) - tolower((int) * b);
if (d != 0 || !*a) {
return d;
}
}
return 0;
}
/* HELPING PRINT FUNCTIONS for cmd_printf */
static inline uint8_t context_split_mask(uint_fast8_t split_value)
{
return (uint8_t) - (0x100u >> split_value);
}
static uint8_t *bitcopy(uint8_t *restrict dst, const uint8_t *restrict src, uint_fast8_t bits)
{
uint_fast8_t bytes = bits / 8;
bits %= 8;
if (bytes) {
dst = (uint8_t *) memcpy(dst, src, bytes) + bytes;
src += bytes;
}
if (bits) {
uint_fast8_t split_bit = context_split_mask(bits);
*dst = (*src & split_bit) | (*dst & ~ split_bit);
}
return dst;
}
char tmp_print_buffer[128] = {0};
char *print_ipv6(const void *addr_ptr)
{
ip6tos(addr_ptr, tmp_print_buffer);
return tmp_print_buffer;
}
char *print_ipv6_prefix(const uint8_t *prefix, uint8_t prefix_len)
{
char *str = tmp_print_buffer;
int retval;
char tmp[40];
uint8_t addr[16] = {0};
if (prefix_len != 0) {
if (prefix == NULL || prefix_len > 128) {
return "<err>";
}
bitcopy(addr, prefix, prefix_len);
}
ip6tos(addr, tmp);
retval = snprintf(str, 128, "%s/%u", tmp, prefix_len);
if (retval <= 0) {
return "";
}
return str;
}
char *print_array(const uint8_t *buf, uint16_t len)
{
int16_t retcode = hextostr(buf, len, tmp_print_buffer, 128, ':');
if (retcode > 0) {
//yeah, something is converted
}
return tmp_print_buffer;
}

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef STRCONVERSION_H
#define STRCONVERSION_H
#ifdef __cplusplus
extern "C" {
#endif
#include "ns_types.h"
/** Convert hex string to binary array
* e.g.
* char str[] = {30, 31, 32, 33};
* uint8_t buffer[4];
* strtohex( buffer, str, 4 );
*/
int8_t strtohex(uint8_t *out, const char *str, int8_t max_length);
/** Convert space separated hex string (eg. "76 ab ff") to binary array.
*/
int string_to_bytes(const char *str, uint8_t *buf, int bytes);
/** Convert a colon/space separated hex string (eg. "76:ab:ff") to binary
* array (inplace) returning the number of the bytes in the array.
*/
int hexstr_to_bytes_inplace(char *str);
/** Convert hex array to string
*/
int16_t hextostr(const uint8_t *buf, uint16_t buf_length, char *out, int16_t out_length, char delimiter);
/** Replace hex characters from string
* e.g.
* "hello\\n" -> "hello\n"
* "hello\\r" -> "hello\r"
* "val: \\10" -> "val: \x0a" //integer
* "val: \\x10" -> "val: \x10" //hex value
* @param str string that will be replaced
* @return length
*/
int replace_hexdata(char *str);
/**
* check if array contains only visible characters
* = isalpha | isdigit
*/
bool is_visible(uint8_t *buf, int len);
/** Convert ipv6 address to string format
*/
char *print_ipv6(const void *addr_ptr);
/** Convert ipv6 prefix to string format
*/
char *print_ipv6_prefix(const uint8_t *prefix, uint8_t prefix_len);
/** Convert binary array to string format and return static results
*/
char *print_array(const uint8_t *buf, uint16_t len);
/** The strdupl() function shall return a pointer to a new string,
* which is a duplicate of the string pointed to by s1. The returned pointer can be passed to free().
* A null pointer is returned if the new string cannot be created.
* strdupl are same than linux strdup, but this way we avoid to duplicate reference in linux
*/
char *strdupl(const char *str);
/** The strdup() function returns a pointer to a new string which is a duplicate of the string.
* Memory for the new string is obtained with malloc(3), and can be freed with free(3).
* The strndup() function is similar, but only copies at most n bytes. If s is longer than n,
* only n bytes are copied, and a terminating null byte ('\0') is added.
*/
char *strndupl(const char *s, int n);
/** strnlen - determine the length of a fixed-size string
* The strnlen() function returns the number of bytes in the string pointed to by s, excluding the terminating null bye ('\0'), but at most maxlen.
* In doing this, strnlen() looks only at the first maxlen bytes at s and never beyond s+maxlen.
* The strnlen() function returns strlen(s), if that is less than maxlen, or maxlen if there is no null byte ('\0')
* among the first maxlen bytes pointed to by s.
*/
int strnlen_(const char *s, int n);
/** strnicmp compares a and b without sensitivity to case.
* All alphabetic characters in the two arguments a and b are converted to lowercase before the comparison.
*/
int strnicmp_(char const *a, char const *b, int n);
#ifdef __cplusplus
}
#endif
#endif

View File

View File

@ -0,0 +1,157 @@
"""
Copyright 2018 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 re
import time
from collections import OrderedDict
from datetime import datetime
from icetea_lib.Plugin.PluginBase import PluginBase
class IpTestParsers(PluginBase):
# constructor
def __init__(self):
super(IpTestParsers, self).__init__()
def get_parsers(self):
return {
'ifconfig': self.__ifconfigParser,
'ifup': self.__ifconfigParser,
'ethup': self.__ifconfigParser,
'dut1': self.__ifconfigParser,
'dut2': self.__ifconfigParser,
'dut3': self.__ifconfigParser,
'dut4': self.__ifconfigParser,
'dut5': self.__ifconfigParser,
'dut6': self.__ifconfigParser,
'socket': self.__mbedossocketParser,
'ticker': self.__ticker_parser
}
# socket command for mbedos
def __mbedossocketParser(self, response):
results = {'socket_id': None,
'data_type': None,
'data': "",
'printed_bytes': None,
'sent_bytes': None,
'received_bytes': None,
'address': None,
'port': None,
'loop_id': None
}
respLines = response.lines
part = None
for line in respLines:
ret = PluginBase.find_one(line, ".*sid: ([0-9]+)")
if ret is not False:
results['socket_id'] = ret
ret = PluginBase.find_one(line, ".*(hex|string) data, printing .* bytes:")
if ret is not False:
results['data_type'] = ret
ret = PluginBase.find_one(line, ".*data, printing (.*) bytes:")
if ret is not False:
part = "data"
ret = PluginBase.find_one(line, "^Printed (.*) bytes$")
if ret is not False:
results['printed_bytes'] = int(ret)
part = None
if part == "data":
ret = PluginBase.find_one(line, "^\d{4}: (.*)$")
if ret is not False:
results['data'] = results['data'] + ret
ret = PluginBase.find_one(line, ".*sent: ([0-9]+) bytes")
if ret is not False:
results['sent_bytes'] = int(ret)
ret = PluginBase.find_one(line, ".*received: ([0-9]+) bytes")
if ret is not False:
results['received_bytes'] = int(ret)
ret = PluginBase.find_one(line, ".*address: ([0-9a-fxA-F:.]+)")
if ret is not False:
results['address'] = ret
ret = PluginBase.find_one(line, ".*port: ([0-9]+)")
if ret is not False:
results['port'] = ret
ret = PluginBase.find_one(line, ".*lid: ([0-9]+)")
if ret is not False:
results['loop_id'] = ret
return results
# response parser for ifup
def __ifconfigParser(self, response):
results = {}
lines = response.traces
part = None
results['address'] = {
'll': '',
'globals': [],
'ipv4': None,
'ipv6': []
}
for line in lines:
# print "ifcfgparser: %s" % line
match = re.search('IPv6 if addr', line)
if match:
part = "address"
match = re.search('IPv4 if addr', line)
if match:
part = "address"
match = re.search('MAC-48\:[\W]{1,}(.*)', line)
if match:
mac48 = match.group(1)
# Validate the MAC-48 syntax as well
match = re.search("([0-9a-fA-F]{2}:??){5}([0-9a-fA-F]{2})", mac48)
if match:
results['MAC'] = mac48
if part == "address":
match = re.search('.*\[(\d)\]:\W([abcdefg\d\:]{5,})', line)
if match:
addr = match.group(2)
if re.search('^fe80\:\:', addr):
results['address']['ll'] = addr
else:
results['address']["globals"].append(addr)
match = re.search('\[(\d)\]:\W([a-fA-F\d\:]{5,})', line)
if match:
results['address']['ipv6'].append(match.group(2))
match = re.search('(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$', line)
if match:
results['address']['ipv4'] = match.group(1)
return results
def __ticker_parser(self, response):
results = {}
respLines = response.lines
for line in respLines:
ret = PluginBase.find_one(line, 'Ticker id: ([0-9]+)')
if ret is not False:
results['ticker_id'] = ret
return results

View File

@ -0,0 +1,19 @@
"""
Copyright 2018 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 ip_test_parsers import IpTestParsers
plugins_to_load = {
"ip_test_parsers": IpTestParsers
}

93
TEST_APPS/readme.md Normal file
View File

@ -0,0 +1,93 @@
## Running Icetea tests located under mbed-os
### Structure
mbed-os has a folder called TEST_APPS that contains everything related to Icetea testing.
There are currently 3 folders:
- device - contains all the different test applications you can flash to your board
- icetea-plugins - contains plugins that are being used by some of the testcases, needed for the test execution
- testcases - contains Icetea testcases written in Python
The testcases depends on test applications
### Preparing your work environment
#### Prerequisities
You need Icetea and mbed-cli that supports Icetea, installed.
#### Selecting the network interface to use
Depending on a device, there might be a default network interface type defined in the mbed-os/targets/targets.json, which is used to locate a test-config file by default.
If there is not, or you want to use a different interface than the default, you need to provide a relevant test-config -file to the mbed test with --test-config option.
The test-config file contains the necessary information for the test application, there are some test-config files located under mbed-os/tools/test-configs.
Devices which have their network drivers residing inside mbed-os can use generic test-configs like HeapBlockDeviceAndEthernetInterface.json and HeapBlockDeviceAndWifiInterface.json. Otherwise you need to use a device specific test-config.
### Running the tests
Now that the interface has been selected you can run the icetea tests from the mbed-os root on your command line by
`>mbed test -m <target> -t <toolchain> --icetea`
This command will compile the mbed-os, then compiles the test applications, creates a test suite and then starts running the tests.
If you want only to run some specific tests, you can use the -n -option. You can list multiple tests by separating them by comma (,).
`>mbed test -m <target> -t <toolchain> --icetea -n test1,test2`
#### Running the tests with specifig test-config
Some devices may offer multiple network interfaces to operate with. For example, UBLOX_EVK_ODIN_W2 offers ethernet and Wi-Fi capabilities.
The tests can be run for either one of those using already existing test-config -files.
To run the tests with Wi-Fi interface:
`>mbed test -m UBLOX_EVK_ODIN_W2 -t <toolchain> --icetea --test-config tools/test-configs/HeapBlockDeviceAndWifiInterface.json`
To run the tests with ethernet interface:
`>mbed test -m UBLOX_EVK_ODIN_W2 -t <toolchain> --icetea --test-config tools/test-configs/HeapBlockDeviceAndEthernetInterface.json`
#### Providing Wi-Fi access point information
If you are using Wi-Fi interface for running the tests, you need to provide also information about the used access point.
The information can be provided in the used test-config file.
Example of access point information:
```
"target_overrides": {
"*": {
"target.network-default-interface-type": "WIFI",
"nsapi.default-wifi-ssid": "\"ssid\"",
"nsapi.default-wifi-password": "\"password\"",
"nsapi.default-wifi-security": "WPA_WPA2"
}
}
```
### Test results
Icetea prints the results from the test run to the command line, and the final result looks similar to this.
```
+--------------------------------+---------+-------------+-------------+-----------+----------+
| Testcase | Verdict | Fail Reason | Skip Reason | platforms | duration |
+--------------------------------+---------+-------------+-------------+-----------+----------+
| test_cmdline | pass | | | K64F | 8.555 |
| UDPSOCKET_BIND_PORT | pass | | | K64F | 19.614 |
| TCPSOCKET_BIND_PORT | pass | | | K64F | 15.852 |
| TCPSERVER_ACCEPT | pass | | | K64F | 41.629 |
| TCPSOCKET_ECHOTEST_BURST_SHORT | pass | | | K64F | 19.926 |
+--------------------------------+---------+-------------+-------------+-----------+----------+
+---------------+----------------+
| Summary | |
+---------------+----------------+
| Final Verdict | PASS |
| count | 5 |
| passrate | 100.00 % |
| pass | 5 |
| Duration | 0:01:45.576000 |
+---------------+----------------+
```
The results from the tests can also be found in the mbed-os/log folder.
You probably want to add the log folder to your .mbedignore file to prevent issues with build commands becoming too long over the time.

View File

View File

View File

@ -0,0 +1,50 @@
"""
Copyright 2018 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 icetea_lib.bench import Bench
class Testcase(Bench):
def __init__(self):
Bench.__init__(self,
name="test_cmdline",
title="Smoke test for command line interface",
status="released",
purpose="Verify Command Line Interface",
component=["cmdline"],
type="smoke",
requirements={
"duts": {
'*': {
"count": 1,
"type": "hardware",
"application": {
"name": "TEST_APPS-device-exampleapp"
}
},
"1": {"nick": "dut1"},
}
}
)
def setup(self):
pass
def case(self):
self.command("dut1", "echo hello world")
self.command("dut1", "help")
def teardown(self):
pass

View File

@ -0,0 +1,71 @@
"""
Copyright 2018 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 icetea_lib.bench import Bench
from icetea_lib.tools import test_case
class MultipleTestcase(Bench):
def __init__(self, **kwargs):
testcase_args = {
'status': "released",
'component': ["mbed-os", "netsocket"],
'type': "smoke",
'subtype': "socket",
'requirements': {
"duts": {
"*": {
"count": 1,
"type": "hardware",
"application": {"name": "TEST_APPS-device-socket_app"}
},
"1": {"nick": "dut1"},
}
}
}
testcase_args.update(kwargs)
Bench.__init__(self, **testcase_args)
def setup(self):
self.command("dut1", "ifup")
def socket_bind_port(self, socket_type):
response = self.command("dut1", "socket new " + socket_type)
socket_id = int(response.parsed['socket_id'])
self.command("dut1", "socket " + str(socket_id) + " open")
self.command("dut1", "socket " + str(socket_id) + " bind port 1024")
self.command("dut1", "socket " + str(socket_id) + " delete")
def teardown(self):
self.command("dut1", "ifdown")
@test_case(MultipleTestcase,
name="TCPSOCKET_BIND_PORT",
title="tcpsocket open and bind port",
purpose="Verify TCPSocket can be created, opened and port binded")
def test1(self):
self.socket_bind_port("TCPSocket")
@test_case(MultipleTestcase,
name="UDPSOCKET_BIND_PORT",
title="udpsocket open and bind port",
purpose="Verify UDPSocket can be created, opened and port binded")
def test2(self):
self.socket_bind_port("UDPSocket")

View File

@ -0,0 +1,103 @@
"""
Copyright 2018 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 threading
import time
from icetea_lib.TestStepError import TestStepFail
from icetea_lib.bench import Bench
from interface import interfaceUp, interfaceDown
class Testcase(Bench):
def __init__(self):
Bench.__init__(self,
name="TCPSERVER_ACCEPT",
title="TCPSERVER_ACCEPT",
purpose="Test that TCPServer::bind(), TCPServer::listen() and TCPServer::accept() works",
status="released",
component=["mbed-os", "netsocket"],
author="Juha Ylinen <juha.ylinen@arm.com>",
type="smoke",
subtype="socket",
requirements={
"duts": {
'*': { # requirements for all nodes
"count": 2,
"type": "hardware",
"application": {"name": "TEST_APPS-device-socket_app"}
},
"1": {"nick": "dut1"},
"2": {"nick": "dut2"}
}
}
)
def setup(self):
interface = interfaceUp(self, ["dut1"])
self.server_ip = interface["dut1"]["ip"]
interface = interfaceUp(self, ["dut2"])
self.client_ip = interface["dut2"]["ip"]
def clientThread(self):
self.logger.info("Starting")
time.sleep(5) # wait accept from server
self.command("dut2", "socket " + str(self.client_socket_id) + " open")
self.command("dut2", "socket " + str(self.client_socket_id) + " connect " + str(self.server_ip) + " " + str(
self.used_port))
def case(self):
self.used_port = 2000
response = self.command("dut1", "socket new TCPServer")
server_base_socket_id = int(response.parsed['socket_id'])
self.command("dut1", "socket " + str(server_base_socket_id) + " open")
self.command("dut1", "socket " + str(server_base_socket_id) + " bind port " + str(self.used_port))
self.command("dut1", "socket " + str(server_base_socket_id) + " listen")
response = self.command("dut1", "socket new TCPSocket")
server_socket_id = int(response.parsed['socket_id'])
self.command("dut1", "socket " + str(server_socket_id) + " open")
response = self.command("dut2", "socket new TCPSocket")
zero = response.timedelta
self.client_socket_id = int(response.parsed['socket_id'])
# Create a thread which calls client connect()
t = threading.Thread(name='clientThread', target=self.clientThread)
t.start()
wait = 5
response = self.command("dut1", "socket " + str(server_base_socket_id) + " accept " + str(server_socket_id))
response.verify_response_duration(expected=wait, zero=zero, threshold_percent=10, break_in_fail=True)
socket_id = int(response.parsed['socket_id'])
t.join()
self.command("dut1", "socket " + str(socket_id) + " send hello")
response = self.command("dut2", "socket " + str(self.client_socket_id) + " recv 5")
data = response.parsed['data'].replace(":", "")
if data != "hello":
raise TestStepFail("Received data doesn't match the sent data")
self.command("dut1", "socket " + str(server_socket_id) + " delete")
self.command("dut1", "socket " + str(server_base_socket_id) + " delete")
self.command("dut2", "socket " + str(self.client_socket_id) + " delete")
def teardown(self):
interfaceDown(self, ["dut1"])
interfaceDown(self, ["dut2"])

View File

@ -0,0 +1,78 @@
"""
Copyright 2018 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 string
from icetea_lib.Randomize.randomize import Randomize
from icetea_lib.bench import Bench, TestStepFail
class Testcase(Bench):
def __init__(self):
Bench.__init__(self,
name="TCPSOCKET_ECHOTEST_BURST_SHORT",
title="TCPSOCKET_ECHOTEST_BURST_SHORT",
purpose="Verify that TCPSocket can send burst of packets to echo server and read incoming packets",
status="released",
component=["mbed-os", "netsocket"],
author="Juha Ylinen <juha.ylinen@arm.com>",
type="smoke",
subtype="socket",
requirements={
"duts": {
'*': { # requirements for all nodes
"count": 1,
"type": "hardware",
"application": {
"name": "TEST_APPS-device-socket_app"
}
},
"1": {"nick": "dut1"},
}
}
)
def setup(self):
self.command("dut1", "ifup")
def case(self):
response = self.command("dut1", "socket new TCPSocket")
socket_id = int(response.parsed['socket_id'])
self.command("dut1", "socket " + str(socket_id) + " open")
self.command("dut1", "socket " + str(socket_id) + " connect echo.mbedcloudtesting.com 7")
for i in range(2):
sentData = ""
for size in (100, 200, 300, 120, 500):
packet = Randomize.random_string(max_len=size, min_len=size, chars=string.ascii_uppercase)
sentData += packet
response = self.command("dut1", "socket " + str(socket_id) + " send " + str(packet))
response.verify_trace("TCPSocket::send() returned: " + str(size))
received = 0
data = ""
totalSize = 1220
while received < totalSize:
response = self.command("dut1", "socket " + str(socket_id) + " recv " + str(totalSize))
data += response.parsed['data'].replace(":", "")
received += int(response.parsed['received_bytes'])
if data != sentData:
raise TestStepFail("Received data doesn't match the sent data")
self.command("dut1", "socket " + str(socket_id) + " delete")
def teardown(self):
self.command("dut1", "ifdown")

View File

@ -0,0 +1,43 @@
"""
Copyright 2018 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 icetea_lib.TestStepError import TestStepFail
'''
This interface script is intended to be a common library to be used in testcase scripts by testers.
It delegates setUp and tearDown functions with different provided network interface types using setUp() and tearDown() methods.
'''
def interfaceUp(tc, duts):
interfaces = {}
for dut in duts:
interface = {dut: {"ipv4": None, "ipv6": None}}
resp = tc.command("%s" % dut, "ifup")
ip = interface[dut]["ip"] = interface[dut]["ipv4"] = resp.parsed["address"]["ipv4"]
if not ip:
if resp.parsed["address"]["ipv6"]:
ip = interface[dut]["ip"] = interface[dut]["ipv6"] = resp.parsed["address"]["ipv6"][0]
if not ip:
raise TestStepFail("Failed to parse IP address")
interfaces.update(interface)
return interfaces
def interfaceDown(tc, duts):
for dut in duts:
tc.command(dut, "ifdown")

0
astyle-branch.out Normal file
View File

View File

@ -0,0 +1,406 @@
/*
* Copyright (c) 2016 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.
*/
/**
* \file ns_cmdline.h
*
* Command line library - mbedOS shell
*
* Usage example:
*
* \code
* //simple print function
* void myprint(const char* fmt, va_list ap){ vprintf(fmt, ap); }
* // simple ready cb, which call next command to be execute
* void cmd_ready_cb(int retcode) { cmd_next( retcode ); }
*
* // dummy command with some option
* int cmd_dummy(int argc, char *argv[]){
* if( cmd_has_option(argc, argv, "o") ) {
* cmd_printf("This is o option");
* } else {
* return CMDLINE_RETCODE_INVALID_PARAMETERS;
* }
* return CMDLINE_RETCODE_SUCCESS;
*}
* // timer cb ( pseudo-timer-code )
* void timer_ready_cb(void) {
* cmd_ready(CMDLINE_RETCODE_SUCCESS);
* }
* // long command, which need e.g. some events to finalize command execution
* int cmd_long(int argc, char *argv[] ) {
timer_start( 5000, timer_ready_cb );
* return CMDLINE_RETCODE_EXCUTING_CONTINUE;
* }
* void main(void) {
* cmd_init( &myprint ); // initialize cmdline with print function
* cmd_set_ready_cb( cmd_ready_cb ); // configure ready cb
* cmd_add("dummy", cmd_dummy, 0, 0); // add one dummy command
* cmd_add("long", cmd_long, 0, 0); // add one dummy command
* //execute dummy and long commands
* cmd_exe( "dymmy;long" );
* }
* \endcode
*/
#ifndef _CMDLINE_H_
#define _CMDLINE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define CMDLINE_RETCODE_COMMAND_BUSY 2 //!< Command Busy
#define CMDLINE_RETCODE_EXCUTING_CONTINUE 1 //!< Execution continue in background
#define CMDLINE_RETCODE_SUCCESS 0 //!< Execution Success
#define CMDLINE_RETCODE_FAIL -1 //!< Execution Fail
#define CMDLINE_RETCODE_INVALID_PARAMETERS -2 //!< Command parameters was incorrect
#define CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED -3 //!< Command not implemented
#define CMDLINE_RETCODE_COMMAND_CB_MISSING -4 //!< Command callback function missing
#define CMDLINE_RETCODE_COMMAND_NOT_FOUND -5 //!< Command not found
/**
* typedef for print functions
*/
typedef void (cmd_print_t)(const char *, va_list);
/**
* Initialize cmdline class.
* This is command line editor without any commands. Application
* needs to add commands that should be enabled.
* usage e.g.
* \code
cmd_init( &default_cmd_response_out );
* \endcode
* \param outf console printing function (like vprintf)
*/
void cmd_init(cmd_print_t *outf);
/** Command ready function for __special__ cases.
* This need to be call if command implementation return CMDLINE_RETCODE_EXECUTING_CONTINUE
* because there is some background stuff ongoing before command is finally completed.
* Normally there is some event, which call cmd_ready().
* \param retcode return code for command
*/
void cmd_ready(int retcode);
/** typedef for ready cb function */
typedef void (cmd_ready_cb_f)(int);
/**
* Configure cb which will be called after commands are executed
* or cmd_ready is called
* \param cb callback function for command ready
*/
void cmd_set_ready_cb(cmd_ready_cb_f *cb);
/**
* execute next command if any
* \param retcode last command return value
*/
void cmd_next(int retcode);
/** Free cmd class */
void cmd_free(void);
/** Reset cmdline to default values
* detach external commands, delete all variables and aliases
*/
void cmd_reset(void);
/** Configure command history size (default 32)
* \param max maximum history size
* max > 0 -> configure new value
* max = 0 -> just return current value
* \return current history max-size
*/
uint8_t cmd_history_size(uint8_t max);
/** command line print function
* This function should be used when user want to print something to the console
* \param fmt console print function (like printf)
*/
#if defined(__GNUC__) || defined(__CC_ARM)
void cmd_printf(const char *fmt, ...) __attribute__ ((__format__(__printf__, 1, 2)));
#else
void cmd_printf(const char *fmt, ...);
#endif
/** command line print function
* This function should be used when user want to print something to the console with vprintf functionality
* \param fmt The format string is a character string, beginning and ending in its initial shift state, if any. The format string is composed of zero or more directives.
* \param ap list of parameters needed by format string. This must correspond properly with the conversion specifier.
*/
#if defined(__GNUC__) || defined(__CC_ARM)
void cmd_vprintf(const char *fmt, va_list ap) __attribute__ ((__format__(__printf__, 1, 0)));
#else
void cmd_vprintf(const char *fmt, va_list ap);
#endif
/** Reconfigure default cmdline out function (cmd_printf)
* \param outf select console print function
*/
void cmd_out_func(cmd_print_t *outf);
/** Configure function, which will be called when Ctrl+A is pressed
* \param sohf control function which called every time when user input control keys
*/
void cmd_ctrl_func(void (*sohf)(uint8_t c));
/**
* Configure mutex wait function
* By default, cmd_printf calls may not be thread safe, depending on the implementation of the used output.
* This can be used to set a callback function that will be called before each cmd_printf call.
* The specific implementation is up to the application developer, but simple mutex locking is assumed.
*/
void cmd_mutex_wait_func(void (*mutex_wait_f)(void));
/**
* Configure mutex wait function
* By default, cmd_printf calls may not be thread safe, depending on the implementation of the used output.
* This can be used to set a callback function that will be called after each cmd_printf call.
* The specific implementation is up to the application developer, but simple mutex locking is assumed.
*/
void cmd_mutex_release_func(void (*mutex_release_f)(void));
/**
* Retrieve output mutex lock
* This can be used to retrieve the output mutex when multiple cmd_printf/cmd_vprintf calls must be
* guaranteed to be grouped together in a thread safe manner. Must be released by a following call to
* cmd_mutex_unlock()
* For example:
* * \code
* cmd_mutex_lock();
for (i = 0; i < 10; i++) {
cmd_printf("%02x ", i);
}
// without locking a print from another thread could happen here
cmd_printf("\r\n);
cmd_mutex_unlock();
* \endcode
* Exact behaviour depends on the implementation of the configured mutex,
* but counting mutexes are required.
*/
void cmd_mutex_lock(void);
/**
* Release output mutex lock
* This can be used to release the output mutex once it has been retrieved with cmd_mutex_lock()
* Exact behaviour depends on the implementation of the configured mutex,
* but counting mutexes are required.
*/
void cmd_mutex_unlock(void);
/** Refresh output */
void cmd_output(void);
/** default cmd response function, use stdout
* \param fmt The format string is a character string, beginning and ending in its initial shift state, if any. The format string is composed of zero or more directives.
* \param ap list of parameters needed by format string. This must correspond properly with the conversion specifier.
*/
void default_cmd_response_out(const char *fmt, va_list ap);
/** Initialize screen */
void cmd_init_screen(void);
/** Get echo state
* \return true if echo is on otherwise false
*/
bool cmd_echo_state(void);
/** Echo off */
void cmd_echo_off(void);
/** Echo on */
void cmd_echo_on(void);
/** Enter character to console.
* insert key pressess to cmdline called from main loop of application
* \param u_data char to be added to console
*/
void cmd_char_input(int16_t u_data);
/*
* Set the passthrough mode callback function. In passthrough mode normal command input handling is skipped and any
* received characters are passed to the passthrough callback function. Setting this to null will disable passthrough mode.
* \param passthrough_fnc The passthrough callback function
*/
typedef void (*input_passthrough_func_t)(uint8_t c);
void cmd_input_passthrough_func(input_passthrough_func_t passthrough_fnc);
/* Methods used for adding and handling of commands and aliases
*/
/** Callback called when your command is run.
* \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command.
* \param argv argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command.
*/
typedef int (cmd_run_cb)(int argc, char *argv[]);
/** Add command to intepreter
* \param name command string
* \param callback This function is called when command line start executing
* \param info Command short description which is visible in help command, or null if not in use
* \param man Help page for this command. This is shown when executing command with invalid parameters or command with --help parameter. Can be null if not in use.
*/
void cmd_add(const char *name, cmd_run_cb *callback, const char *info, const char *man);
/** delete command from intepreter
* \param name command to be delete
*/
void cmd_delete(const char *name);
/** Command executer.
* Command executer, which split&push command(s) to the buffer and
* start executing commands in cmd tasklet.
* if not, execute command directly.
* If command implementation returns CMDLINE_RETCODE_EXCUTING_CONTINUE,
* executor will wait for cmd_ready() before continue to next command.
* \param str command string, e.g. "help"
*/
void cmd_exe(char *str);
/** Add alias to interpreter.
* Aliases are replaced with values before executing a command. All aliases must be started from beginning of line.
* null or empty value deletes alias.
* \code
cmd_alias_add("print", "echo");
cmd_exe("print \"hello world!\""); // this is now same as "echo \"hello world!\"" .
* \endcode
* \param alias alias name
* \param value value for alias. Values can be any visible ASCII -characters.
*/
void cmd_alias_add(const char *alias, const char *value);
/** Add Variable to interpreter.
* Variables are replaced with values before executing a command.
* To use variables from cli, use dollar ($) -character so that interpreter knows user want to use variable in that place.
* null or empty value deletes variable.
* \code
cmd_variable_add("world", "hello world!");
cmd_exe("echo $world"); // this is now same as echo "hello world!" .
* \endcode
* \param variable Variable name, which will be replaced in interpreter.
* \param value Value for variable. Values can contains white spaces and '"' or '"' characters.
*/
void cmd_variable_add(char *variable, char *value);
/** find command parameter index by key.
* e.g.
* \code
int main(void){
//..init cmd..
//..
cmd_exe("mycmd enable")
}
int mycmd_command(int argc, char *argv[]) {
bool found = cmd_parameter_index( argc, argv, "enable" ) > 0;
}
* \endcode
* \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command.
* \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command.
* \param key option key, which index you want to find out.
* \return index where parameter was or -1 when not found
*/
int cmd_parameter_index(int argc, char *argv[], const char *key);
/** check if command option is present.
* e.g. cmd: "mycmd -c"
* \code
* bool on = cmd_has_option( argc, argv, "p" );
* \endcode
* \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command.
* \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command.
* \param key option key to be find
* \return true if option found otherwise false
*/
bool cmd_has_option(int argc, char *argv[], const char *key);
/** find command parameter by key.
* if exists, return true, otherwise false.
* e.g. cmd: "mycmd enable 1"
* \code
int mycmd_command(int argc, char *argv[]) {
bool value;
bool found = cmd_parameter_bool( argc, argv, "mykey", &value );
if( found ) return CMDLINE_RETCODE_SUCCESS;
else return CMDLINE_RETCODE_FAIL;
}
* \endcode
* \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command.
* \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command.
* \param key parameter key to be find
* \param value parameter value to be fetch, if key not found value are untouched. "1" and "on" and "true" and "enable" and "allow" are True -value, all others false.
* \return true if parameter key and value found otherwise false
*/
bool cmd_parameter_bool(int argc, char *argv[], const char *key, bool *value);
/** find command parameter by key and return value (next parameter).
* if exists, return parameter pointer, otherwise null.
* e.g. cmd: "mycmd mykey myvalue"
* \code
int mycmd_command(int argc, char *argv[]) {
char *value;
bool found = cmd_parameter_val( argc, argv, "mykey", &value );
if( found ) return CMDLINE_RETCODE_SUCCESS;
else return CMDLINE_RETCODE_FAIL;
}
* \endcode
* \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command.
* \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command.
* \param key parameter key to be find
* \param value pointer to pointer, which will point to cli input data when key and value found. if key or value not found this parameter are untouched.
* \return true if parameter key and value found otherwise false
*/
bool cmd_parameter_val(int argc, char *argv[], const char *key, char **value);
/** find command parameter by key and return value (next parameter) in integer. Only whitespaces are allowed in addition to the float to be read.
* e.g. cmd: "mycmd mykey myvalue"
* \code
int32_t value;
cmd_parameter_int( argc, argv, "key", &value );
* \endcode
* \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the item 0 in the list argv is a string to name of command.
* \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command.
* \param key parameter key to be found
* \param value A pointer to a variable where to write the converted number. If value cannot be converted, it is not touched.
* \return true if parameter key and an integer is found, otherwise return false
*/
bool cmd_parameter_int(int argc, char *argv[], const char *key, int32_t *value);
/** find command parameter by key and return value (next parameter) in float. Only whitespaces are allowed in addition to the float to be read.
* e.g. cmd: "mycmd mykey myvalue"
* \code
float value;
cmd_parameter_float( argc, argv, "key", &value );
* \endcode
* \param argc argc is the count of arguments given in argv pointer list. values begin from 1 and this means that the item 0 in the list argv is a string to name of command.
* \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command.
* \param key parameter key to be found
* \param value A pointer to a variable where to write the converted number. If value cannot be converted, it is not touched.
* \return true if parameter key and a float found, otherwise return false
*/
bool cmd_parameter_float(int argc, char *argv[], const char *key, float *value);
/** Get last command line parameter as string.
* e.g.
* cmd: "mycmd hello world"
* cmd_parameter_last -> "world"
* cmd: "mycmd"
* cmd_parameter_last() -> NULL
* \code
cmd_parameter_last(argc, argv)
* \endcode
* \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command.
* \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command.
* \return pointer to last parameter or NULL when there is no any parameters.
*/
char *cmd_parameter_last(int argc, char *argv[]);
/** find command parameter by key and return value (next parameter) in int64.
* e.g. cmd: "mycmd mykey myvalue"
* \code
uint32_t i;
cmd_parameter_timestamp( argc, argv, "mykey", &i );
* \endcode
*
* Supports following formats:
* number -> direct conversion
* 11:22:33:44:55:66:77:88 -> converts to number
* seconds,tics -> converts thread type timestamp to int64
*
* \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command.
* \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command.
* \param key parameter key to be find
* \param value parameter value to be fetch, if key not found value are untouched.
* \return true if parameter key and value found otherwise false
*/
bool cmd_parameter_timestamp(int argc, char *argv[], const char *key, int64_t *value);
#ifdef __cplusplus
}
#endif
#endif /*_CMDLINE_H_*/

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,15 @@
"fea-ipv6": {
"help": "Used to globally disable ipv6 tracing features.",
"value": null
},
"allocator": {
"value": "malloc",
"macro_name": "MEM_ALLOC"
},
"deallocator": {
"value": "free",
"macro_name": "MEM_FREE"
}
}
}
}

View File

@ -6,7 +6,7 @@ IntelHex>=1.3
junit-xml
pyYAML
requests
mbed-ls>=0.2.13
mbed-ls>=1.4.2,==1.*
mbed-host-tests>=1.1.2
mbed-greentea>=0.2.24
beautifulsoup4>=4
@ -17,3 +17,4 @@ future>=0.16.0
six>=1.11.0
git+https://github.com/armmbed/manifest-tool.git@v1.4.5
mbed-cloud-sdk==2.0.0
icetea>=1.0.1,<2

View File

@ -1348,11 +1348,13 @@ def merge_build_data(filename, toolchain_report, app_type):
for project in tc.values():
for build in project:
try:
build[0]['bin_fullpath'] = build[0]['bin']
build[0]['elf_fullpath'] = build[0]['elf']
build[0]['elf'] = relpath(build[0]['elf'], path_to_file)
build[0]['bin'] = relpath(build[0]['bin'], path_to_file)
except KeyError:
pass
if 'type' not in build[0]:
build[0]['type'] = app_type
build_data['builds'].append(build[0])
dump(build_data, open(filename, "w"), indent=4, separators=(',', ': '))
build_data['builds'].insert(0, build[0])
dump(build_data, open(filename, "wb"), indent=4, separators=(',', ': '))

View File

@ -69,6 +69,7 @@ LEGACY_IGNORE_DIRS = set([
# Tests, here for simplicity
'TESTS',
'TEST_APPS',
])
LEGACY_TOOLCHAIN_NAMES = {
'ARM_STD':'ARM',

332
tools/run_icetea.py Normal file
View File

@ -0,0 +1,332 @@
#! /usr/bin/env python2
"""
Copyright 2018 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 __future__ import print_function, division, absolute_import
import sys
import os
import re
from os.path import abspath, join, dirname, relpath, sep
import json
import traceback
from fnmatch import translate
from argparse import ArgumentParser
ROOT = abspath(join(dirname(__file__), '..'))
sys.path.insert(0, ROOT)
from tools.config import ConfigException
from tools.utils import cmd, run_cmd
plugins_path = abspath(join(ROOT, 'TEST_APPS', 'icetea_plugins', 'plugins_to_load.py'))
def find_build_from_build_data(build_data, id, target, toolchain):
if 'builds' not in build_data:
raise Exception("build data is in wrong format, does not include builds object")
for build in build_data['builds']:
if 'id' in build.keys() \
and id.upper() in build['id'].upper() \
and 'target_name' in build.keys() \
and target.upper() == build['target_name'].upper() \
and 'toolchain_name' in build.keys() \
and toolchain.upper() == build['toolchain_name'].upper() \
and 'result' in build.keys() \
and "OK" == build['result']:
return build
return None
def create_test_suite(target, tool, icetea_json_output, build_data, tests_by_name):
"""
Create test suite content
:param target:
:param tool:
:param icetea_json_output:
:param build_data:
:return:
"""
test_suite = dict()
test_suite['testcases'] = list()
for test in icetea_json_output:
skip = False
for dut in test['requirements']['duts'].values():
# Set binary path based on application name
if 'application' in dut.keys() and 'name' in dut['application'].keys():
build = find_build_from_build_data(
build_data=build_data,
id=dut['application']['name'],
target=target,
toolchain=tool)
if build:
try:
dut['application']['bin'] = build['bin_fullpath']
except KeyError:
raise Exception('Full path is missing from build: {}'.format(build))
else:
skip = True
if not tests_by_name or is_test_in_test_by_name(test['name'], tests_by_name):
test_case = {
'name': test['name'],
'config': {
'requirements': test['requirements']
}
}
# Skip test if not binary path
if skip:
test_case['config']['execution'] = {
'skip': {
'value': True,
'reason': "Test requiring application binary not build"
}
}
test_suite['testcases'].append(test_case)
return test_suite
def get_applications(test):
ret = list()
for dut in test['requirements']['duts'].values():
if 'application' in dut.keys() and 'name' in dut['application'].keys():
ret.append(dut['application']['name'])
return ret
def filter_test_by_build_data(icetea_json_output, build_data, target, toolchain):
if not build_data:
return icetea_json_output
ret = list()
for test in icetea_json_output:
for dut in test['requirements']['duts'].values():
if 'application' in dut.keys() and 'name' in dut['application'].keys():
id = dut['application']['name']
if find_build_from_build_data(build_data, id, target, toolchain):
# Test requiring build found
ret.append(test)
return ret
def filter_test_by_name(icetea_json_output, test_by_name):
if not test_by_name:
return icetea_json_output
ret = list()
for test_temp in icetea_json_output:
if is_test_in_test_by_name(test_temp['name'], test_by_name) and test_temp not in ret:
ret.append(test_temp)
return ret
def get_applications_from_test(test):
ret = list()
if u'requirements' in test.keys() and u'duts' in test[u'requirements']:
for name, dut in test[u'requirements'][u'duts'].items():
if u'application' in dut.keys() and u'name' in dut[u'application']:
ret.append(dut[u'application'][u'name'])
return ret
def get_application_list(icetea_json_output, tests_by_name):
""" Return comma separated list of application which are used in tests """
ret = list()
for test in filter_test_by_name(icetea_json_output, tests_by_name):
ret.extend(get_applications_from_test(test))
# Remove duplicates
return list(set(ret))
def icetea_tests(target, tcdir, verbose):
command = ['icetea', '--tcdir', tcdir, '--list', '--json', '--platform_filter', target] \
+ (['-v'] if verbose else [])
stdout, stderr, returncode = run_cmd(command)
if returncode != 0:
raise Exception(
"Error when running icetea. \ncwd:{} \nCommand:'{}' \noutput:{}".format(os.getcwd(), ' '.join(command),
stderr.decode()))
return json.loads(stdout)
def is_test_in_test_by_name(test_name, test_by_name):
for tbn_temp in test_by_name:
if re.search(translate(tbn_temp), test_name):
return True
return False
def check_tests(icetea_json_output):
"""
Check that all tests have all necessary information
:return:
"""
for test in icetea_json_output:
if not get_applications_from_test(test):
raise Exception('Test {} does not have application with correct name'.format(test['name']))
def load_build_data(build_data_path):
"""
:return: build_data.json content as dict and None if build data is not available
"""
if not os.path.isfile(build_data_path):
return None
return json.load(open(build_data_path))
if __name__ == '__main__':
try:
# Parse Options
parser = ArgumentParser()
parser.add_argument('-m', '--mcu',
dest='target',
default=None,
help='Test target MCU',
required=True)
parser.add_argument('-t', '--toolchain',
dest='toolchain',
default=None,
help='Toolchain',
required=True)
parser.add_argument('--build-data',
dest='build_data',
default=None,
help='Detail data from build')
parser.add_argument('--test-suite',
dest='test_suite',
default=None,
help='Path used for test suite file')
parser.add_argument('-n', '--tests-by-name',
dest='tests_by_name',
default=None,
help='Limit the tests to a list (ex. test1,test2,test3)')
parser.add_argument('--tcdir',
dest='tcdir',
default='TEST_APPS',
help='Test case directory',
required=False)
parser.add_argument('--compile-list',
action='store_true',
dest='compile_list',
default=False,
help='List tests, which applications can be compiled')
parser.add_argument('--run-list',
action='store_true',
dest='run_list',
default=False,
help='List tests, which applications are compiled and ready for run')
parser.add_argument('--application-list',
action='store_true',
dest='application_list',
default=False,
help='List applications that need to be build')
parser.add_argument('--ignore-checks',
action='store_true',
dest='ignore_checks',
default=False,
help='Ignore data validation checks')
parser.add_argument('-v', '--verbose',
action='store_true',
dest='verbose',
default=False,
help='Verbose diagnostic output')
options = parser.parse_args()
icetea_json_output = icetea_tests(options.target, options.tcdir, options.verbose)
tests_by_name = options.tests_by_name.split(',') if options.tests_by_name else None
build_data = load_build_data(options.build_data) if options.build_data else None
if not options.ignore_checks:
check_tests(icetea_json_output)
if options.compile_list:
print('Available icetea tests for build \'{}-{}\', location \'{}\''.format(
options.target, options.toolchain, options.tcdir))
for test in icetea_json_output:
print(
'Test Case:\n Name: {name}\n Path: .{sep}{filepath}\n Test applications: .{sep}{apps}'.format(
name=test['name'],
sep=sep,
filepath=relpath(test['filepath'], ROOT),
apps=''.join(get_applications(test)).replace('-', os.path.sep)))
elif options.run_list:
print('Available icetea tests for build \'{}-{}\', location \'{}\''.format(
options.target, options.toolchain, options.tcdir))
# Filters
tests = filter_test_by_name(icetea_json_output, tests_by_name)
if build_data:
tests = filter_test_by_build_data(tests, build_data, options.target, options.toolchain)
for test in tests:
print(' test \'{name}\''.format(name=test['name']))
elif options.application_list:
print(','.join(get_application_list(icetea_json_output, tests_by_name)))
else:
if not build_data:
raise Exception("Build data file does not exist: {}".format(options.build_data))
test_suite = create_test_suite(options.target, options.toolchain, icetea_json_output, build_data,
tests_by_name)
if not test_suite['testcases']:
raise Exception("Test suite is empty. Check that --tcdir and --tests-by-name have correct values")
if not options.test_suite:
raise Exception('--test-suite is required when running tests')
with open(options.test_suite, 'w') as f:
json.dump(test_suite, f, indent=2)
# List just for debug
if options.verbose:
cmd(['icetea', '--tcdir', options.tcdir, '--list'] + (['-v'] if options.verbose else []))
cmd(['icetea', '--tcdir', options.tcdir, '--suite', options.test_suite, '--clean', '--plugin_path',
plugins_path] + (['-v'] if options.verbose else []))
except KeyboardInterrupt as e:
print('\n[CTRL+c] exit')
except ConfigException as e:
# Catching ConfigException here to prevent a traceback
print('[ERROR] {}'.format(e))
except Exception as e:
traceback.print_exc(file=sys.stdout)
print('[ERROR] {}'.format(e))
sys.exit(1)

View File

@ -16,27 +16,28 @@ See the License for the specific language governing permissions and
limitations under the License.
TEST BUILD & RUN
TEST BUILD
"""
from __future__ import print_function, division, absolute_import
import sys
import os
import json
import fnmatch
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
sys.path.insert(0, ROOT)
from tools.config import ConfigException, Config
from tools.test_api import test_path_to_name, find_tests, get_test_config, print_tests, build_tests, test_spec_from_test_builds
from tools.test_configs import get_default_config
from tools.config import ConfigException
from tools.test_api import find_tests, get_test_config, print_tests, build_tests, test_spec_from_test_builds
import tools.test_configs as TestConfig
from tools.options import get_default_options_parser, extract_profile, extract_mcus
from tools.build_api import build_project, build_library
from tools.build_api import print_build_memory_usage
from tools.build_api import merge_build_data
from tools.targets import TARGET_MAP
from tools.notifier.term import TerminalNotifier
from tools.utils import mkdir, ToolException, NotSupportedException, args_error
from tools.utils import mkdir, ToolException, NotSupportedException, args_error, write_json_to_file
from tools.test_exporters import ReportExporter, ResultExporterType
from tools.utils import argparse_filestring_type, argparse_lowercase_type, argparse_many
from tools.utils import argparse_dir_not_parent
@ -111,9 +112,19 @@ if __name__ == '__main__':
dest="stats_depth",
default=2,
help="Depth level for static memory report")
parser.add_argument("--ignore", dest="ignore", type=argparse_many(str),
default=None, help="Comma separated list of patterns to add to mbedignore (eg. ./main.cpp)")
parser.add_argument("--icetea",
action="store_true",
dest="icetea",
default=False,
help="Only icetea tests")
parser.add_argument("--greentea",
action="store_true",
dest="greentea",
default=False,
help="Only greentea tests")
options = parser.parse_args()
@ -126,8 +137,13 @@ if __name__ == '__main__':
all_tests = {}
tests = {}
# As default both test tools are enabled
if not (options.greentea or options.icetea):
options.greentea = True
options.icetea = True
# Target
if options.mcu is None :
if options.mcu is None:
args_error(parser, "argument -m/--mcu is required")
mcu = extract_mcus(parser, options)[0]
@ -159,8 +175,13 @@ if __name__ == '__main__':
# Find all tests in the relevant paths
for path in all_paths:
all_tests.update(find_tests(path, mcu, toolchain,
app_config=config))
all_tests.update(find_tests(
base_dir=path,
target_name=mcu,
toolchain_name=toolchain,
icetea=options.icetea,
greentea=options.greentea,
app_config=config))
# Filter tests by name if specified
if options.names:
@ -251,20 +272,7 @@ if __name__ == '__main__':
# If a path to a test spec is provided, write it to a file
if options.test_spec:
test_spec_data = test_spec_from_test_builds(test_build)
# Create the target dir for the test spec if necessary
# mkdir will not create the dir if it already exists
test_spec_dir = os.path.dirname(options.test_spec)
if test_spec_dir:
mkdir(test_spec_dir)
try:
with open(options.test_spec, 'w') as f:
f.write(json.dumps(test_spec_data, indent=2))
except IOError as e:
print("[ERROR] Error writing test spec to file")
print(e)
write_json_to_file(test_spec_from_test_builds(test_build), options.test_spec)
# If a path to a JUnit build report spec is provided, write it to a file
if options.build_report_junit:
@ -296,3 +304,4 @@ if __name__ == '__main__':
traceback.print_exc(file=sys.stdout)
print("[ERROR] %s" % str(e))
sys.exit(1)

0
tools/test/__init__.py Normal file
View File

View File

@ -0,0 +1,14 @@
"""
Copyright 2017 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.
"""

View File

@ -0,0 +1,37 @@
"""
Copyright 2018 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 icetea_lib.bench import Bench
class Testcase(Bench):
def __init__(self):
Bench.__init__(self,
name="test_pass",
title="Test icetea integration",
status="released",
purpose="Just for testing scripts",
component=[],
type="smoke"
)
def setup(self):
pass
def case(self):
print("Test2 running")
def teardown(self):
pass

View File

@ -0,0 +1,37 @@
"""
Copyright 2018 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 icetea_lib.bench import Bench
class Testcase(Bench):
def __init__(self):
Bench.__init__(self,
name="test_print",
title="Test icetea integration",
status="released",
purpose="Just for testing scripts",
component=[],
type="smoke"
)
def setup(self):
pass
def case(self):
print("Test running")
def teardown(self):
pass

View File

@ -0,0 +1 @@
This folder contains hardware test data for icetea integration

View File

@ -0,0 +1,49 @@
"""
Copyright 2018 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 icetea_lib.bench import Bench
class Testcase(Bench):
def __init__(self):
Bench.__init__(self,
name="test_K64F_only",
title="Test a test case which have only K64F support",
status="released",
purpose="Just for testing scripts",
component=[],
type="smoke",
requirements={
"duts": {
'*': {
"count": 1,
"type": "hardware",
"allowed_platforms": ['K64F'],
"application": {
"name": "TEST_APPS-device-exampleapp"
}
}
}
}
)
def setup(self):
pass
def case(self):
pass
def teardown(self):
pass

View File

@ -0,0 +1,58 @@
"""
Copyright 2018 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 icetea_lib.bench import Bench
class Testcase(Bench):
def __init__(self):
Bench.__init__(self,
name="test_predefined_platforms",
title="Test a test case which have support for multiple platforms",
status="released",
purpose="Just for testing scripts",
component=[],
type="regression",
requirements={
"duts": {
'*': {
"count": 1,
"type": "hardware",
"allowed_platforms": [
"LPC1768", "KL25Z", "K64F", "K66F", "K22F", "LPC4088", "LPC1549",
"NUCLEO_F072RB", "NUCLEO_F091RC", "NUCLEO_F302R8", "NUCLEO_F303K8",
"NUCLEO_F303RE", "NUCLEO_F207ZG", "NUCLEO_F334R8", "NUCLEO_F303ZE",
"NUCLEO_L053R8", "DISCO_L072CZ_LRWAN1", "NUCLEO_L073RZ", "NUCLEO_L152RE",
"NUCLEO_F410RB", "NUCLEO_F446RE", "NUCLEO_F446ZE", "NUCLEO_F429ZI",
"DISCO_F407VG", "NUCLEO_F746ZG", "NUCLEO_L476RG", "DISCO_L053C8", "DISCO_F334C8",
"DISCO_L475VG_IOT01A", "DISCO_L476VG", "DISCO_F469NI", "DISCO_F429ZI",
"DISCO_F769NI", "ARCH_MAX", "MAX32600MBED", "MOTE_L152RC", "B96B_F446VE"
],
"application": {
"name": "TEST_APPS-device-exampleapp"
}
}
}
}
)
def setup(self):
pass
def case(self):
pass
def teardown(self):
pass

View File

View File

@ -0,0 +1,3 @@
{
"builds": []
}

View File

@ -0,0 +1,102 @@
"""
Copyright 2018 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 os.path import realpath, join, dirname, isfile
import subprocess
"""
Tests for run_icetea.py
"""
this_file_dir = dirname(realpath(__file__))
hw_test_dir = join(this_file_dir, 'TEST_DIR_HW')
test_dir = join(this_file_dir, 'TEST_DIR')
empty_build_data = join(this_file_dir, 'empty_build_data.json')
test_suite = join(this_file_dir, 'test_suite.json')
run_icetea_py = join(dirname(dirname(this_file_dir)), 'run_icetea.py')
assert isfile(run_icetea_py)
def _execute_icetea(*params):
command = ["python", run_icetea_py] + list(params)
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stout, sterr = process.communicate()
status = process.poll()
if status != 0:
raise Exception("Error with {}, \nreturn code: {}, \nerror message: {}, \noutput:{}".format(
" ".join(command), status, sterr, stout
))
return stout.decode()
def test_help():
"""
Just test that something works
:return:
"""
_execute_icetea('--help')
def test_list_tests_k64f():
out = _execute_icetea('--compile-list', '--mcu', 'K64F', '--toolchain', 'GCC_ARM', '--tcdir', hw_test_dir)
assert 'test_K64F_only' in out
assert 'test_predefined_platforms' in out
def test_list_tests_nucleo_l073rz():
out = _execute_icetea('--compile-list', '--mcu', 'NUCLEO_L073RZ', '--toolchain', 'GCC_ARM', '--tcdir', hw_test_dir)
assert 'test_predefined_platforms' in out
assert 'test_K64F_only' not in out
def test_run():
out = _execute_icetea('--mcu', 'K64F', '--toolchain', 'GCC_ARM', '--tcdir', test_dir, '--build-data',
empty_build_data, '--test-suite', test_suite, '--ignore-checks')
assert 'test_print' in out
assert 'test_pass' in out
def test_run_by_name():
out = _execute_icetea('--mcu', 'K64F', '--toolchain', 'GCC_ARM', '--tcdir', test_dir, '--build-data',
empty_build_data, '--test-suite', test_suite, '--tests-by-name', 'test_pass',
'--ignore-checks')
assert 'test_pass' in out
assert 'test_print' not in out
def test_run_hw_with_not_build_tests():
"""
When test binaries are not found tests will be skipped
:return:
"""
out = _execute_icetea('--mcu', 'K64F', '--toolchain', 'GCC_ARM', '--tcdir', hw_test_dir, '--build-data',
empty_build_data, '--test-suite', test_suite)
output_lines = out.split('\n')
# Assert that
temp = list(filter(lambda x: 'test_K64F_only' in x, output_lines))[0]
assert 'skip' in temp
temp = list(filter(lambda x: 'test_predefined_platforms' in x, output_lines))[0]
assert 'skip' in temp
def test_data_validation():
exception_happened = False
try:
_execute_icetea('--mcu', 'K64F', '--toolchain', 'GCC_ARM', '--tcdir', test_dir, '--build-data',
empty_build_data, '--test-suite', test_suite)
except BaseException:
exception_happened = True
assert exception_happened

View File

@ -0,0 +1,144 @@
"""
Copyright 2018 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 os
import sys
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..",
".."))
sys.path.insert(0, ROOT)
from tools.run_icetea import find_build_from_build_data, filter_test_by_build_data, filter_test_by_name, \
get_application_list
"""
Unit tests for run_icetea.py
"""
test_build_data = {
'builds': [
{
"id": "TEST_APPS-DEVICE-SOCKET_APP",
"target_name": "K64F",
"toolchain_name": "GCC_ARM"
}
]
}
def test_find_build_from_build_data_empty():
assert find_build_from_build_data(build_data={'builds': []}, id="something", target="K64F",
toolchain="GCC_ARM") is None
def test_find_build_from_build_data_wrong_target():
assert find_build_from_build_data(build_data=test_build_data, id="TEST_APPS-DEVICE-SOCKET_APP", target="AAAA",
toolchain="GCC_ARM") is None
def test_find_build_from_build_data():
assert find_build_from_build_data(build_data=test_build_data, id="TEST_APPS-DEVICE-SOCKET_APP", target="K64F",
toolchain="GCC_ARM") is not None
icetea_json_output = [
{
"status": "released",
"requirements": {
"duts": {
"1": {
"nick": "dut1"
},
"*": {
"count": 1,
"application": {
"bin": None,
"name": "TEST_APPS-device-socket_app"
},
"type": "hardware"
}
},
"external": {
"apps": []
}
},
"name": "UDPSOCKET_BIND_PORT",
"filepath": "/Users/test/mbed-os/TEST_APPS/testcases/SOCKET_BIND_PORT.py",
"title": "udpsocket open and bind port",
"component": [
"mbed-os",
"netsocket"
],
"compatible": {
"framework": {
"version": ">=1.0.0",
"name": "Icetea"
},
"hw": {
"value": True
},
"automation": {
"value": True
}
},
"subtype": "socket",
"purpose": "Verify UDPSocket can be created, opened and port binded",
"type": "smoke",
"sub_type": None
}
]
def test_filter_test_by_build_data_when_data_is_empty():
assert filter_test_by_build_data(
icetea_json_output=icetea_json_output,
build_data=None,
target="K64F",
toolchain="GCC_ARM"
) == icetea_json_output
def test_filter_test_by_build_data():
temp = filter_test_by_build_data(
icetea_json_output=icetea_json_output,
build_data=test_build_data,
target="K64F",
toolchain="GCC_ARM"
)
assert len(temp) > 0
def test_filter_test_by_name():
assert len(filter_test_by_name(icetea_json_output, ['UDPSOCKET_BIND_PORT'])) > 0
def test_filter_test_by_name_when_not_found():
assert filter_test_by_name(icetea_json_output, ['AAA']) == list()
def test_filter_test_by_name_when_name_is_empty():
assert filter_test_by_name(icetea_json_output, None) == icetea_json_output
def test_get_application_list():
assert 'TEST_APPS-device-socket_app' in get_application_list(icetea_json_output, ['UDPSOCKET_BIND_PORT'])
def test_get_application_list_not_found():
assert 'TEST_APPS-device-socket_app' not in get_application_list(icetea_json_output, ['SOMETHING_ELSE'])
def test_get_application_list_none():
assert 'TEST_APPS-device-socket_app' in get_application_list(icetea_json_output, None)

View File

@ -2066,13 +2066,15 @@ def get_test_config(config_name, target_name):
# Otherwise find the path to configuration file based on mbed OS interface
return TestConfig.get_config_path(config_name, target_name)
def find_tests(base_dir, target_name, toolchain_name, app_config=None):
def find_tests(base_dir, target_name, toolchain_name, icetea, greentea, app_config=None):
""" Finds all tests in a directory recursively
base_dir: path to the directory to scan for tests (ex. 'path/to/project')
target_name: name of the target to use for scanning (ex. 'K64F')
toolchain_name: name of the toolchain to use for scanning (ex. 'GCC_ARM')
options: Compile options to pass to the toolchain (ex. ['debug-info'])
app_config - location of a chosen mbed_app.json file
:param base_dir: path to the directory to scan for tests (ex. 'path/to/project')
:param target_name: name of the target to use for scanning (ex. 'K64F')
:param toolchain_name: name of the toolchain to use for scanning (ex. 'GCC_ARM')
:param icetea: icetea enabled
:param greentea: greentea enabled
:param app_config - location of a chosen mbed_app.json file
returns a dictionary where keys are the test name, and the values are
lists of paths needed to biuld the test.
@ -2089,38 +2091,56 @@ def find_tests(base_dir, target_name, toolchain_name, app_config=None):
base_resources = Resources(MockNotifier(), collect_ignores=True)
base_resources.scan_with_config(base_dir, config)
dirs = [d for d in base_resources.ignored_dirs if basename(d) == 'TESTS']
ignoreset = MbedIgnoreSet()
if greentea:
dirs = [d for d in base_resources.ignored_dirs if basename(d) == 'TESTS']
ignoreset = MbedIgnoreSet()
for directory in dirs:
ignorefile = join(directory, IGNORE_FILENAME)
if isfile(ignorefile):
ignoreset.add_mbedignore(directory, ignorefile)
for test_group_directory in os.listdir(directory):
grp_dir = join(directory, test_group_directory)
if not isdir(grp_dir) or ignoreset.is_ignored(grp_dir):
continue
grpignorefile = join(grp_dir, IGNORE_FILENAME)
if isfile(grpignorefile):
ignoreset.add_mbedignore(grp_dir, grpignorefile)
for test_case_directory in os.listdir(grp_dir):
d = join(directory, test_group_directory, test_case_directory)
if not isdir(d) or ignoreset.is_ignored(d):
for directory in dirs:
ignorefile = join(directory, IGNORE_FILENAME)
if isfile(ignorefile):
ignoreset.add_mbedignore(directory, ignorefile)
for test_group_directory in os.listdir(directory):
grp_dir = join(directory, test_group_directory)
if not isdir(grp_dir) or ignoreset.is_ignored(grp_dir):
continue
special_dirs = ['host_tests', 'COMMON']
if test_group_directory not in special_dirs and test_case_directory not in special_dirs:
test_name = test_path_to_name(d, base_dir)
tests[(test_name, directory, test_group_directory, test_case_directory)] = [d]
if test_case_directory == 'COMMON':
def predicate(base_pred, group_pred, name_base_group_case):
grpignorefile = join(grp_dir, IGNORE_FILENAME)
if isfile(grpignorefile):
ignoreset.add_mbedignore(grp_dir, grpignorefile)
for test_case_directory in os.listdir(grp_dir):
d = join(directory, test_group_directory, test_case_directory)
if not isdir(d) or ignoreset.is_ignored(d):
continue
special_dirs = ['host_tests', 'COMMON']
if test_group_directory not in special_dirs and test_case_directory not in special_dirs:
test_name = test_path_to_name(d, base_dir)
tests[(test_name, directory, test_group_directory, test_case_directory)] = [d]
if test_case_directory == 'COMMON':
def predicate(base_pred, group_pred, name_base_group_case):
(name, base, group, case) = name_base_group_case
return base == base_pred and group == group_pred
commons.append((functools.partial(predicate, directory, test_group_directory), d))
if test_group_directory == 'COMMON':
def predicate(base_pred, name_base_group_case):
(name, base, group, case) = name_base_group_case
return base == base_pred and group == group_pred
commons.append((functools.partial(predicate, directory, test_group_directory), d))
if test_group_directory == 'COMMON':
def predicate(base_pred, name_base_group_case):
(name, base, group, case) = name_base_group_case
return base == base_pred
commons.append((functools.partial(predicate, directory), grp_dir))
return base == base_pred
commons.append((functools.partial(predicate, directory), grp_dir))
if icetea:
dirs = [d for d in base_resources.ignored_dirs if basename(d) == 'TEST_APPS']
for directory in dirs:
if not isdir(directory):
continue
for subdir in os.listdir(directory):
d = join(directory, subdir)
if not isdir(d):
continue
if 'device' == subdir:
for test_dir in os.listdir(d):
test_dir_path = join(d, test_dir)
test_name = test_path_to_name(test_dir_path, base_dir)
tests[(test_name, directory, subdir, test_dir)] = [test_dir_path]
# Apply common directories
for pred, path in commons:
@ -2131,6 +2151,7 @@ def find_tests(base_dir, target_name, toolchain_name, app_config=None):
# Drop identity besides name
return {name: paths for (name, _, _, _), paths in six.iteritems(tests)}
def print_tests(tests, format="list", sort=True):
"""Given a dictionary of tests (as returned from "find_tests"), print them
in the specified format"""
@ -2235,7 +2256,8 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
"base_path": base_path,
"baud_rate": baud_rate,
"binary_type": "bootable",
"tests": {}
"tests": {},
"test_apps": {}
}
result = True
@ -2314,7 +2336,8 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
'bin_file' in worker_result):
bin_file = norm_relative_path(worker_result['bin_file'], execution_directory)
test_build['tests'][worker_result['kwargs']['project_id']] = {
test_key = 'test_apps' if 'test_apps-' in worker_result['kwargs']['project_id'] else 'tests'
test_build[test_key][worker_result['kwargs']['project_id']] = {
"binaries": [
{
"path": bin_file
@ -2357,4 +2380,4 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
def test_spec_from_test_builds(test_builds):
return {
"builds": test_builds
}
}

View File

@ -3,6 +3,9 @@
"HEAPBLOCKDEVICE": "HeapBlockDevice.json",
"HEAPBLOCKDEVICE_AND_ETHERNET": "HeapBlockDeviceAndEthernetInterface.json",
"HEAPBLOCKDEVICE_AND_WIFI": "HeapBlockDeviceAndWifiInterface.json",
"ODIN_WIFI" : "OdinInterface.json",
"ODIN_ETHERNET" : "Odin_EthernetInterface.json",
"REALTEK_WIFI" : "RealtekInterface.json",
"ESP8266_WIFI" : "ESP8266Interface.json",
"ISM43362_WIFI" : "ISM43362Interface.json",
"IDW0XX1_WIFI" : "SpwfSAInterface.json",

View File

@ -9,11 +9,11 @@
},
"K64F": {
"default_test_configuration": "HEAPBLOCKDEVICE_AND_ETHERNET",
"test_configurations": ["HEAPBLOCKDEVICE_AND_ETHERNET", "ESP8266_WIFI", "ETHERNET"]
"test_configurations": ["HEAPBLOCKDEVICE_AND_ETHERNET", "MAC_TESTER", "ESP8266_WIFI", "ETHERNET"]
},
"NUCLEO_F429ZI": {
"default_test_configuration": "HEAPBLOCKDEVICE_AND_ETHERNET",
"test_configurations": ["HEAPBLOCKDEVICE_AND_ETHERNET"]
"test_configurations": ["HEAPBLOCKDEVICE_AND_ETHERNET", "MAC_TESTER"]
},
"DISCO_L475VG_IOT01A": {
"default_test_configuration": "NONE",

View File

@ -179,6 +179,27 @@ def mkdir(path):
makedirs(path)
def write_json_to_file(json_data, file_name):
"""
Write json content in file
:param json_data:
:param file_name:
:return:
"""
# Create the target dir for file if necessary
test_spec_dir = os.path.dirname(file_name)
if test_spec_dir:
mkdir(test_spec_dir)
try:
with open(file_name, 'w') as f:
f.write(json.dumps(json_data, indent=2))
except IOError as e:
print("[ERROR] Error writing test spec to file")
print(e)
def copy_file(src, dst):
""" Implement the behaviour of "shutil.copy(src, dst)" without copying the
permissions (this was causing errors with directories mounted with samba)