diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e66b0bec..fd5465a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,11 @@ jobs: with: run: make -C firmware TARGET=local + - name: Run local tests + uses: ./.github/actions/docker + with: + run: make -C firmware TARGET=local APP_TEST=1 run + - name: Build F2 firmware in docker uses: ./.github/actions/docker with: diff --git a/applications/applications.mk b/applications/applications.mk index e088b41c..d199e638 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -15,6 +15,7 @@ CFLAGS += -DAPP_TEST C_SOURCES += $(APP_DIR)/tests/furiac_test.c C_SOURCES += $(APP_DIR)/tests/furi_record_test.c C_SOURCES += $(APP_DIR)/tests/test_index.c +C_SOURCES += $(APP_DIR)/tests/minunit_test.c endif APP_EXAMPLE_BLINK ?= 0 diff --git a/applications/tests/furi_record_test.c b/applications/tests/furi_record_test.c index f59d5c62..b8d0b66f 100644 --- a/applications/tests/furi_record_test.c +++ b/applications/tests/furi_record_test.c @@ -22,10 +22,10 @@ void pipe_record_cb(const void* value, size_t size, void* ctx) { pipe_record_value = *((uint8_t*)value); } -bool test_furi_pipe_record(FuriRecordSubscriber* log) { +bool test_furi_pipe_record() { // 1. create pipe record if(!furi_create("test/pipe", NULL, 0)) { - fuprintf(log, "cannot create record\n"); + printf("cannot create record\n"); return false; } @@ -33,27 +33,27 @@ bool test_furi_pipe_record(FuriRecordSubscriber* log) { FuriRecordSubscriber* pipe_record = furi_open("test/pipe", false, false, pipe_record_cb, NULL, NULL); if(pipe_record == NULL) { - fuprintf(log, "cannot open record\n"); + printf("cannot open record\n"); return false; } const uint8_t WRITE_VALUE = 1; // 3. write data if(!furi_write(pipe_record, &WRITE_VALUE, sizeof(uint8_t))) { - fuprintf(log, "cannot write to record\n"); + printf("cannot write to record\n"); return false; } // 4. check that subscriber get data if(pipe_record_value != WRITE_VALUE) { - fuprintf(log, "wrong value (get %d, write %d)\n", pipe_record_value, WRITE_VALUE); + printf("wrong value (get %d, write %d)\n", pipe_record_value, WRITE_VALUE); return false; } // 5. try to read, get error uint8_t read_value = 0; if(furi_read(pipe_record, &read_value, sizeof(uint8_t))) { - fuprintf(log, "reading from pipe record not allowed\n"); + printf("reading from pipe record not allowed\n"); return false; } @@ -62,7 +62,7 @@ bool test_furi_pipe_record(FuriRecordSubscriber* log) { // 7. try to write, get error if(furi_write(pipe_record, &WRITE_VALUE, sizeof(uint8_t))) { - fuprintf(log, "writing to closed record not allowed\n"); + printf("writing to closed record not allowed\n"); return false; } @@ -87,11 +87,11 @@ void holding_record_cb(const void* value, size_t size, void* ctx) { holding_record_value = *((uint8_t*)value); } -bool test_furi_holding_data(FuriRecordSubscriber* log) { +bool test_furi_holding_data() { // 1. Create holding record uint8_t holder = 0; if(!furi_create("test/holding", (void*)&holder, sizeof(holder))) { - fuprintf(log, "cannot create record\n"); + printf("cannot create record\n"); return false; } @@ -99,43 +99,43 @@ bool test_furi_holding_data(FuriRecordSubscriber* log) { FuriRecordSubscriber* holding_record = furi_open("test/holding", false, false, holding_record_cb, NULL, NULL); if(holding_record == NULL) { - fuprintf(log, "cannot open record\n"); + printf("cannot open record\n"); return false; } const uint8_t WRITE_VALUE = 1; // 3. write data if(!furi_write(holding_record, &WRITE_VALUE, sizeof(uint8_t))) { - fuprintf(log, "cannot write to record\n"); + printf("cannot write to record\n"); return false; } // 4. check that subscriber get data if(holding_record_value != WRITE_VALUE) { - fuprintf(log, "wrong sub value (get %d, write %d)\n", holding_record_value, WRITE_VALUE); + printf("wrong sub value (get %d, write %d)\n", holding_record_value, WRITE_VALUE); return false; } // 5. Read and check data uint8_t read_value = 0; if(!furi_read(holding_record, &read_value, sizeof(uint8_t))) { - fuprintf(log, "cannot read from record\n"); + printf("cannot read from record\n"); return false; } if(read_value != WRITE_VALUE) { - fuprintf(log, "wrong read value (get %d, write %d)\n", read_value, WRITE_VALUE); + printf("wrong read value (get %d, write %d)\n", read_value, WRITE_VALUE); return false; } // 6. Try to write/read wrong size of data if(furi_write(holding_record, &WRITE_VALUE, 100)) { - fuprintf(log, "overflowed write not allowed\n"); + printf("overflowed write not allowed\n"); return false; } if(furi_read(holding_record, &read_value, 100)) { - fuprintf(log, "overflowed read not allowed\n"); + printf("overflowed read not allowed\n"); return false; } @@ -159,12 +159,10 @@ typedef struct { } ConcurrentValue; void furi_concurent_app(void* p) { - FuriRecordSubscriber* log = (FuriRecordSubscriber*)p; - FuriRecordSubscriber* holding_record = furi_open("test/concurrent", false, false, NULL, NULL, NULL); if(holding_record == NULL) { - fuprintf(log, "cannot open record\n"); + printf("cannot open record\n"); furiac_exit(NULL); } @@ -172,7 +170,7 @@ void furi_concurent_app(void* p) { ConcurrentValue* value = (ConcurrentValue*)furi_take(holding_record); if(value == NULL) { - fuprintf(log, "cannot take record\n"); + printf("cannot take record\n"); furi_give(holding_record); furiac_exit(NULL); } @@ -190,11 +188,11 @@ void furi_concurent_app(void* p) { furiac_exit(NULL); } -bool test_furi_concurrent_access(FuriRecordSubscriber* log) { +bool test_furi_concurrent_access() { // 1. Create holding record ConcurrentValue holder = {.a = 0, .b = 0}; if(!furi_create("test/concurrent", (void*)&holder, sizeof(ConcurrentValue))) { - fuprintf(log, "cannot create record\n"); + printf("cannot create record\n"); return false; } @@ -202,19 +200,19 @@ bool test_furi_concurrent_access(FuriRecordSubscriber* log) { FuriRecordSubscriber* holding_record = furi_open("test/concurrent", false, false, NULL, NULL, NULL); if(holding_record == NULL) { - fuprintf(log, "cannot open record\n"); + printf("cannot open record\n"); return false; } // 3. Create second app for interact with it - FuriApp* second_app = furiac_start(furi_concurent_app, "furi concurent app", (void*)log); + FuriApp* second_app = furiac_start(furi_concurent_app, "furi concurent app", NULL); // 4. multiply ConcurrentValue::a for(size_t i = 0; i < 4; i++) { ConcurrentValue* value = (ConcurrentValue*)furi_take(holding_record); if(value == NULL) { - fuprintf(log, "cannot take record\n"); + printf("cannot take record\n"); furi_give(holding_record); return false; } @@ -232,12 +230,12 @@ bool test_furi_concurrent_access(FuriRecordSubscriber* log) { delay(50); if(second_app->handler != NULL) { - fuprintf(log, "second app still alive\n"); + printf("second app still alive\n"); return false; } if(holder.a != holder.b) { - fuprintf(log, "broken integrity: a=%d, b=%d\n", holder.a, holder.b); + printf("broken integrity: a=%d, b=%d\n", holder.a, holder.b); return false; } @@ -252,7 +250,7 @@ TEST: non-existent data TODO: implement this test */ -bool test_furi_nonexistent_data(FuriRecordSubscriber* log) { +bool test_furi_nonexistent_data() { return true; } @@ -310,11 +308,9 @@ void mute_record_state_cb(FlipperRecordState state, void* ctx) { } void furi_mute_parent_app(void* p) { - FuriRecordSubscriber* log = (FuriRecordSubscriber*)p; - // 1. Create pipe record if(!furi_create("test/mute", NULL, 0)) { - fuprintf(log, "cannot create record\n"); + printf("cannot create record\n"); furiac_exit(NULL); } @@ -322,7 +318,7 @@ void furi_mute_parent_app(void* p) { FuriRecordSubscriber* watch_handler = furi_open("test/mute", false, false, mute_record_cb, NULL, NULL); if(watch_handler == NULL) { - fuprintf(log, "cannot open watch handler\n"); + printf("cannot open watch handler\n"); furiac_exit(NULL); } @@ -332,9 +328,9 @@ void furi_mute_parent_app(void* p) { } } -bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { +bool test_furi_mute_algorithm() { // 1. Create "parent" application: - FuriApp* parent_app = furiac_start(furi_mute_parent_app, "parent app", (void*)log); + FuriApp* parent_app = furiac_start(furi_mute_parent_app, "parent app", NULL); delay(2); // wait creating record @@ -342,7 +338,7 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { FuriRecordSubscriber* handler_a = furi_open("test/mute", false, false, NULL, mute_record_state_cb, NULL); if(handler_a == NULL) { - fuprintf(log, "cannot open handler A\n"); + printf("cannot open handler A\n"); return false; } @@ -350,25 +346,25 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { // Try to write data to A and check subscriber if(!furi_write(handler_a, &test_counter, sizeof(uint8_t))) { - fuprintf(log, "write to A failed\n"); + printf("write to A failed\n"); return false; } if(mute_last_value != test_counter) { - fuprintf(log, "value A mismatch: %d vs %d\n", mute_last_value, test_counter); + printf("value A mismatch: %d vs %d\n", mute_last_value, test_counter); return false; } // 3. Open handler B: solo=true, no_mute=true, NULL subscriber. FuriRecordSubscriber* handler_b = furi_open("test/mute", true, true, NULL, NULL, NULL); if(handler_b == NULL) { - fuprintf(log, "cannot open handler B\n"); + printf("cannot open handler B\n"); return false; } // Check A state cb get FlipperRecordStateMute. if(mute_last_state != FlipperRecordStateMute) { - fuprintf(log, "A state is not FlipperRecordStateMute: %d\n", mute_last_state); + printf("A state is not FlipperRecordStateMute: %d\n", mute_last_state); return false; } @@ -376,12 +372,12 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { // Try to write data to A and check that subscriber get no data. (muted) if(furi_write(handler_a, &test_counter, sizeof(uint8_t))) { - fuprintf(log, "A not muted\n"); + printf("A not muted\n"); return false; } if(mute_last_value == test_counter) { - fuprintf(log, "value A must be muted\n"); + printf("value A must be muted\n"); return false; } @@ -389,19 +385,19 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { // Try to write data to B and check that subscriber get data. if(!furi_write(handler_b, &test_counter, sizeof(uint8_t))) { - fuprintf(log, "write to B failed\n"); + printf("write to B failed\n"); return false; } if(mute_last_value != test_counter) { - fuprintf(log, "value B mismatch: %d vs %d\n", mute_last_value, test_counter); + printf("value B mismatch: %d vs %d\n", mute_last_value, test_counter); return false; } // 4. Open hadler C: solo=true, no_mute=false, NULL subscriber. FuriRecordSubscriber* handler_c = furi_open("test/mute", true, false, NULL, NULL, NULL); if(handler_c == NULL) { - fuprintf(log, "cannot open handler C\n"); + printf("cannot open handler C\n"); return false; } @@ -412,7 +408,7 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { // 5. Open handler D: solo=false, no_mute=false, NULL subscriber. FuriRecordSubscriber* handler_d = furi_open("test/mute", false, false, NULL, NULL, NULL); if(handler_d == NULL) { - fuprintf(log, "cannot open handler D\n"); + printf("cannot open handler D\n"); return false; } @@ -428,7 +424,7 @@ bool test_furi_mute_algorithm(FuriRecordSubscriber* log) { // 7. Exit "parent application" if(!furiac_kill(parent_app)) { - fuprintf(log, "kill parent_app fail\n"); + printf("kill parent_app fail\n"); return false; } diff --git a/applications/tests/furiac_test.c b/applications/tests/furiac_test.c index f9cdc201..05eaaeae 100644 --- a/applications/tests/furiac_test.c +++ b/applications/tests/furiac_test.c @@ -23,26 +23,26 @@ void create_kill_app(void* p) { } } -bool test_furi_ac_create_kill(FuriRecordSubscriber* log) { +bool test_furi_ac_create_kill() { uint8_t counter = 0; uint8_t value_a = counter; FuriApp* widget = furiac_start(create_kill_app, "create_kill_app", (void*)&counter); if(widget == NULL) { - fuprintf(log, "create widget fail\n"); + printf("create widget fail\n"); return false; } delay(10); if(!furiac_kill(widget)) { - fuprintf(log, "kill widget fail\n"); + printf("kill widget fail\n"); return false; } if(value_a == counter) { - fuprintf(log, "counter unchanged\n"); + printf("counter unchanged\n"); return false; } @@ -51,7 +51,7 @@ bool test_furi_ac_create_kill(FuriRecordSubscriber* log) { delay(10); if(value_a != counter) { - fuprintf(log, "counter changes after kill (counter = %d vs %d)\n", value_a, counter); + printf("counter changes after kill (counter = %d vs %d)\n", value_a, counter); return false; } @@ -111,7 +111,7 @@ void task_b(void* p) { furiac_exit(p); } -bool test_furi_ac_switch_exit(FuriRecordSubscriber* log) { +bool test_furi_ac_switch_exit() { // init sequence TestSwitchSequence seq; seq.count = 0; @@ -124,7 +124,7 @@ bool test_furi_ac_switch_exit(FuriRecordSubscriber* log) { seq.sequence[seq.count] = '\0'; if(strcmp(seq.sequence, "ABA/") != 0) { - fuprintf(log, "wrong sequence: %s\n", seq.sequence); + printf("wrong sequence: %s\n", seq.sequence); return false; } diff --git a/applications/tests/minunit.h b/applications/tests/minunit.h new file mode 100644 index 00000000..c90a7454 --- /dev/null +++ b/applications/tests/minunit.h @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2012 David Siñuela Pastor, siu.4coders@gmail.com + * + * 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 MINUNIT_MINUNIT_H +#define MINUNIT_MINUNIT_H + +#ifdef __cplusplus + extern "C" { +#endif + +#if defined(_WIN32) +#include +#if defined(_MSC_VER) && _MSC_VER < 1900 + #define snprintf _snprintf + #define __func__ __FUNCTION__ +#endif + +#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) + +/* Change POSIX C SOURCE version for pure c99 compilers */ +#if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200112L +#undef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200112L +#endif + +#include /* POSIX flags */ +#include /* clock_gettime(), time() */ +#include /* gethrtime(), gettimeofday() */ +#include +#include +#include + +#if defined(__MACH__) && defined(__APPLE__) +#include +#include +#endif + +#if __GNUC__ >= 5 && !defined(__STDC_VERSION__) +#define __func__ __extension__ __FUNCTION__ +#endif + +#else + +// #error "Unable to define timers for an unknown OS." + +#endif + +#include +#include + +/* Maximum length of last message */ +#define MINUNIT_MESSAGE_LEN 1024 +/* Accuracy with which floats are compared */ +#define MINUNIT_EPSILON 1E-12 + +#include "minunit_vars_ex.h" + +/* Test setup and teardown function pointers */ +static void (*minunit_setup)(void) = NULL; +static void (*minunit_teardown)(void) = NULL; + +/* Definitions */ +#define MU_TEST(method_name) static void method_name(void) +#define MU_TEST_SUITE(suite_name) static void suite_name(void) + +#define MU__SAFE_BLOCK(block) do {\ + block\ +} while(0) + +/* Run test suite and unset setup and teardown functions */ +#define MU_RUN_SUITE(suite_name) MU__SAFE_BLOCK(\ + suite_name();\ + minunit_setup = NULL;\ + minunit_teardown = NULL;\ +) + +/* Configure setup and teardown functions */ +#define MU_SUITE_CONFIGURE(setup_fun, teardown_fun) MU__SAFE_BLOCK(\ + minunit_setup = setup_fun;\ + minunit_teardown = teardown_fun;\ +) + +/* Test runner */ +#define MU_RUN_TEST(test) MU__SAFE_BLOCK(\ + if (minunit_real_timer==0 && minunit_proc_timer==0) {\ + minunit_real_timer = mu_timer_real();\ + minunit_proc_timer = mu_timer_cpu();\ + }\ + if (minunit_setup) (*minunit_setup)();\ + minunit_status = 0;\ + test();\ + minunit_run++;\ + if (minunit_status) {\ + minunit_fail++;\ + printf("F");\ + printf("\n%s\n", minunit_last_message);\ + }\ + fflush(stdout);\ + if (minunit_teardown) (*minunit_teardown)();\ +) + +/* Report */ +#define MU_REPORT() MU__SAFE_BLOCK(\ + double minunit_end_real_timer;\ + double minunit_end_proc_timer;\ + printf("\n\n%d tests, %d assertions, %d failures\n", minunit_run, minunit_assert, minunit_fail);\ + minunit_end_real_timer = mu_timer_real();\ + minunit_end_proc_timer = mu_timer_cpu();\ + printf("\nFinished in %.8f seconds (real) %.8f seconds (proc)\n\n",\ + minunit_end_real_timer - minunit_real_timer,\ + minunit_end_proc_timer - minunit_proc_timer);\ +) +#define MU_EXIT_CODE minunit_fail + +/* Assertions */ +#define mu_check(test) MU__SAFE_BLOCK(\ + minunit_assert++;\ + if (!(test)) {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, #test);\ + minunit_status = 1;\ + return;\ + } else {\ + printf(".");\ + }\ +) + +#define mu_fail(message) MU__SAFE_BLOCK(\ + minunit_assert++;\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, message);\ + minunit_status = 1;\ + return;\ +) + +#define mu_assert(test, message) MU__SAFE_BLOCK(\ + minunit_assert++;\ + if (!(test)) {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, message);\ + minunit_status = 1;\ + return;\ + } else {\ + printf(".");\ + }\ +) + +#define mu_assert_int_eq(expected, result) MU__SAFE_BLOCK(\ + int minunit_tmp_e;\ + int minunit_tmp_r;\ + minunit_assert++;\ + minunit_tmp_e = (expected);\ + minunit_tmp_r = (result);\ + if (minunit_tmp_e != minunit_tmp_r) {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %d expected but was %d", __func__, __FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r);\ + minunit_status = 1;\ + return;\ + } else {\ + printf(".");\ + }\ +) + +#define mu_assert_int_not_eq(expected, result) MU__SAFE_BLOCK(\ + int minunit_tmp_e;\ + int minunit_tmp_r;\ + minunit_assert++;\ + minunit_tmp_e = (expected);\ + minunit_tmp_r = (result);\ + if (minunit_tmp_e == minunit_tmp_r) {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: expected different results but both were %d", __func__, __FILE__, __LINE__, minunit_tmp_e);\ + minunit_status = 1;\ + return;\ + } else {\ + printf(".");\ + }\ +) + +#define mu_assert_int_greater_than(val, result) MU__SAFE_BLOCK(\ + int minunit_tmp_e;\ + int minunit_tmp_r;\ + minunit_assert++;\ + minunit_tmp_e = (val);\ + minunit_tmp_r = (result);\ + if (val >= minunit_tmp_r) {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %d <= %d", __func__, __FILE__, __LINE__, minunit_tmp_r, minunit_tmp_e);\ + minunit_status = 1;\ + return;\ + } else {\ + printf(".");\ + }\ +) + +#define mu_assert_int_less_than(val, result) MU__SAFE_BLOCK(\ + int minunit_tmp_e;\ + int minunit_tmp_r;\ + minunit_assert++;\ + minunit_tmp_e = (val);\ + minunit_tmp_r = (result);\ + if (val <= minunit_tmp_r) {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %d >= %d", __func__, __FILE__, __LINE__, minunit_tmp_r, minunit_tmp_e);\ + minunit_status = 1;\ + return;\ + } else {\ + printf(".");\ + }\ +) + +#define mu_assert_int_between(expected_lower, expected_upper, result) MU__SAFE_BLOCK(\ + int minunit_tmp_e;\ + int minunit_tmp_m;\ + int minunit_tmp_r;\ + minunit_assert++;\ + minunit_tmp_e = (expected_lower);\ + minunit_tmp_m = (expected_upper);\ + minunit_tmp_r = (result);\ + if (result < minunit_tmp_e || result > minunit_tmp_m) {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %d was not between (inclusive) %d and %d", __func__, __FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r, minunit_tmp_m);\ + minunit_status = 1;\ + return;\ + } else {\ + printf(".");\ + }\ +) + +#define mu_assert_int_in(expected, array_length, result) MU__SAFE_BLOCK(\ + int minunit_tmp_r;\ + minunit_assert++;\ + minunit_tmp_r = (result);\ + int t = 0;\ + int i;\ + for (i = 0; i < array_length; i++) {\ + if (expected[i] == minunit_tmp_r)\ + t = 1;\ + }\ + if (t == 0) {\ + char tmp[500] = {0};\ + tmp[0] = '[';\ + for (i = 0; i < array_length; i++) {\ + sprintf(tmp + strlen(tmp), "%d, ", expected[i]);\ + }\ + int len = strlen(tmp);\ + tmp[len - 2] = ']';\ + tmp[len - 1] = '\0';\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: expected to be one of %s but was %d", __func__, __FILE__, __LINE__, tmp, minunit_tmp_r);\ + minunit_status = 1;\ + return;\ + } else {\ + printf(".");\ + }\ +) + +#define mu_assert_double_eq(expected, result) MU__SAFE_BLOCK(\ + double minunit_tmp_e;\ + double minunit_tmp_r;\ + minunit_assert++;\ + minunit_tmp_e = (expected);\ + minunit_tmp_r = (result);\ + if (fabs(minunit_tmp_e-minunit_tmp_r) > MINUNIT_EPSILON) {\ + int minunit_significant_figures = 1 - log10(MINUNIT_EPSILON);\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %.*g expected but was %.*g", __func__, __FILE__, __LINE__, minunit_significant_figures, minunit_tmp_e, minunit_significant_figures, minunit_tmp_r);\ + minunit_status = 1;\ + return;\ + } else {\ + printf(".");\ + }\ +) + +#define mu_assert_double_greater_than(val, result) MU__SAFE_BLOCK(\ + double minunit_tmp_e;\ + double minunit_tmp_r;\ + minunit_assert++;\ + minunit_tmp_e = (val);\ + minunit_tmp_r = (result);\ + if (val >= minunit_tmp_r) {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %f <= %f", __func__, __FILE__, __LINE__, minunit_tmp_r, minunit_tmp_e);\ + minunit_status = 1;\ + return;\ + } else {\ + printf(".");\ + }\ +) + +#define mu_assert_double_less_than(val, result) MU__SAFE_BLOCK(\ + double minunit_tmp_e;\ + double minunit_tmp_r;\ + minunit_assert++;\ + minunit_tmp_e = (val);\ + minunit_tmp_r = (result);\ + if (val <= minunit_tmp_r) {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %f >= %f", __func__, __FILE__, __LINE__, minunit_tmp_r, minunit_tmp_e);\ + minunit_status = 1;\ + return;\ + } else {\ + printf(".");\ + }\ +) + +#define mu_assert_double_between(expected_lower, expected_upper, result) MU__SAFE_BLOCK(\ + double minunit_tmp_e;\ + double minunit_tmp_m;\ + double minunit_tmp_r;\ + minunit_assert++;\ + minunit_tmp_e = (expected_lower);\ + minunit_tmp_m = (expected_upper);\ + minunit_tmp_r = (result);\ + if (result < minunit_tmp_e || result > minunit_tmp_m) {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %f was not between (inclusive) %f and %f", __func__, __FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r, minunit_tmp_m);\ + minunit_status = 1;\ + return;\ + } else {\ + printf(".");\ + }\ +) + +#define mu_assert_string_eq(expected, result) MU__SAFE_BLOCK(\ + const char* minunit_tmp_e = expected;\ + const char* minunit_tmp_r = result;\ + minunit_assert++;\ + if (!minunit_tmp_e) {\ + minunit_tmp_e = "";\ + }\ + if (!minunit_tmp_r) {\ + minunit_tmp_r = "";\ + }\ + if(strcmp(minunit_tmp_e, minunit_tmp_r)) {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: '%s' expected but was '%s'", __func__, __FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r);\ + minunit_status = 1;\ + return;\ + } else {\ + printf(".");\ + }\ +) + +#define mu_assert_null(result) MU__SAFE_BLOCK(\ + minunit_assert++;\ + if (result == NULL) {\ + printf(".");\ + } else {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: Expected result was not NULL", __func__, __FILE__, __LINE__);\ + minunit_status = 1;\ + return;\ + }\ +) + +#define mu_assert_not_null(result) MU__SAFE_BLOCK(\ + minunit_assert++;\ + if (result != NULL) {\ + printf(".");\ + } else {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: Expected result was not NULL", __func__, __FILE__, __LINE__);\ + minunit_status = 1;\ + return;\ + }\ +) + +#define mu_assert_pointers_eq(pointer1, pointer2) MU__SAFE_BLOCK(\ + minunit_assert++;\ + if (pointer1 == pointer2) {\ + printf(".");\ + } else {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: Expected the pointers to point to the same memory location", __func__, __FILE__, __LINE__);\ + minunit_status = 1;\ + return;\ + }\ +) + +#define mu_assert_pointers_not_eq(pointer1, pointer2) MU__SAFE_BLOCK(\ + minunit_assert++;\ + if (pointer1 != pointer2) {\ + printf(".");\ + } else {\ + snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: Expected the pointers to point to the same memory location", __func__, __FILE__, __LINE__);\ + minunit_status = 1;\ + return;\ + }\ +) + +/* + * The following two functions were written by David Robert Nadeau + * from http://NadeauSoftware.com/ and distributed under the + * Creative Commons Attribution 3.0 Unported License + */ + +/** + * Returns the real time, in seconds, or -1.0 if an error occurred. + * + * Time is measured since an arbitrary and OS-dependent start time. + * The returned real time is only useful for computing an elapsed time + * between two calls to this function. + */ +static double mu_timer_real(void) +{ +#if defined(_WIN32) + /* Windows 2000 and later. ---------------------------------- */ + LARGE_INTEGER Time; + LARGE_INTEGER Frequency; + + QueryPerformanceFrequency(&Frequency); + QueryPerformanceCounter(&Time); + + Time.QuadPart *= 1000000; + Time.QuadPart /= Frequency.QuadPart; + + return (double)Time.QuadPart / 1000000.0; + +#elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) + /* HP-UX, Solaris. ------------------------------------------ */ + return (double)gethrtime( ) / 1000000000.0; + +#elif defined(__MACH__) && defined(__APPLE__) + /* OSX. ----------------------------------------------------- */ + static double timeConvert = 0.0; + if ( timeConvert == 0.0 ) + { + mach_timebase_info_data_t timeBase; + (void)mach_timebase_info( &timeBase ); + timeConvert = (double)timeBase.numer / + (double)timeBase.denom / + 1000000000.0; + } + return (double)mach_absolute_time( ) * timeConvert; + +#elif defined(_POSIX_VERSION) + /* POSIX. --------------------------------------------------- */ + struct timeval tm; +#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) + { + struct timespec ts; +#if defined(CLOCK_MONOTONIC_PRECISE) + /* BSD. --------------------------------------------- */ + const clockid_t id = CLOCK_MONOTONIC_PRECISE; +#elif defined(CLOCK_MONOTONIC_RAW) + /* Linux. ------------------------------------------- */ + const clockid_t id = CLOCK_MONOTONIC_RAW; +#elif defined(CLOCK_HIGHRES) + /* Solaris. ----------------------------------------- */ + const clockid_t id = CLOCK_HIGHRES; +#elif defined(CLOCK_MONOTONIC) + /* AIX, BSD, Linux, POSIX, Solaris. ----------------- */ + const clockid_t id = CLOCK_MONOTONIC; +#elif defined(CLOCK_REALTIME) + /* AIX, BSD, HP-UX, Linux, POSIX. ------------------- */ + const clockid_t id = CLOCK_REALTIME; +#else + const clockid_t id = (clockid_t)-1; /* Unknown. */ +#endif /* CLOCK_* */ + if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 ) + return (double)ts.tv_sec + + (double)ts.tv_nsec / 1000000000.0; + /* Fall thru. */ + } +#endif /* _POSIX_TIMERS */ + + /* AIX, BSD, Cygwin, HP-UX, Linux, OSX, POSIX, Solaris. ----- */ + gettimeofday( &tm, NULL ); + return (double)tm.tv_sec + (double)tm.tv_usec / 1000000.0; +#else + return -1.0; /* Failed. */ +#endif +} + +/** + * Returns the amount of CPU time used by the current process, + * in seconds, or -1.0 if an error occurred. + */ +static double mu_timer_cpu(void) +{ +#if defined(_WIN32) + /* Windows -------------------------------------------------- */ + FILETIME createTime; + FILETIME exitTime; + FILETIME kernelTime; + FILETIME userTime; + + /* This approach has a resolution of 1/64 second. Unfortunately, Windows' API does not offer better */ + if ( GetProcessTimes( GetCurrentProcess( ), + &createTime, &exitTime, &kernelTime, &userTime ) != 0 ) + { + ULARGE_INTEGER userSystemTime; + memcpy(&userSystemTime, &userTime, sizeof(ULARGE_INTEGER)); + return (double)userSystemTime.QuadPart / 10000000.0; + } + +#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) + /* AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris --------- */ + +#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) + /* Prefer high-res POSIX timers, when available. */ + { + clockid_t id; + struct timespec ts; +#if _POSIX_CPUTIME > 0 + /* Clock ids vary by OS. Query the id, if possible. */ + if ( clock_getcpuclockid( 0, &id ) == -1 ) +#endif +#if defined(CLOCK_PROCESS_CPUTIME_ID) + /* Use known clock id for AIX, Linux, or Solaris. */ + id = CLOCK_PROCESS_CPUTIME_ID; +#elif defined(CLOCK_VIRTUAL) + /* Use known clock id for BSD or HP-UX. */ + id = CLOCK_VIRTUAL; +#else + id = (clockid_t)-1; +#endif + if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 ) + return (double)ts.tv_sec + + (double)ts.tv_nsec / 1000000000.0; + } +#endif + +#if defined(RUSAGE_SELF) + { + struct rusage rusage; + if ( getrusage( RUSAGE_SELF, &rusage ) != -1 ) + return (double)rusage.ru_utime.tv_sec + + (double)rusage.ru_utime.tv_usec / 1000000.0; + } +#endif + +#if defined(_SC_CLK_TCK) + { + const double ticks = (double)sysconf( _SC_CLK_TCK ); + struct tms tms; + if ( times( &tms ) != (clock_t)-1 ) + return (double)tms.tms_utime / ticks; + } +#endif + +#if defined(CLOCKS_PER_SEC) + { + clock_t cl = clock( ); + if ( cl != (clock_t)-1 ) + return (double)cl / (double)CLOCKS_PER_SEC; + } +#endif + +#endif + + return -1; /* Failed. */ +} + +#ifdef __cplusplus +} +#endif + +#endif /* MINUNIT_MINUNIT_H */ \ No newline at end of file diff --git a/applications/tests/minunit_test.c b/applications/tests/minunit_test.c new file mode 100644 index 00000000..62e0da37 --- /dev/null +++ b/applications/tests/minunit_test.c @@ -0,0 +1,77 @@ +#include +#include "flipper.h" +#include "log.h" +#include "minunit_vars.h" +#include "minunit.h" + +bool test_furi_ac_create_kill(); +bool test_furi_ac_switch_exit(); +bool test_furi_pipe_record(); +bool test_furi_holding_data(); +bool test_furi_concurrent_access(); +bool test_furi_nonexistent_data(); +bool test_furi_mute_algorithm(); + +static int foo = 0; + +void test_setup(void) { + foo = 7; +} + +void test_teardown(void) { + /* Nothing */ +} + +MU_TEST(test_check) { + mu_check(foo != 6); +} + +MU_TEST(mu_test_furi_ac_create_kill) { + mu_assert_int_eq(test_furi_ac_create_kill(), true); +} + +MU_TEST(mu_test_furi_ac_switch_exit) { + mu_assert_int_eq(test_furi_ac_switch_exit(), true); +} + +MU_TEST(mu_test_furi_pipe_record) { + mu_assert_int_eq(test_furi_pipe_record(), true); +} + +MU_TEST(mu_test_furi_holding_data) { + mu_assert_int_eq(test_furi_holding_data(), true); +} + +MU_TEST(mu_test_furi_concurrent_access) { + mu_assert_int_eq(test_furi_concurrent_access(), true); +} + +MU_TEST(mu_test_furi_nonexistent_data) { + mu_assert_int_eq(test_furi_nonexistent_data(), true); +} + +/* +MU_TEST(mu_test_furi_mute_algorithm) { + mu_assert_int_eq(test_furi_mute_algorithm(test_log), true); +} +*/ + +MU_TEST_SUITE(test_suite) { + MU_SUITE_CONFIGURE(&test_setup, &test_teardown); + + MU_RUN_TEST(test_check); + MU_RUN_TEST(mu_test_furi_ac_create_kill); + MU_RUN_TEST(mu_test_furi_ac_switch_exit); + MU_RUN_TEST(mu_test_furi_pipe_record); + MU_RUN_TEST(mu_test_furi_holding_data); + MU_RUN_TEST(mu_test_furi_concurrent_access); + MU_RUN_TEST(mu_test_furi_nonexistent_data); + // MU_RUN_TEST(mu_test_furi_mute_algorithm); +} + +int run_minunit() { + MU_RUN_SUITE(test_suite); + MU_REPORT(); + + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/tests/minunit_vars.h b/applications/tests/minunit_vars.h new file mode 100644 index 00000000..7ef5f825 --- /dev/null +++ b/applications/tests/minunit_vars.h @@ -0,0 +1,15 @@ +#pragma once +#include "minunit.h" + +/* Misc. counters */ +int minunit_run = 0; +int minunit_assert = 0; +int minunit_fail = 0; +int minunit_status = 0; + +/* Timers */ +double minunit_real_timer = 0; +double minunit_proc_timer = 0; + +/* Last message */ +char minunit_last_message[MINUNIT_MESSAGE_LEN]; \ No newline at end of file diff --git a/applications/tests/minunit_vars_ex.h b/applications/tests/minunit_vars_ex.h new file mode 100644 index 00000000..e2743d1c --- /dev/null +++ b/applications/tests/minunit_vars_ex.h @@ -0,0 +1,15 @@ +#pragma once +#include "minunit.h" + +/* Misc. counters */ +extern int minunit_run; +extern int minunit_assert; +extern int minunit_fail; +extern int minunit_status; + +/* Timers */ +extern double minunit_real_timer; +extern double minunit_proc_timer; + +/* Last message */ +extern char minunit_last_message[MINUNIT_MESSAGE_LEN]; \ No newline at end of file diff --git a/applications/tests/test_index.c b/applications/tests/test_index.c index 9c970a38..c15985ce 100644 --- a/applications/tests/test_index.c +++ b/applications/tests/test_index.c @@ -4,70 +4,38 @@ // #include "flipper-core.h" TODO: Rust build disabled -bool test_furi_ac_create_kill(FuriRecordSubscriber* log); -bool test_furi_ac_switch_exit(FuriRecordSubscriber* log); - -bool test_furi_pipe_record(FuriRecordSubscriber* log); -bool test_furi_holding_data(FuriRecordSubscriber* log); -bool test_furi_concurrent_access(FuriRecordSubscriber* log); -bool test_furi_nonexistent_data(FuriRecordSubscriber* log); -bool test_furi_mute_algorithm(FuriRecordSubscriber* log); +int run_minunit(); void flipper_test_app(void* p) { - FuriRecordSubscriber* log = get_default_log(); + // create pins + GpioPin red = {.pin = LED_RED_Pin, .port = LED_RED_GPIO_Port}; + GpioPin green = {.pin = LED_GREEN_Pin, .port = LED_GREEN_GPIO_Port}; + GpioPin blue = {.pin = LED_BLUE_Pin, .port = LED_BLUE_GPIO_Port}; - if(test_furi_ac_create_kill(log)) { - fuprintf(log, "[TEST] test_furi_ac_create_kill PASSED\n"); + // configure pins + pinMode(red, GpioModeOpenDrain); + pinMode(green, GpioModeOpenDrain); + pinMode(blue, GpioModeOpenDrain); + + digitalWrite(red, HIGH); + digitalWrite(green, HIGH); + digitalWrite(blue, LOW); + + uint32_t exitcode = run_minunit(); + + if(exitcode == 0) { + // test passed + digitalWrite(red, HIGH); + digitalWrite(green, LOW); + digitalWrite(blue, HIGH); } else { - fuprintf(log, "[TEST] test_furi_ac_create_kill FAILED\n"); + // test failed + digitalWrite(red, LOW); + digitalWrite(green, HIGH); + digitalWrite(blue, HIGH); } - if(test_furi_ac_switch_exit(log)) { - fuprintf(log, "[TEST] test_furi_ac_switch_exit PASSED\n"); - } else { - fuprintf(log, "[TEST] test_furi_ac_switch_exit FAILED\n"); - } - - if(test_furi_pipe_record(log)) { - fuprintf(log, "[TEST] test_furi_pipe_record PASSED\n"); - } else { - fuprintf(log, "[TEST] test_furi_pipe_record FAILED\n"); - } - - if(test_furi_holding_data(log)) { - fuprintf(log, "[TEST] test_furi_holding_data PASSED\n"); - } else { - fuprintf(log, "[TEST] test_furi_holding_data FAILED\n"); - } - - if(test_furi_concurrent_access(log)) { - fuprintf(log, "[TEST] test_furi_concurrent_access PASSED\n"); - } else { - fuprintf(log, "[TEST] test_furi_concurrent_access FAILED\n"); - } - - if(test_furi_nonexistent_data(log)) { - fuprintf(log, "[TEST] test_furi_nonexistent_data PASSED\n"); - } else { - fuprintf(log, "[TEST] test_furi_nonexistent_data FAILED\n"); - } - - if(test_furi_mute_algorithm(log)) { - fuprintf(log, "[TEST] test_furi_mute_algorithm PASSED\n"); - } else { - fuprintf(log, "[TEST] test_furi_mute_algorithm FAILED\n"); - } - - /* - TODO: Rust build disabled - if(add(1, 2) == 3) { - fuprintf(log, "[TEST] Rust add PASSED\n"); - } else { - fuprintf(log, "[TEST] Rust add FAILED\n"); - } - - rust_uart_write(); - */ + set_exitcode(exitcode); furiac_exit(NULL); } \ No newline at end of file diff --git a/core/app.cpp b/core/app.cpp index 5b2abe14..e9590ead 100644 --- a/core/app.cpp +++ b/core/app.cpp @@ -1,14 +1,20 @@ -#include "flipper.h" #include extern "C" { -#include "furi.h" -#include "log.h" -#include "startup.h" -#include "tty_uart.h" + #include "flipper.h" + #include "furi.h" + #include "log.h" + #include "startup.h" + #include "tty_uart.h" } -extern "C" void app() { +// for testing purpose +uint32_t exitcode = 0; +extern "C" void set_exitcode(uint32_t _exitcode) { + exitcode = _exitcode; +} + +extern "C" int app() { register_tty_uart(); FuriRecordSubscriber* log = get_default_log(); @@ -33,8 +39,9 @@ extern "C" void app() { } } delay(500); - // TODO add deferred event queue here } while(is_alive); fuprintf(log, "\n=== Bye from Flipper Zero! ===\n\n"); + + return (int)exitcode; } \ No newline at end of file diff --git a/core/flipper.h b/core/flipper.h index a4b0e3f5..da78456b 100644 --- a/core/flipper.h +++ b/core/flipper.h @@ -29,3 +29,5 @@ extern "C" { #define INPUT GpioModeInput #define LOW false #define HIGH true + +void set_exitcode(uint32_t _exitcode); diff --git a/firmware/targets/local/Inc/main.h b/firmware/targets/local/Inc/main.h index e169e63d..75870d21 100644 --- a/firmware/targets/local/Inc/main.h +++ b/firmware/targets/local/Inc/main.h @@ -10,5 +10,9 @@ HAL_UART_Transmit(UART_HandleTypeDef* handle, uint8_t* bufer, uint16_t size, uin typedef uint32_t TIM_HandleTypeDef; -#define LED_RED_GPIO_Port 1 -#define LED_RED_Pin 1 \ No newline at end of file +#define LED_RED_Pin 1 +#define LED_RED_GPIO_Port "Red:" +#define LED_GREEN_Pin 1 +#define LED_GREEN_GPIO_Port "Green:" +#define LED_BLUE_Pin 1 +#define LED_BLUE_GPIO_Port "Blue:" diff --git a/firmware/targets/local/Src/main.c b/firmware/targets/local/Src/main.c index 0a8a59be..64b2d05b 100644 --- a/firmware/targets/local/Src/main.c +++ b/firmware/targets/local/Src/main.c @@ -4,10 +4,8 @@ Flipper devices inc. Local fw build entry point. */ -void app(); +int app(); int main() { - app(); - - return 0; + return app(); } \ No newline at end of file diff --git a/wiki/Testing.md b/wiki/Testing.md index 46c58ad6..2c5e71f7 100644 --- a/wiki/Testing.md +++ b/wiki/Testing.md @@ -1,121 +1,11 @@ -# Bootloader testcase +# Integration testing -1. `# Clean flash` -2. `make -C bootloader flash` `# Load bootloader` -3. `# reboot device` - * Press right - * Press left - * Wait 0.1 s - * Release left - * Release right -4. Wait 0.5 s -5. `# Expect no FW` - * Expect: no uart welcome message - * Expect: red led on - * Expect: no USB -6. `# reboot device and go to DFU` - * Press left - * Press right - * Wait 0.1 s - * Release left - * Wait 0.5 s - * Release right -7. Wait 0.5 s -8. `# Expect DFU` - * Expect: blue led on - * Expect: USB: DFU -9. `target_f2/deploy-dfu.sh` `# load FW` -10. `# reboot device` - * Press right - * Press left - * Wait 0.1 s - * Release left - * Release right -11. Wait 0.5 s -12. `# Expect FW` - * Expect: uart welcome message - * Expect: USB Flipper CDC +* **[Bootloader testcase](Bootloader-test)** +* **[Input testcase](Input-test)** +* **[General testcase](General-building-test)** -# Input testcase +# Unit testing -1. `docker-compose exec dev make -C target_f2 example_input_dump` -2. Flash -3. For x in ``` -[ - (Up, "00"), - (Down, "01"), - (Right, "02"), - (Left, "03"), - (Ok, "04"), - (Back, "05"), -] -``` - * Press ${x[0]} - * wait 0.05 - * Expect: Uart: "event: ${x[1]} pressed" - * wait 0.05 - * Release ${x[0]} - * wait 0.05 - * Expect: Uart: "event: ${x[1]} released" - * wait 0.05 - -TODO: add debouncing check (multiple press and check there is no multiple events) - -# General building testcase - -Local target: - -* `docker-compose exec dev make -C firmware TARGET=local APP_TEST=1 clean` -* `docker-compose exec dev make -C firmware TARGET=local APP_TEST=1 run` -* check tests pass/fail - -* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_BLINK=1 run` -* GPIO on and off - -* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_UART_WRITE=1 run` -* GPIO on/off and `counter: %` writes - -* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_IPC=1 run` -* ASCII display draw - -* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_INPUT_DUMP=1 run` not implemented - -* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_QRCODE=1 run` -* Some writes to display - -* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_DISPLAY=1 run` -* Some writes to display - -* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_FATFS=1 flash` -* TODO: FatFs emulation and test not implemented - -F2 target: - -* `docker-compose exec dev make -C firmware TARGET=f2 APP_TEST=1 clean` -* `docker-compose exec dev make -C firmware TARGET=f2 APP_TEST=1 flash` -* check UART for test pass/fail - -* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_BLINK=1 flash` -* Red LED blink (1s period) - -* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_UART_WRITE=1 flash` -* Red LED shortly blinking, `counter: %` writes to UART - -* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_IPC=1 flash` -* ASCII display draw in UART - -* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_INPUT_DUMP=1 flash` -* Press all buttons, `state` and `event` writes to UART - -* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_QRCODE=1 flash` -* QR code show on the screen - -* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_DISPLAY=1 flash` -* `Hello world` show on the screen - -* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_FATFS=1 flash` -* `Init sd card error` on the screen -* Insert SD-card -* Reboot -* Show file list on the screen -* Scroll by pressing up and down +1. We use [minunit]() as testing framework +2. Tests root placed in `applications/tests/minuint_test.c` +3. There is `Run local tests` job in `CI` pipeline (`.github/workflows/ci.yml`) diff --git a/wiki/fw/Environment.md b/wiki/fw/Environment.md index 8744212a..a441e08f 100644 --- a/wiki/fw/Environment.md +++ b/wiki/fw/Environment.md @@ -35,7 +35,7 @@ For more HW- and RTOS- specific checks we run real FW in [Renode](https://interr Eventually we run real FW on remote debug/test bench (#26): flipper board + RPi + some stuff to control and check real hardware. -# Debug/test bench (not implemented) +# Debug/test bench (in progress) * 24×7 connected target Flipper device and accessible via Internet. Raspberry PI or some Linux single-board PC can be used as basic high-level control board. * Tool can push/click each user buttons by hardware by "control board" (low level). Usage of optocouples/reed-switch relays is fine for that. @@ -61,3 +61,7 @@ Eventually we run real FW on remote debug/test bench (#26): flipper board + RPi 2. Run CI tests: * For test automation we can use RobotDemo or simple expect tool/python scripts/etc. * Apply test cases and submit its results. + +# Testing + +You can read about testing in [Testing](Testing) page. diff --git a/wiki/testing/Bootloader-test.md b/wiki/testing/Bootloader-test.md new file mode 100644 index 00000000..855e5de7 --- /dev/null +++ b/wiki/testing/Bootloader-test.md @@ -0,0 +1,35 @@ +1. `# Clean flash` +2. `make -C bootloader flash` `# Load bootloader` +3. `# reboot device` + * Press right + * Press left + * Wait 0.1 s + * Release left + * Release right +4. Wait 0.5 s +5. `# Expect no FW` + * Expect: no uart welcome message + * Expect: red led on + * Expect: no USB +6. `# reboot device and go to DFU` + * Press left + * Press right + * Wait 0.1 s + * Release left + * Wait 0.5 s + * Release right +7. Wait 0.5 s +8. `# Expect DFU` + * Expect: blue led on + * Expect: USB: DFU +9. `target_f2/deploy-dfu.sh` `# load FW` +10. `# reboot device` + * Press right + * Press left + * Wait 0.1 s + * Release left + * Release right +11. Wait 0.5 s +12. `# Expect FW` + * Expect: uart welcome message + * Expect: USB Flipper CDC diff --git a/wiki/testing/General-building-test.md b/wiki/testing/General-building-test.md new file mode 100644 index 00000000..2dfd913b --- /dev/null +++ b/wiki/testing/General-building-test.md @@ -0,0 +1,59 @@ +Local target: + +* `docker-compose exec dev make -C firmware TARGET=local APP_TEST=1 clean` +* `docker-compose exec dev make -C firmware TARGET=local APP_TEST=1 run` +* check tests pass/fail (by exitcode == 0) + +* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_BLINK=1 run` +* GPIO on and off + +* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_UART_WRITE=1 run` +* GPIO on/off and `counter: %` writes + +* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_IPC=1 run` +* ASCII display draw + +* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_INPUT_DUMP=1 run` not implemented + +* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_QRCODE=1 run` +* Some writes to display + +* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_DISPLAY=1 run` +* Some writes to display + +* `docker-compose exec dev make -C firmware TARGET=local APP_EXAMPLE_FATFS=1 flash` +* TODO: FatFs emulation and test not implemented + +F2 target: + +* `docker-compose exec dev make -C firmware TARGET=f2 APP_TEST=1 clean` +* `docker-compose exec dev make -C firmware TARGET=f2 APP_TEST=1 flash` +* check UART for test pass/fail +* blue led when test is running +* green led if test is passed +* red led if test is failed + +* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_BLINK=1 flash` +* Red LED blink (1s period) + +* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_UART_WRITE=1 flash` +* Red LED shortly blinking, `counter: %` writes to UART + +* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_IPC=1 flash` +* ASCII display draw in UART + +* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_INPUT_DUMP=1 flash` +* Press all buttons, `state` and `event` writes to UART + +* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_QRCODE=1 flash` +* QR code show on the screen + +* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_DISPLAY=1 flash` +* `Hello world` show on the screen + +* `docker-compose exec dev make -C firmware TARGET=f2 APP_EXAMPLE_FATFS=1 flash` +* `Init sd card error` on the screen +* Insert SD-card +* Reboot +* Show file list on the screen +* Scroll by pressing up and down \ No newline at end of file diff --git a/wiki/testing/Input-test.md b/wiki/testing/Input-test.md new file mode 100644 index 00000000..8c4226e5 --- /dev/null +++ b/wiki/testing/Input-test.md @@ -0,0 +1,22 @@ +1. `docker-compose exec dev make -C target_f2 example_input_dump` +2. Flash +3. For x in ``` +[ + (Up, "00"), + (Down, "01"), + (Right, "02"), + (Left, "03"), + (Ok, "04"), + (Back, "05"), +] +``` + * Press ${x[0]} + * wait 0.05 + * Expect: Uart: "event: ${x[1]} pressed" + * wait 0.05 + * Release ${x[0]} + * wait 0.05 + * Expect: Uart: "event: ${x[1]} released" + * wait 0.05 + +TODO: add debouncing check (multiple press and check there is no multiple events)