diff --git a/.gitmodules b/.gitmodules index 5dddd18..98a1318 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,6 +13,6 @@ [submodule "extras/spiffs/spiffs"] path = extras/spiffs/spiffs url = https://github.com/pellepl/spiffs.git -[submodule "examples/posix_fs/fs-test"] - path = examples/posix_fs/fs-test +[submodule "tests/spiffs/fs-test"] + path = tests/spiffs/fs-test url = https://github.com/sheinz/fs-test diff --git a/extras/test/component.mk b/extras/test/component.mk new file mode 100644 index 0000000..f808ad9 --- /dev/null +++ b/extras/test/component.mk @@ -0,0 +1,8 @@ +# Component makefile for extras/test + +INC_DIRS += $(test_ROOT).. + +# args for passing into compile rule generation +test_SRC_DIR = $(test_ROOT) + +$(eval $(call component_compile_rules,test)) diff --git a/extras/test/run_test.py b/extras/test/run_test.py new file mode 100644 index 0000000..0efd0f8 --- /dev/null +++ b/extras/test/run_test.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This script reads specified serial port for test output. If the test is +# successfully finished the script returns 0 otherwise -1. The script also +# checks for time-out. The timeout value is received from the test itself +# during test initialization. +# +# The script runs as many instances as many serial devices provided. +# +# Usage: +# > run_test.py /dev/ttyUSB0 +# or +# > run_test.py /dev/ttyUSB0 /dev/ttyUSB1 /dev/ttyUSB2 +# if three devices are connected and should be tested simultaneously +# + + +import serial +import re +import sys +import time +import threading + + +class Test(threading.Thread): + def __init__(self, port, baudrate): + threading.Thread.__init__(self) + print("init: {}".format(port)) + self._port = serial.Serial(port, baudrate, timeout=1) + self._timeout = None + self.result = None + + def wait_start(self): + skip_lines = 100 + while skip_lines > 0: + self._port.write('START\n'.encode()) + l = self._port.readline() + if 'TEST_INIT: TIMEOUT=' in l: + res = re.findall(r'\d+', l) + if len(res) != 1: + print("Invalid timeout format") + self.result = False + return + else: + self._timeout = int(res[0]) + break + else: + sys.stdout.write(l) + skip_lines -= 1 + if self._timeout is None: + print("Device is not responding") + self.result = False + else: + print("Timeout: {}".format(self._timeout)) + self.result = True + + def test(self): + end_time = time.time() + self._timeout + result = None + while end_time > time.time(): + l = self._port.readline() + if 'TEST_FINISH: ' in l: + if 'SUCCESS' in l: + print("Test finished successfully") + result = True + break + elif 'FAIL' in l: + print("Test failed") + result = False + else: + print("Unknown status") + result = False + else: + if l is not None: + sys.stdout.write(l) + if result is None: + print("Test timeout") + return result + + def run(self): + self.wait_start() + if self.result is not None: + self.result = self.test() + + def __del__(self): + self._port.close() + + +def main(): + if len(sys.argv) <= 1: + print("Need at least one serial device to test a device") + exit(-1) + + tasks = [] + for dev in sys.argv[1:]: + t = Test(dev, 115200) + t.start() + tasks.append(t) + + result = True + for t in tasks: + t.join() + result = result and t.result + + if result: + print("Test: SUCCESS") + else: + print("Test: FAIL") + +if __name__ == "__main__": + main() diff --git a/extras/test/test.c b/extras/test/test.c new file mode 100644 index 0000000..0a2a72e --- /dev/null +++ b/extras/test/test.c @@ -0,0 +1,57 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 sheinz (https://github.com/sheinz) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "test.h" +#include +#include "task.h" +#include "esp/uart.h" + +bool test_result = true; + +void test_init(uint32_t timeout) +{ + char buf[256]; + int data_len = 0; + buf[data_len] = 0; + + uart_set_baud(0, 115200); + + while (true) { + int c = getchar(); + if (c == EOF || c == '\n' || c == '\r') { + if (strcmp(buf, "START") == 0) { + break; + } else { + data_len = 0; + buf[data_len] = 0; + } + } else{ + buf[data_len++] = c; + buf[data_len] = 0; + } + taskYIELD(); + } + + printf("TEST_INIT: TIMEOUT=%ds\n", timeout); \ +} diff --git a/extras/test/test.h b/extras/test/test.h new file mode 100644 index 0000000..2157e56 --- /dev/null +++ b/extras/test/test.h @@ -0,0 +1,62 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 sheinz (https://github.com/sheinz) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef __TEST_H__ +#define __TEST_H__ + +#include +#include + +#include "FreeRTOS.h" + +extern bool test_result; + +#define TEST_CHECK(x, msg) \ + if (!(x)) { \ + printf("FAIL:%s:%d:%s\n", __FILE__, __LINE__, (msg)); \ + test_result = false; \ + } + +#define TEST_ASSERT(x, msg) \ + if (!(x)) { \ + printf("FAIL:%s:%d:%s\n", __FILE__, __LINE__, (msg)); \ + printf("TEST_FINISH: FAIL\n"); \ + test_result = false; \ + vTaskDelete(NULL); \ + taskYIELD(); \ + } + +#define TEST_FAIL(msg) TEST_ASSERT(false, msg) + +#define TEST_FINISH() \ + if (test_result) { \ + printf("TEST_FINISH: SUCCESS\n"); \ + } else { \ + printf("TEST_FINISH: FAIL\n"); \ + } + +#define TEST_INIT(timeout) test_init(timeout) + +void test_init(uint32_t timeout); + +#endif // __TEST_H__ diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..5f16210 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,27 @@ +TESTS = $(shell find $(dir $(lastword $(MAKEFILE_LIST))) \ + -mindepth 2 -maxdepth 2 -name Makefile | sed s/Makefile//g) + +# Generate some dummy .dummybuild/.dummyrebuild target files +TESTS_BUILD = $(patsubst %,%.dummybuild,$(TESTS)) +TESTS_TEST = $(patsubst %,%.dummytest,$(TESTS)) + +all: $(TESTS_BUILD) + +$(TESTS_TEST): $(TESTS_BUILD) + +test: $(TESTS_TEST) + +clean: $(TESTS_BUILD) + +clean: GOAL=clean + +%.dummybuild: + $(MAKE) -C $(dir $@) $(GOAL) + +%.dummytest: + $(MAKE) -C $(dir $@) flash + $(MAKE) -C $(dir $@) run_test + +.PHONY: all test +.NOTPARALLEL: +.ONESHELL: diff --git a/tests/basic_wifi/Makefile b/tests/basic_wifi/Makefile new file mode 100644 index 0000000..a44bc91 --- /dev/null +++ b/tests/basic_wifi/Makefile @@ -0,0 +1,29 @@ + +STATION_PORT := $(ESPPORT) +AP_PORT := /dev/tty.SLAB_USBtoUART59 + +all: station ap + +flash: ap station + +clean: ap station + +flash: GOAL=flash + +clean: GOAL=clean + +station: ESPPORT=$(STATION_PORT) + +ap: ESPPORT=$(AP_PORT) + +station: + $(MAKE) -C station $(GOAL) + +ap: + $(MAKE) -C ap $(GOAL) + +run_test: + python ../../extras/test/run_test.py $(STATION_PORT) $(AP_PORT) + +.PHONY: all station ap run_test flash clean + diff --git a/tests/basic_wifi/ap/Makefile b/tests/basic_wifi/ap/Makefile new file mode 100644 index 0000000..2f6465f --- /dev/null +++ b/tests/basic_wifi/ap/Makefile @@ -0,0 +1,5 @@ +# Makefile for access_point example +PROGRAM=access_point +EXTRA_COMPONENTS=extras/dhcpserver extras/test + +include ../../../common.mk diff --git a/tests/basic_wifi/ap/ap.c b/tests/basic_wifi/ap/ap.c new file mode 100644 index 0000000..22ee732 --- /dev/null +++ b/tests/basic_wifi/ap/ap.c @@ -0,0 +1,81 @@ +/** + * This test creates a WiFi access point and listens on port 23 for incomming + * connection. When incomming connection occurs it sends a string and waits for + * a response. + */ +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "test/test.h" + +#define AP_SSID "esp-open-rtos-ap" +#define AP_PSK "esp-open-rtos" +#define SERVER_PORT 23 + + +static void server_task(void *pvParameters) +{ + struct netconn *nc = netconn_new(NETCONN_TCP); + TEST_ASSERT(nc, "failed to allocate socket.") + + netconn_bind(nc, IP_ADDR_ANY, SERVER_PORT); + netconn_listen(nc); + + struct netconn *client = NULL; + err_t err = netconn_accept(nc, &client); + TEST_ASSERT(err == ERR_OK, "Error accepting connection"); + + ip_addr_t client_addr; + uint16_t port_ignore; + netconn_peer(client, &client_addr, &port_ignore); + + char buf[80]; + snprintf(buf, sizeof(buf), "test ping\r\n"); + printf("writing data to socket: %s\n", buf); + netconn_write(client, buf, strlen(buf), NETCONN_COPY); + vTaskDelay(1000 / portTICK_RATE_MS); + + netconn_delete(client); + + TEST_FINISH(); +} + +void user_init(void) +{ + TEST_INIT(30); + + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + sdk_wifi_set_opmode(SOFTAP_MODE); + struct ip_info ap_ip; + IP4_ADDR(&ap_ip.ip, 172, 16, 0, 1); + IP4_ADDR(&ap_ip.gw, 0, 0, 0, 0); + IP4_ADDR(&ap_ip.netmask, 255, 255, 0, 0); + sdk_wifi_set_ip_info(1, &ap_ip); + + struct sdk_softap_config ap_config = { + .ssid = AP_SSID, + .ssid_hidden = 0, + .channel = 3, + .ssid_len = strlen(AP_SSID), + .authmode = AUTH_WPA_WPA2_PSK, + .password = AP_PSK, + .max_connection = 3, + .beacon_interval = 100, + }; + sdk_wifi_softap_set_config(&ap_config); + + ip_addr_t first_client_ip; + IP4_ADDR(&first_client_ip, 172, 16, 0, 2); + dhcpserver_start(&first_client_ip, 4); + + xTaskCreate(server_task, (signed char *)"setver_task", 512, NULL, 2, NULL); +} diff --git a/tests/basic_wifi/station/Makefile b/tests/basic_wifi/station/Makefile new file mode 100644 index 0000000..5ec1b55 --- /dev/null +++ b/tests/basic_wifi/station/Makefile @@ -0,0 +1,3 @@ +PROGRAM=http_get +EXTRA_COMPONENTS=extras/test +include ../../../common.mk diff --git a/tests/basic_wifi/station/station.c b/tests/basic_wifi/station/station.c new file mode 100644 index 0000000..2629de5 --- /dev/null +++ b/tests/basic_wifi/station/station.c @@ -0,0 +1,95 @@ +/** + * This test connects to an access point and connects to a server on port 23. + * It waits for a server data and responds with another data. + */ +#include "espressif/esp_common.h" +#include "esp/uart.h" + +#include + +#include "FreeRTOS.h" +#include "task.h" + +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include "lwip/netdb.h" +#include "lwip/dns.h" + +#include "test/test.h" + +#define AP_SSID "esp-open-rtos-ap" +#define AP_PSK "esp-open-rtos" +#define SERVER "172.16.0.1" +#define PORT 23 + +void connect_task(void *pvParameters) +{ + printf("HTTP get task starting...\r\n"); + + while(1) { + struct sockaddr_in serv_addr; + uint8_t buf[256]; + + // wait for wifi connection + while (sdk_wifi_station_get_connect_status() != STATION_GOT_IP) { + vTaskDelay(100 / portTICK_RATE_MS); + } + + int s = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT(s > 0, "failed to allocate socket."); + + bzero(&serv_addr, sizeof(serv_addr)); + serv_addr.sin_port = htons(PORT); + serv_addr.sin_family = AF_INET; + + TEST_ASSERT(inet_aton(SERVER, &serv_addr.sin_addr.s_addr), + "failed to set IP address\n"); + + printf("allocated socket\n"); + + TEST_ASSERT(connect(s, (struct sockaddr*)&serv_addr, sizeof(serv_addr)), + "socket connect failed"); + + printf("connected\n"); + + bzero(buf, 256); + + int r = 0; + for (int i = 0; i < 10; i++) { + r = read(s, buf, 256); + if (r > 0) { + break; + } + vTaskDelay(1000 / portTICK_RATE_MS); + } + TEST_CHECK(r > 0, "no data received"); + + TEST_CHECK(strcmp((const char*)buf, "test ping\r\n") == 0, + "received wrong data"); + + close(s); + + TEST_FINISH(); + break; + } +} + +void user_init(void) +{ + TEST_INIT(30); + + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + struct sdk_station_config config = { + .ssid = AP_SSID, + .password = AP_PSK, + }; + + /* required to call wifi_set_opmode before station_set_config */ + sdk_wifi_set_opmode(STATION_MODE); + sdk_wifi_station_set_config(&config); + + xTaskCreate(&connect_task, (signed char *)"connect_task", 256, NULL, 2, NULL); +} + diff --git a/tests/ets_timers/Makefile b/tests/ets_timers/Makefile new file mode 100644 index 0000000..585f471 --- /dev/null +++ b/tests/ets_timers/Makefile @@ -0,0 +1,10 @@ +PROGRAM=ets_timers_test + +EXTRA_COMPONENTS = extras/test + +include ../../common.mk + +run_test: + python ../../extras/test/run_test.py $(ESPPORT) + +.PHONY: run_test diff --git a/tests/ets_timers/timers.c b/tests/ets_timers/timers.c new file mode 100644 index 0000000..2ec1e58 --- /dev/null +++ b/tests/ets_timers/timers.c @@ -0,0 +1,53 @@ +#include +#include +#include "FreeRTOS.h" +#include "task.h" +#include "etstimer.h" + +#include "test/test.h" + +static ETSTimer test_timer_0; + +static int timer_0_counter = 0; + +static uint32_t test_start_time; + + +static uint32_t get_current_time() +{ + return timer_get_count(FRC2) / 5000; // to get roughly 1ms resolution +} + +static void timer_0_cb(void *arg) +{ + uint32_t v = (uint32_t)arg; + uint32_t delay = get_current_time() - test_start_time; + + TEST_CHECK(v == 0xAA, "Timer 0 argument invalid"); + TEST_CHECK(timer_0_counter == 0, "Timer 0 repeat error"); + + // Timer should fire in 100ms + TEST_CHECK(delay > 80, "Timer 0 time wrong"); + TEST_CHECK(delay < 120, "Timer 0 time wrong"); + + timer_0_counter++; +} + +void test_task(void *pvParameters) +{ + test_start_time = get_current_time(); + + sdk_ets_timer_disarm(&test_timer_0); + sdk_ets_timer_setfn(&test_timer_0, timer_0_cb, (void*)0xAA); + sdk_ets_timer_arm(&test_timer_0, 100, false); + + // Finish the test and report the result + TEST_FINISH(); +} + +void user_init(void) +{ + TEST_INIT(10); // 10 seconds timeout + + xTaskCreate(test_task, (signed char *)"test_task", 256, NULL, 2, NULL); +} diff --git a/tests/example/Makefile b/tests/example/Makefile new file mode 100644 index 0000000..bd2ec39 --- /dev/null +++ b/tests/example/Makefile @@ -0,0 +1,10 @@ +PROGRAM=example_test + +EXTRA_COMPONENTS = extras/test + +include ../../common.mk + +run_test: + python ../../extras/test/run_test.py $(ESPPORT) + +.PHONY: run_test diff --git a/tests/example/example_test.c b/tests/example/example_test.c new file mode 100644 index 0000000..82a3ccd --- /dev/null +++ b/tests/example/example_test.c @@ -0,0 +1,25 @@ +#include +#include +#include "FreeRTOS.h" +#include "task.h" + +#include "test/test.h" + +void test_task(void *pvParameters) +{ + // Test will continue even if check is failed + TEST_CHECK(2 + 2 == 4, "math doesn't work"); + + // Test will end imidiatelly if assertion is failed + TEST_ASSERT(strlen("test") == 4, "strlen doesn't work"); + + // Finish the test and report the result + TEST_FINISH(); +} + +void user_init(void) +{ + TEST_INIT(5); // 5 seconds timeout + + xTaskCreate(test_task, (signed char *)"test_task", 256, NULL, 2, NULL); +} diff --git a/examples/posix_fs/Makefile b/tests/spiffs/Makefile similarity index 60% rename from examples/posix_fs/Makefile rename to tests/spiffs/Makefile index bf45ed7..f6f5d79 100644 --- a/examples/posix_fs/Makefile +++ b/tests/spiffs/Makefile @@ -1,7 +1,7 @@ PROGRAM=posix_fs_example PROGRAM_EXTRA_SRC_FILES=./fs-test/fs_test.c -EXTRA_COMPONENTS = extras/spiffs +EXTRA_COMPONENTS = extras/spiffs extras/test FLASH_SIZE = 32 # spiffs configuration @@ -9,3 +9,8 @@ SPIFFS_BASE_ADDR = 0x200000 SPIFFS_SIZE = 0x100000 include ../../common.mk + +run_test: + python ../../extras/test/run_test.py $(ESPPORT) + +.PHONY: run_test diff --git a/examples/posix_fs/README.md b/tests/spiffs/README.md similarity index 100% rename from examples/posix_fs/README.md rename to tests/spiffs/README.md diff --git a/examples/posix_fs/fs-test b/tests/spiffs/fs-test similarity index 100% rename from examples/posix_fs/fs-test rename to tests/spiffs/fs-test diff --git a/examples/posix_fs/posix_fs_example.c b/tests/spiffs/posix_fs_example.c similarity index 57% rename from examples/posix_fs/posix_fs_example.c rename to tests/spiffs/posix_fs_example.c index 2f5ad14..5ac081b 100644 --- a/examples/posix_fs/posix_fs_example.c +++ b/tests/spiffs/posix_fs_example.c @@ -11,6 +11,8 @@ #include "fs-test/fs_test.h" +#include "test/test.h" + static fs_time_t get_current_time() { return timer_get_count(FRC2) / 5000; // to get roughly 1ms resolution @@ -28,28 +30,21 @@ void test_task(void *pvParameters) } esp_spiffs_mount(); - while (1) { - vTaskDelay(5000 / portTICK_RATE_MS); - if (fs_load_test_run(100)) { - printf("PASS\n"); - } else { - printf("FAIL\n"); - } + TEST_CHECK(fs_load_test_run(100), "Load test failed"); - vTaskDelay(5000 / portTICK_RATE_MS); - float write_rate, read_rate; - if (fs_speed_test_run(get_current_time, &write_rate, &read_rate)) { - printf("Read speed: %.0f bytes/s\n", read_rate * 1000); - printf("Write speed: %.0f bytes/s\n", write_rate * 1000); - } else { - printf("FAIL\n"); - } + float write_rate, read_rate; + if (fs_speed_test_run(get_current_time, &write_rate, &read_rate)) { + printf("Read speed: %.0f bytes/s\n", read_rate * 1000); + printf("Write speed: %.0f bytes/s\n", write_rate * 1000); + } else { + TEST_FAIL("Speed test failed"); } + TEST_FINISH(); } void user_init(void) { - uart_set_baud(0, 115200); + TEST_INIT(60); xTaskCreate(test_task, (signed char *)"test_task", 1024, NULL, 2, NULL); }