[FL-872] Furi, API-HAL, App-Loader cleanup and improvements (#334)
* Furi: replace obsolete furiac_exit with osThreadExit, drop obsolete apis and test. Rename systemd to flipper and move to separate file, cleanup. ApiHal: Rename timebase to os and move freertos hooks there, move insomnia api to power module. * Furi: new thread helper * Furi: cleanup thread documentation * Flipper, AppLoader: update to use FuriThread. Update tasks signatures to match FuriThreadCallback signature. * F4: rename API_HAL_TIMEBASE_DEBUG to API_HAL_OS_DEBUG * Applications: rename FuriApplication to FlipperApplication, use FuriThreadCallback signature for apps. * C++ app template sample, new exit method
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include "minunit.h"
|
||||
|
||||
static void furi_concurent_app(void* p) {
|
||||
Event* event = p;
|
||||
|
||||
signal_event(event);
|
||||
|
||||
furiac_exit(NULL);
|
||||
}
|
||||
|
||||
void test_furi_event() {
|
||||
mu_assert(false, "please reimplement or delete test");
|
||||
|
||||
/*Event event;
|
||||
|
||||
mu_check(init_event(&event));
|
||||
|
||||
// The event should not be signalled right after creation
|
||||
mu_check(!wait_event_with_timeout(&event, 100));
|
||||
|
||||
// Create second app
|
||||
FuriApp* second_app __attribute__((unused)) =
|
||||
furiac_start(furi_concurent_app, "furi concurent app", (void*)&event);
|
||||
|
||||
// The event should be signalled now
|
||||
mu_check(wait_event_with_timeout(&event, 100));
|
||||
|
||||
// The event should not be signalled once it's processed
|
||||
mu_check(!wait_event_with_timeout(&event, 100));
|
||||
|
||||
mu_check(delete_event(&event));*/
|
||||
}
|
@@ -1,145 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include "minunit.h"
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint8_t red;
|
||||
uint8_t green;
|
||||
uint8_t blue;
|
||||
} Rgb;
|
||||
|
||||
static uint32_t rgb_final_state;
|
||||
|
||||
static void rgb_clear(void* ctx, void* state) {
|
||||
Rgb* rgb = state;
|
||||
rgb->red = 0;
|
||||
rgb->green = 0;
|
||||
rgb->blue = 0;
|
||||
}
|
||||
|
||||
static void rgb_commit(void* ctx, void* state) {
|
||||
Rgb* rgb = state;
|
||||
rgb_final_state = ((uint32_t)rgb->red) | (((uint32_t)rgb->green) << 8) |
|
||||
(((uint32_t)rgb->blue) << 16);
|
||||
}
|
||||
|
||||
static void set_red_composer(void* ctx, void* state) {
|
||||
Rgb* rgb = state;
|
||||
uint8_t* red = ctx;
|
||||
|
||||
rgb->red = *red;
|
||||
}
|
||||
|
||||
void test_furi_value_composer() {
|
||||
Rgb rgb = {0, 0, 0};
|
||||
ValueComposer composer;
|
||||
Rgb layer1_rgb = {0, 0, 0};
|
||||
ValueMutex layer1_mutex;
|
||||
uint8_t layer2_red = 0;
|
||||
|
||||
rgb_final_state = 0xdeadbeef;
|
||||
|
||||
mu_check(init_composer(&composer, &rgb));
|
||||
|
||||
mu_check(init_mutex(&layer1_mutex, &layer1_rgb, sizeof(layer1_rgb)));
|
||||
|
||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||
mu_assert_int_eq(0xdeadbeef, rgb_final_state);
|
||||
|
||||
ValueComposerHandle* layer1_handle =
|
||||
add_compose_layer(&composer, COPY_COMPOSE, &layer1_mutex, UiLayerNotify);
|
||||
mu_assert_pointers_not_eq(layer1_handle, NULL);
|
||||
|
||||
// RGB state should be updated with the layer1 state
|
||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||
mu_assert_int_eq(0x000000, rgb_final_state);
|
||||
|
||||
layer2_red = 0xcc;
|
||||
ValueComposerHandle* layer2_handle =
|
||||
add_compose_layer(&composer, set_red_composer, &layer2_red, UiLayerAboveNotify);
|
||||
mu_assert_pointers_not_eq(layer2_handle, NULL);
|
||||
|
||||
// RGB state should be updated with the layer1 and layer2 state, in order
|
||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||
mu_assert_int_eq(0x0000cc, rgb_final_state);
|
||||
|
||||
// Change layer1 state
|
||||
Rgb* state = acquire_mutex(&layer1_mutex, 0);
|
||||
mu_assert_pointers_not_eq(state, NULL);
|
||||
state->red = 0x12;
|
||||
state->green = 0x34;
|
||||
state->blue = 0x56;
|
||||
release_mutex(&layer1_mutex, state);
|
||||
|
||||
// Nothing should happen, we need to trigger composition request first
|
||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||
mu_assert_int_eq(0x0000cc, rgb_final_state);
|
||||
|
||||
request_compose(layer1_handle);
|
||||
|
||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||
mu_assert_int_eq(0x5634cc, rgb_final_state);
|
||||
|
||||
// Change layer2 state
|
||||
layer2_red = 0xff;
|
||||
|
||||
// Nothing should happen, we need to trigger composition request first
|
||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||
mu_assert_int_eq(0x5634cc, rgb_final_state);
|
||||
|
||||
request_compose(layer2_handle);
|
||||
|
||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||
mu_assert_int_eq(0x5634ff, rgb_final_state);
|
||||
|
||||
// Remove layer1
|
||||
mu_check(remove_compose_layer(layer1_handle));
|
||||
|
||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||
mu_assert_int_eq(0x0000ff, rgb_final_state);
|
||||
|
||||
// Remove layer2
|
||||
mu_check(remove_compose_layer(layer2_handle));
|
||||
|
||||
perform_compose(&composer, rgb_clear, rgb_commit, NULL);
|
||||
mu_assert_int_eq(0x000000, rgb_final_state);
|
||||
|
||||
mu_check(delete_composer(&composer));
|
||||
}
|
||||
|
||||
static const uint32_t notify_value_0 = 0x12345678;
|
||||
static const uint32_t notify_value_1 = 0x11223344;
|
||||
|
||||
static uint32_t pubsub_value = 0;
|
||||
|
||||
void test_value_manager_handler(const void* arg, void* ctx) {
|
||||
pubsub_value = *(uint32_t*)arg;
|
||||
}
|
||||
|
||||
void test_furi_value_manager() {
|
||||
uint32_t value = 0;
|
||||
ValueManager managed;
|
||||
|
||||
mu_check(init_managed(&managed, &value, sizeof(value)));
|
||||
|
||||
pubsub_value = 0;
|
||||
|
||||
PubSubItem* test_pubsub_item;
|
||||
test_pubsub_item = subscribe_pubsub(&managed.pubsub, test_value_manager_handler, 0);
|
||||
mu_assert_pointers_not_eq(test_pubsub_item, NULL);
|
||||
|
||||
mu_check(write_managed(&managed, (void*)¬ify_value_0, sizeof(notify_value_0), 100));
|
||||
|
||||
mu_assert_int_eq(pubsub_value, notify_value_0);
|
||||
|
||||
uint32_t* ptr = acquire_mutex(&managed.value, 100);
|
||||
mu_assert_pointers_not_eq(ptr, NULL);
|
||||
|
||||
*ptr = notify_value_1;
|
||||
|
||||
mu_check(commit_managed(&managed, ptr));
|
||||
|
||||
mu_assert_int_eq(pubsub_value, notify_value_1);
|
||||
|
||||
mu_check(delete_managed(&managed));
|
||||
}
|
@@ -60,7 +60,7 @@ void furi_concurent_app(void* p) {
|
||||
ValueMutex* mutex = (ValueMutex*)p;
|
||||
if(mutex == NULL) {
|
||||
printf("cannot open mutex\r\n");
|
||||
furiac_exit(NULL);
|
||||
osThreadExit();
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < 10; i++) {
|
||||
@@ -69,7 +69,7 @@ void furi_concurent_app(void* p) {
|
||||
if(value == NULL) {
|
||||
printf("cannot take record\r\n");
|
||||
release_mutex(mutex, value);
|
||||
furiac_exit(NULL);
|
||||
osThreadExit();
|
||||
}
|
||||
|
||||
// emulate read-modify-write broken by context switching
|
||||
@@ -83,7 +83,7 @@ void furi_concurent_app(void* p) {
|
||||
release_mutex(mutex, value);
|
||||
}
|
||||
|
||||
furiac_exit(NULL);
|
||||
osThreadExit();
|
||||
}
|
||||
|
||||
void test_furi_concurrent_access() {
|
||||
|
@@ -1,134 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <furi.h>
|
||||
|
||||
/*
|
||||
Test: creating and killing task
|
||||
|
||||
1. create task
|
||||
2. delay 10 ms
|
||||
3. kill task
|
||||
4. check that value changes
|
||||
5. delay 2 ms
|
||||
6. check that value stay unchanged
|
||||
*/
|
||||
|
||||
void create_kill_app(void* p) {
|
||||
// this app simply increase counter
|
||||
uint8_t* counter = (uint8_t*)p;
|
||||
while(1) {
|
||||
*counter = *counter + 1;
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
bool test_furi_ac_create_kill() {
|
||||
mu_assert(false, "please reimplement or delete test");
|
||||
/*
|
||||
uint8_t counter = 0;
|
||||
|
||||
uint8_t value_a = counter;
|
||||
|
||||
FuriApp* widget = furiac_start(create_kill_app, "create_kill_app", (void*)&counter);
|
||||
if(widget == NULL) {
|
||||
printf("create widget fail\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
delay(10);
|
||||
|
||||
if(!furiac_kill(widget)) {
|
||||
printf("kill widget fail\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(value_a == counter) {
|
||||
printf("counter unchanged\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
value_a = counter;
|
||||
|
||||
delay(10);
|
||||
|
||||
if(value_a != counter) {
|
||||
printf("counter changes after kill (counter = %d vs %d)\n", value_a, counter);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
Test: switch between tasks
|
||||
1. init s
|
||||
2. create task A, add 'A" to sequence'
|
||||
3. switch to task B, add 'B' to sequence
|
||||
4. exit from task B -> switch to A and add 'A' to sequence
|
||||
5. cleanup: exit from task A
|
||||
6. check sequence
|
||||
*/
|
||||
|
||||
#define TEST_SWITCH_CONTEXT_SEQ_SIZE 8
|
||||
|
||||
typedef struct {
|
||||
char sequence[TEST_SWITCH_CONTEXT_SEQ_SIZE];
|
||||
size_t count;
|
||||
} TestSwitchSequence;
|
||||
|
||||
void task_a(void*);
|
||||
void task_b(void*);
|
||||
|
||||
void task_a(void* p) {
|
||||
// simply starts, add 'A' letter to sequence and switch
|
||||
// if sequence counter = 0, call task B, exit otherwise
|
||||
|
||||
TestSwitchSequence* seq = (TestSwitchSequence*)p;
|
||||
|
||||
seq->sequence[seq->count] = 'A';
|
||||
seq->count++;
|
||||
|
||||
if(seq->count == 1) {
|
||||
furiac_switch(task_b, "task B", p);
|
||||
|
||||
// if switch unsuccessfull, this code will executed
|
||||
seq->sequence[seq->count] = 'x';
|
||||
seq->count++;
|
||||
} else {
|
||||
// add '/' symbol on exit
|
||||
seq->sequence[seq->count] = '/';
|
||||
seq->count++;
|
||||
furiac_exit(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// application simply add 'B' end exit
|
||||
void task_b(void* p) {
|
||||
TestSwitchSequence* seq = (TestSwitchSequence*)p;
|
||||
|
||||
seq->sequence[seq->count] = 'B';
|
||||
seq->count++;
|
||||
|
||||
furiac_exit(p);
|
||||
}
|
||||
|
||||
bool test_furi_ac_switch_exit() {
|
||||
// init sequence
|
||||
TestSwitchSequence seq;
|
||||
seq.count = 0;
|
||||
|
||||
furiac_start(task_a, "task A", (void*)&seq);
|
||||
// TODO how to check that all child task ends?
|
||||
|
||||
delay(10); // wait while task do its work
|
||||
|
||||
seq.sequence[seq.count] = '\0';
|
||||
|
||||
if(strcmp(seq.sequence, "ABA/") != 0) {
|
||||
printf("wrong sequence: %s\n", seq.sequence);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@@ -3,17 +3,11 @@
|
||||
#include "minunit_vars.h"
|
||||
#include "minunit.h"
|
||||
|
||||
bool test_furi_ac_create_kill();
|
||||
bool test_furi_ac_switch_exit();
|
||||
|
||||
// v2 tests
|
||||
void test_furi_create_open();
|
||||
void test_furi_valuemutex();
|
||||
void test_furi_concurrent_access();
|
||||
void test_furi_pubsub();
|
||||
void test_furi_value_composer();
|
||||
void test_furi_value_manager();
|
||||
void test_furi_event();
|
||||
|
||||
void test_furi_memmgr();
|
||||
|
||||
@@ -31,14 +25,6 @@ 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);
|
||||
}
|
||||
|
||||
// v2 tests
|
||||
MU_TEST(mu_test_furi_create_open) {
|
||||
test_furi_create_open();
|
||||
@@ -62,30 +48,16 @@ MU_TEST(mu_test_furi_memmgr) {
|
||||
test_furi_memmgr();
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_value_expanders) {
|
||||
test_furi_value_composer();
|
||||
test_furi_value_manager();
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_event) {
|
||||
test_furi_event();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// v2 tests
|
||||
MU_RUN_TEST(mu_test_furi_create_open);
|
||||
MU_RUN_TEST(mu_test_furi_valuemutex);
|
||||
MU_RUN_TEST(mu_test_furi_concurrent_access);
|
||||
MU_RUN_TEST(mu_test_furi_pubsub);
|
||||
MU_RUN_TEST(mu_test_furi_value_expanders);
|
||||
MU_RUN_TEST(mu_test_furi_event);
|
||||
|
||||
MU_RUN_TEST(mu_test_furi_memmgr);
|
||||
}
|
||||
|
||||
|
@@ -5,7 +5,7 @@
|
||||
|
||||
int run_minunit();
|
||||
|
||||
void flipper_test_app(void* p) {
|
||||
int32_t flipper_test_app(void* p) {
|
||||
// create pins
|
||||
GpioPin red = {.pin = LED_RED_Pin, .port = LED_RED_GPIO_Port};
|
||||
GpioPin green = {.pin = LED_GREEN_Pin, .port = LED_GREEN_GPIO_Port};
|
||||
@@ -38,7 +38,5 @@ void flipper_test_app(void* p) {
|
||||
gpio_write(blue_record, true);
|
||||
}
|
||||
|
||||
set_exitcode(exitcode);
|
||||
|
||||
furiac_exit(NULL);
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user