mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Merge remote-tracking branch 'OFW/dev' into dev
This commit is contained in:
10
applications/debug/text_box_element_test/application.fam
Normal file
10
applications/debug/text_box_element_test/application.fam
Normal file
@@ -0,0 +1,10 @@
|
||||
App(
|
||||
appid="text_box_element_test",
|
||||
name="Text Box Element Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="text_box_element_test_app",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=140,
|
||||
fap_category="Debug",
|
||||
)
|
||||
@@ -71,7 +71,7 @@ static void text_box_test_input_callback(InputEvent* input_event, void* ctx) {
|
||||
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
int32_t text_box_test_app(void* p) {
|
||||
int32_t text_box_element_test_app(void* p) {
|
||||
UNUSED(p);
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent));
|
||||
furi_check(event_queue);
|
||||
@@ -1,8 +1,8 @@
|
||||
App(
|
||||
appid="text_box_test",
|
||||
name="Text Box Test",
|
||||
appid="text_box_view_test",
|
||||
name="Text Box View Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="text_box_test_app",
|
||||
entry_point="text_box_view_test_app",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=140,
|
||||
133
applications/debug/text_box_view_test/text_box_view_test.c
Normal file
133
applications/debug/text_box_view_test/text_box_view_test.c
Normal file
@@ -0,0 +1,133 @@
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/text_box.h>
|
||||
#include <gui/view_stack.h>
|
||||
|
||||
#define TAG "TextBoxViewTest"
|
||||
|
||||
typedef struct {
|
||||
TextBoxFont font;
|
||||
TextBoxFocus focus;
|
||||
const char* text;
|
||||
} TextBoxViewTestContent;
|
||||
|
||||
static const TextBoxViewTestContent text_box_view_test_content_arr[] = {
|
||||
{
|
||||
.font = TextBoxFontText,
|
||||
.focus = TextBoxFocusStart,
|
||||
.text = "Hello, let's test text box. Press Right and Left to switch content",
|
||||
},
|
||||
{
|
||||
.font = TextBoxFontText,
|
||||
.focus = TextBoxFocusStart,
|
||||
.text =
|
||||
"Verify that symbols don't overlap borders: llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllend",
|
||||
},
|
||||
{
|
||||
.font = TextBoxFontText,
|
||||
.focus = TextBoxFocusStart,
|
||||
.text =
|
||||
"\n\n\n Start from several newline chars. Verify that scrolling doesn't break.\n\n\n\n\nThe end",
|
||||
},
|
||||
{
|
||||
.font = TextBoxFontText,
|
||||
.focus = TextBoxFocusStart,
|
||||
.text =
|
||||
"Let's test big text.\n\n The ARM Cortex-M is a group of 32-bit RISC ARM processor cores licensed by ARM Limited. These cores are optimized for low-cost and energy-efficient integrated circuits, which have been embedded in tens of billions of consumer devices.[1] Though they are most often the main component of microcontroller chips, sometimes they are embedded inside other types of chips too. The Cortex-M family consists of Cortex-M0,[2] Cortex-M0+,[3] Cortex-M1,[4] Cortex-M3,[5] Cortex-M4,[6] Cortex-M7,[7] Cortex-M23,[8] Cortex-M33,[9] Cortex-M35P,[10] Cortex-M52,[11] Cortex-M55,[12] Cortex-M85.[13] A floating-point unit (FPU) option is available for Cortex-M4 / M7 / M33 / M35P / M52 / M55 / M85 cores, and when included in the silicon these cores are sometimes known as \"Cortex-MxF\", where 'x' is the core variant.\n\nThe ARM Cortex-M family are ARM microprocessor cores that are designed for use in microcontrollers, ASICs, ASSPs, FPGAs, and SoCs. Cortex-M cores are commonly used as dedicated microcontroller chips, but also are hidden inside of SoC chips as power management controllers, I/O controllers, system controllers, touch screen controllers, smart battery controllers, and sensor controllers. The main difference from Cortex-A cores is that Cortex-M cores have no memory management unit (MMU) for virtual memory, considered essential for full-fledged operating systems. Cortex-M programs instead run bare metal or on one of the many real-time operating systems which support a Cortex-M.Though 8-bit microcontrollers were very popular in the past, Cortex-M has slowly been chipping away at the 8-bit market as the prices of low-end Cortex-M chips have moved downward. Cortex-M have become a popular replacements for 8-bit chips in applications that benefit from 32-bit math operations, and replacing older legacy ARM cores such as ARM7 and ARM9.",
|
||||
},
|
||||
{
|
||||
.font = TextBoxFontText,
|
||||
.focus = TextBoxFocusEnd,
|
||||
.text =
|
||||
"The same but with EndFocus\n\n The ARM Cortex-M is a group of 32-bit RISC ARM processor cores licensed by ARM Limited. These cores are optimized for low-cost and energy-efficient integrated circuits, which have been embedded in tens of billions of consumer devices.[1] Though they are most often the main component of microcontroller chips, sometimes they are embedded inside other types of chips too. The Cortex-M family consists of Cortex-M0,[2] Cortex-M0+,[3] Cortex-M1,[4] Cortex-M3,[5] Cortex-M4,[6] Cortex-M7,[7] Cortex-M23,[8] Cortex-M33,[9] Cortex-M35P,[10] Cortex-M52,[11] Cortex-M55,[12] Cortex-M85.[13] A floating-point unit (FPU) option is available for Cortex-M4 / M7 / M33 / M35P / M52 / M55 / M85 cores, and when included in the silicon these cores are sometimes known as \"Cortex-MxF\", where 'x' is the core variant.\n\nThe ARM Cortex-M family are ARM microprocessor cores that are designed for use in microcontrollers, ASICs, ASSPs, FPGAs, and SoCs. Cortex-M cores are commonly used as dedicated microcontroller chips, but also are hidden inside of SoC chips as power management controllers, I/O controllers, system controllers, touch screen controllers, smart battery controllers, and sensor controllers. The main difference from Cortex-A cores is that Cortex-M cores have no memory management unit (MMU) for virtual memory, considered essential for full-fledged operating systems. Cortex-M programs instead run bare metal or on one of the many real-time operating systems which support a Cortex-M.Though 8-bit microcontrollers were very popular in the past, Cortex-M has slowly been chipping away at the 8-bit market as the prices of low-end Cortex-M chips have moved downward. Cortex-M have become a popular replacements for 8-bit chips in applications that benefit from 32-bit math operations, and replacing older legacy ARM cores such as ARM7 and ARM9.",
|
||||
},
|
||||
{
|
||||
.font = TextBoxFontHex,
|
||||
.focus = TextBoxFocusEnd,
|
||||
.text =
|
||||
"0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999\n0000 0000 0000 0000\n1111 1111 1111 1111\n2222 2222 2222 2222\n3333 3333 3333 3333\n4444 4444 4444 4444\n5555 5555 5555 5555\n6666 6666 6666 6666\n7777 7777 7777 7777\n8888 8888 8888 8888\n9999 9999 9999 9999",
|
||||
},
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
TextBox* text_box;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
size_t current_content_i;
|
||||
} TextBoxViewTest;
|
||||
|
||||
static void text_box_update_view(TextBoxViewTest* instance) {
|
||||
text_box_reset(instance->text_box);
|
||||
|
||||
const TextBoxViewTestContent* content =
|
||||
&text_box_view_test_content_arr[instance->current_content_i];
|
||||
text_box_set_font(instance->text_box, content->font);
|
||||
text_box_set_focus(instance->text_box, content->focus);
|
||||
text_box_set_text(instance->text_box, content->text);
|
||||
}
|
||||
|
||||
static bool text_box_switch_view_input_callback(InputEvent* event, void* context) {
|
||||
bool consumed = false;
|
||||
TextBoxViewTest* instance = context;
|
||||
size_t contents_cnt = COUNT_OF(text_box_view_test_content_arr);
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyRight) {
|
||||
if(instance->current_content_i < contents_cnt - 1) {
|
||||
instance->current_content_i++;
|
||||
text_box_update_view(instance);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
if(instance->current_content_i > 0) {
|
||||
instance->current_content_i--;
|
||||
text_box_update_view(instance);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event->key == InputKeyBack) {
|
||||
view_dispatcher_stop(instance->view_dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
int32_t text_box_view_test_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
ViewDispatcher* view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
|
||||
view_dispatcher_enable_queue(view_dispatcher);
|
||||
|
||||
TextBoxViewTest instance = {
|
||||
.text_box = text_box_alloc(),
|
||||
.current_content_i = 0,
|
||||
.view_dispatcher = view_dispatcher,
|
||||
};
|
||||
|
||||
text_box_update_view(&instance);
|
||||
|
||||
View* text_box_switch_view = view_alloc();
|
||||
view_set_input_callback(text_box_switch_view, text_box_switch_view_input_callback);
|
||||
view_set_context(text_box_switch_view, &instance);
|
||||
|
||||
ViewStack* view_stack = view_stack_alloc();
|
||||
view_stack_add_view(view_stack, text_box_switch_view);
|
||||
view_stack_add_view(view_stack, text_box_get_view(instance.text_box));
|
||||
|
||||
view_dispatcher_add_view(view_dispatcher, 0, view_stack_get_view(view_stack));
|
||||
view_dispatcher_switch_to_view(view_dispatcher, 0);
|
||||
|
||||
view_dispatcher_run(view_dispatcher);
|
||||
|
||||
view_dispatcher_remove_view(view_dispatcher, 0);
|
||||
view_dispatcher_free(view_dispatcher);
|
||||
view_stack_free(view_stack);
|
||||
view_free(text_box_switch_view);
|
||||
text_box_free(instance.text_box);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2,8 +2,9 @@ App(
|
||||
appid="unit_tests",
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="unit_tests_on_system_start",
|
||||
sources=["unit_tests.c", "test_runner.c", "unit_test_api_table.cpp"],
|
||||
cdefines=["APP_UNIT_TESTS"],
|
||||
requires=["system_settings"],
|
||||
requires=["system_settings", "subghz_start"],
|
||||
provides=["delay_test"],
|
||||
resources="resources",
|
||||
order=100,
|
||||
@@ -12,9 +13,210 @@ App(
|
||||
App(
|
||||
appid="delay_test",
|
||||
name="Delay Test",
|
||||
sources=["tests/common/*.c", "tests/rpc/*.c"],
|
||||
apptype=FlipperAppType.SYSTEM,
|
||||
entry_point="delay_test_app",
|
||||
stack_size=1 * 1024,
|
||||
requires=["unit_tests"],
|
||||
order=110,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_varint",
|
||||
sources=["tests/common/*.c", "tests/varint/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_furi",
|
||||
sources=["tests/common/*.c", "tests/furi/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_furi_hal",
|
||||
sources=["tests/common/*.c", "tests/furi_hal/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_furi_hal_crypto",
|
||||
sources=["tests/common/*.c", "tests/furi_hal_crypto/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_furi_string",
|
||||
sources=["tests/common/*.c", "tests/furi_string/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_storage",
|
||||
sources=["tests/common/*.c", "tests/storage/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_stream",
|
||||
sources=["tests/common/*.c", "tests/stream/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_dirwalk",
|
||||
sources=["tests/common/*.c", "tests/dirwalk/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_manifest",
|
||||
sources=["tests/common/*.c", "tests/manifest/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_flipper_format",
|
||||
sources=["tests/common/*.c", "tests/flipper_format/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_flipper_format_string",
|
||||
sources=["tests/common/*.c", "tests/flipper_format_string/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_rpc",
|
||||
sources=["tests/common/*.c", "tests/rpc/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_subghz",
|
||||
sources=["tests/common/*.c", "tests/subghz/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_infrared",
|
||||
sources=["tests/common/*.c", "tests/infrared/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_nfc",
|
||||
sources=["tests/common/*.c", "tests/nfc/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_power",
|
||||
sources=["tests/common/*.c", "tests/power/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_protocol_dict",
|
||||
sources=["tests/common/*.c", "tests/protocol_dict/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_lfrfid",
|
||||
sources=["tests/common/*.c", "tests/lfrfid/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_bit_lib",
|
||||
sources=["tests/common/*.c", "tests/bit_lib/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_datetime",
|
||||
sources=["tests/common/*.c", "tests/datetime/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_float_tools",
|
||||
sources=["tests/common/*.c", "tests/float_tools/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_bt",
|
||||
sources=["tests/common/*.c", "tests/bt/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_dialogs_file_browser_options",
|
||||
sources=["tests/common/*.c", "tests/dialogs_file_browser_options/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_expansion",
|
||||
sources=["tests/common/*.c", "tests/expansion/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="test_compress",
|
||||
sources=["tests/common/*.c", "tests/compress/*.c"],
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="get_api",
|
||||
requires=["unit_tests"],
|
||||
)
|
||||
|
||||
@@ -1,476 +0,0 @@
|
||||
#ifdef FW_CFG_unit_tests
|
||||
|
||||
#include <lib/nfc/nfc.h>
|
||||
#include <lib/nfc/helpers/iso14443_crc.h>
|
||||
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>
|
||||
|
||||
#include <furi/furi.h>
|
||||
|
||||
#define NFC_MAX_BUFFER_SIZE (256)
|
||||
|
||||
typedef enum {
|
||||
NfcTransportLogLevelWarning,
|
||||
NfcTransportLogLevelInfo,
|
||||
} NfcTransportLogLevel;
|
||||
|
||||
FuriMessageQueue* poller_queue = NULL;
|
||||
FuriMessageQueue* listener_queue = NULL;
|
||||
|
||||
typedef enum {
|
||||
NfcMessageTypeTx,
|
||||
NfcMessageTypeTimeout,
|
||||
NfcMessageTypeAbort,
|
||||
} NfcMessageType;
|
||||
|
||||
typedef struct {
|
||||
uint16_t data_bits;
|
||||
uint8_t data[NFC_MAX_BUFFER_SIZE];
|
||||
} NfcMessageData;
|
||||
|
||||
typedef struct {
|
||||
NfcMessageType type;
|
||||
NfcMessageData data;
|
||||
} NfcMessage;
|
||||
|
||||
typedef enum {
|
||||
NfcStateIdle,
|
||||
NfcStateReady,
|
||||
NfcStateReset,
|
||||
} NfcState;
|
||||
|
||||
typedef enum {
|
||||
Iso14443_3aColResStatusIdle,
|
||||
Iso14443_3aColResStatusInProgress,
|
||||
Iso14443_3aColResStatusDone,
|
||||
} Iso14443_3aColResStatus;
|
||||
|
||||
typedef struct {
|
||||
Iso14443_3aSensResp sens_resp;
|
||||
Iso14443_3aSddResp sdd_resp[2];
|
||||
Iso14443_3aSelResp sel_resp[2];
|
||||
} Iso14443_3aColResData;
|
||||
|
||||
struct Nfc {
|
||||
NfcState state;
|
||||
|
||||
Iso14443_3aColResStatus col_res_status;
|
||||
Iso14443_3aColResData col_res_data;
|
||||
bool software_col_res_required;
|
||||
|
||||
NfcEventCallback callback;
|
||||
void* context;
|
||||
|
||||
NfcMode mode;
|
||||
|
||||
FuriThread* worker_thread;
|
||||
};
|
||||
|
||||
static void nfc_test_print(
|
||||
NfcTransportLogLevel log_level,
|
||||
const char* message,
|
||||
uint8_t* buffer,
|
||||
uint16_t bits) {
|
||||
FuriString* str = furi_string_alloc();
|
||||
size_t bytes = (bits + 7) / 8;
|
||||
|
||||
for(size_t i = 0; i < bytes; i++) {
|
||||
furi_string_cat_printf(str, " %02X", buffer[i]);
|
||||
}
|
||||
if(log_level == NfcTransportLogLevelWarning) {
|
||||
FURI_LOG_W(message, "%s", furi_string_get_cstr(str));
|
||||
} else {
|
||||
FURI_LOG_I(message, "%s", furi_string_get_cstr(str));
|
||||
}
|
||||
|
||||
furi_string_free(str);
|
||||
}
|
||||
|
||||
static void nfc_prepare_col_res_data(
|
||||
Nfc* instance,
|
||||
uint8_t* uid,
|
||||
uint8_t uid_len,
|
||||
uint8_t* atqa,
|
||||
uint8_t sak) {
|
||||
memcpy(instance->col_res_data.sens_resp.sens_resp, atqa, 2);
|
||||
|
||||
if(uid_len == 7) {
|
||||
instance->col_res_data.sdd_resp[0].nfcid[0] = 0x88;
|
||||
memcpy(&instance->col_res_data.sdd_resp[0].nfcid[1], uid, 3);
|
||||
uint8_t bss = 0;
|
||||
for(size_t i = 0; i < 4; i++) {
|
||||
bss ^= instance->col_res_data.sdd_resp[0].nfcid[i];
|
||||
}
|
||||
instance->col_res_data.sdd_resp[0].bss = bss;
|
||||
instance->col_res_data.sel_resp[0].sak = 0x04;
|
||||
|
||||
memcpy(instance->col_res_data.sdd_resp[1].nfcid, &uid[3], 4);
|
||||
bss = 0;
|
||||
for(size_t i = 0; i < 4; i++) {
|
||||
bss ^= instance->col_res_data.sdd_resp[1].nfcid[i];
|
||||
}
|
||||
instance->col_res_data.sdd_resp[1].bss = bss;
|
||||
instance->col_res_data.sel_resp[1].sak = sak;
|
||||
|
||||
} else {
|
||||
furi_crash("Not supporting not 7 bytes");
|
||||
}
|
||||
}
|
||||
|
||||
Nfc* nfc_alloc(void) {
|
||||
Nfc* instance = malloc(sizeof(Nfc));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_free(Nfc* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech) {
|
||||
UNUSED(instance);
|
||||
UNUSED(tech);
|
||||
|
||||
instance->mode = mode;
|
||||
}
|
||||
|
||||
void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc) {
|
||||
UNUSED(instance);
|
||||
UNUSED(fdt_poll_fc);
|
||||
}
|
||||
|
||||
void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc) {
|
||||
UNUSED(instance);
|
||||
UNUSED(fdt_listen_fc);
|
||||
}
|
||||
|
||||
void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc) {
|
||||
UNUSED(instance);
|
||||
UNUSED(mask_rx_time_fc);
|
||||
}
|
||||
|
||||
void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us) {
|
||||
UNUSED(instance);
|
||||
UNUSED(fdt_poll_poll_us);
|
||||
}
|
||||
|
||||
void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us) {
|
||||
UNUSED(instance);
|
||||
UNUSED(guard_time_us);
|
||||
}
|
||||
|
||||
NfcError nfc_iso14443a_listener_set_col_res_data(
|
||||
Nfc* instance,
|
||||
uint8_t* uid,
|
||||
uint8_t uid_len,
|
||||
uint8_t* atqa,
|
||||
uint8_t sak) {
|
||||
furi_check(instance);
|
||||
furi_check(uid);
|
||||
furi_check(atqa);
|
||||
|
||||
nfc_prepare_col_res_data(instance, uid, uid_len, atqa, sak);
|
||||
instance->software_col_res_required = true;
|
||||
|
||||
return NfcErrorNone;
|
||||
}
|
||||
|
||||
static int32_t nfc_worker_poller(void* context) {
|
||||
Nfc* instance = context;
|
||||
furi_check(instance->callback);
|
||||
|
||||
instance->state = NfcStateReady;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
NfcEvent event = {};
|
||||
|
||||
while(true) {
|
||||
event.type = NfcEventTypePollerReady;
|
||||
command = instance->callback(event, instance->context);
|
||||
if(command == NfcCommandStop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
instance->state = NfcStateIdle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nfc_worker_listener_pass_col_res(Nfc* instance, uint8_t* rx_data, uint16_t rx_bits) {
|
||||
furi_check(instance->col_res_status != Iso14443_3aColResStatusDone);
|
||||
BitBuffer* tx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
|
||||
|
||||
bool processed = false;
|
||||
|
||||
if((rx_bits == 7) && (rx_data[0] == 0x52)) {
|
||||
instance->col_res_status = Iso14443_3aColResStatusInProgress;
|
||||
bit_buffer_copy_bytes(
|
||||
tx_buffer,
|
||||
instance->col_res_data.sens_resp.sens_resp,
|
||||
sizeof(instance->col_res_data.sens_resp.sens_resp));
|
||||
nfc_listener_tx(instance, tx_buffer);
|
||||
processed = true;
|
||||
} else if(rx_bits == 2 * 8) {
|
||||
if((rx_data[0] == 0x93) && (rx_data[1] == 0x20)) {
|
||||
bit_buffer_copy_bytes(
|
||||
tx_buffer,
|
||||
(const uint8_t*)&instance->col_res_data.sdd_resp[0],
|
||||
sizeof(Iso14443_3aSddResp));
|
||||
nfc_listener_tx(instance, tx_buffer);
|
||||
processed = true;
|
||||
} else if((rx_data[0] == 0x95) && (rx_data[1] == 0x20)) {
|
||||
bit_buffer_copy_bytes(
|
||||
tx_buffer,
|
||||
(const uint8_t*)&instance->col_res_data.sdd_resp[1],
|
||||
sizeof(Iso14443_3aSddResp));
|
||||
nfc_listener_tx(instance, tx_buffer);
|
||||
processed = true;
|
||||
}
|
||||
} else if(rx_bits == 9 * 8) {
|
||||
if((rx_data[0] == 0x93) && (rx_data[1] == 0x70)) {
|
||||
bit_buffer_set_size_bytes(tx_buffer, 1);
|
||||
bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[0].sak);
|
||||
iso14443_crc_append(Iso14443CrcTypeA, tx_buffer);
|
||||
nfc_listener_tx(instance, tx_buffer);
|
||||
processed = true;
|
||||
} else if((rx_data[0] == 0x95) && (rx_data[1] == 0x70)) {
|
||||
bit_buffer_set_size_bytes(tx_buffer, 1);
|
||||
bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[1].sak);
|
||||
iso14443_crc_append(Iso14443CrcTypeA, tx_buffer);
|
||||
nfc_listener_tx(instance, tx_buffer);
|
||||
instance->col_res_status = Iso14443_3aColResStatusDone;
|
||||
NfcEvent event = {.type = NfcEventTypeListenerActivated};
|
||||
instance->callback(event, instance->context);
|
||||
|
||||
processed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!processed) {
|
||||
NfcMessage message = {.type = NfcMessageTypeTimeout};
|
||||
furi_message_queue_put(poller_queue, &message, FuriWaitForever);
|
||||
}
|
||||
|
||||
bit_buffer_free(tx_buffer);
|
||||
}
|
||||
|
||||
static int32_t nfc_worker_listener(void* context) {
|
||||
Nfc* instance = context;
|
||||
furi_check(instance->callback);
|
||||
|
||||
NfcMessage message = {};
|
||||
|
||||
NfcEventData event_data = {};
|
||||
event_data.buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
|
||||
NfcEvent nfc_event = {.data = event_data};
|
||||
|
||||
while(true) {
|
||||
furi_message_queue_get(listener_queue, &message, FuriWaitForever);
|
||||
bit_buffer_copy_bits(event_data.buffer, message.data.data, message.data.data_bits);
|
||||
if((message.data.data[0] == 0x52) && (message.data.data_bits == 7)) {
|
||||
instance->col_res_status = Iso14443_3aColResStatusIdle;
|
||||
}
|
||||
|
||||
if(message.type == NfcMessageTypeAbort) {
|
||||
break;
|
||||
} else if(message.type == NfcMessageTypeTx) {
|
||||
nfc_test_print(
|
||||
NfcTransportLogLevelInfo, "RDR", message.data.data, message.data.data_bits);
|
||||
if(instance->software_col_res_required &&
|
||||
(instance->col_res_status != Iso14443_3aColResStatusDone)) {
|
||||
nfc_worker_listener_pass_col_res(
|
||||
instance, message.data.data, message.data.data_bits);
|
||||
} else {
|
||||
instance->state = NfcStateReady;
|
||||
nfc_event.type = NfcEventTypeRxEnd;
|
||||
instance->callback(nfc_event, instance->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instance->state = NfcStateIdle;
|
||||
instance->col_res_status = Iso14443_3aColResStatusIdle;
|
||||
memset(&instance->col_res_data, 0, sizeof(instance->col_res_data));
|
||||
bit_buffer_free(nfc_event.data.buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nfc_start(Nfc* instance, NfcEventCallback callback, void* context) {
|
||||
furi_check(instance);
|
||||
furi_check(instance->worker_thread == NULL);
|
||||
|
||||
if(instance->mode == NfcModeListener) {
|
||||
furi_check(listener_queue == NULL);
|
||||
// Check that poller didn't start
|
||||
furi_check(poller_queue == NULL);
|
||||
} else {
|
||||
furi_check(poller_queue == NULL);
|
||||
// Check that poller is started after listener
|
||||
furi_check(listener_queue);
|
||||
}
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
|
||||
if(instance->mode == NfcModeListener) {
|
||||
listener_queue = furi_message_queue_alloc(4, sizeof(NfcMessage));
|
||||
} else {
|
||||
poller_queue = furi_message_queue_alloc(4, sizeof(NfcMessage));
|
||||
}
|
||||
|
||||
instance->worker_thread = furi_thread_alloc();
|
||||
furi_thread_set_context(instance->worker_thread, instance);
|
||||
furi_thread_set_priority(instance->worker_thread, FuriThreadPriorityHigh);
|
||||
furi_thread_set_stack_size(instance->worker_thread, 8 * 1024);
|
||||
|
||||
if(instance->mode == NfcModeListener) {
|
||||
furi_thread_set_name(instance->worker_thread, "NfcWorkerListener");
|
||||
furi_thread_set_callback(instance->worker_thread, nfc_worker_listener);
|
||||
} else {
|
||||
furi_thread_set_name(instance->worker_thread, "NfcWorkerPoller");
|
||||
furi_thread_set_callback(instance->worker_thread, nfc_worker_poller);
|
||||
}
|
||||
|
||||
furi_thread_start(instance->worker_thread);
|
||||
}
|
||||
|
||||
void nfc_stop(Nfc* instance) {
|
||||
furi_check(instance);
|
||||
furi_check(instance->worker_thread);
|
||||
|
||||
if(instance->mode == NfcModeListener) {
|
||||
NfcMessage message = {.type = NfcMessageTypeAbort};
|
||||
furi_message_queue_put(listener_queue, &message, FuriWaitForever);
|
||||
furi_thread_join(instance->worker_thread);
|
||||
|
||||
furi_message_queue_free(listener_queue);
|
||||
listener_queue = NULL;
|
||||
|
||||
furi_thread_free(instance->worker_thread);
|
||||
instance->worker_thread = NULL;
|
||||
} else {
|
||||
furi_thread_join(instance->worker_thread);
|
||||
|
||||
furi_message_queue_free(poller_queue);
|
||||
poller_queue = NULL;
|
||||
|
||||
furi_thread_free(instance->worker_thread);
|
||||
instance->worker_thread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Called from worker thread
|
||||
|
||||
NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer) {
|
||||
furi_check(instance);
|
||||
furi_check(poller_queue);
|
||||
furi_check(listener_queue);
|
||||
furi_check(tx_buffer);
|
||||
|
||||
NfcMessage message = {};
|
||||
message.type = NfcMessageTypeTx;
|
||||
message.data.data_bits = bit_buffer_get_size(tx_buffer);
|
||||
bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer));
|
||||
|
||||
furi_message_queue_put(poller_queue, &message, FuriWaitForever);
|
||||
|
||||
return NfcErrorNone;
|
||||
}
|
||||
|
||||
NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer) {
|
||||
return nfc_listener_tx(instance, tx_buffer);
|
||||
}
|
||||
|
||||
NfcError
|
||||
nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt) {
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
furi_check(poller_queue);
|
||||
furi_check(listener_queue);
|
||||
UNUSED(fwt);
|
||||
|
||||
NfcError error = NfcErrorNone;
|
||||
|
||||
NfcMessage message = {};
|
||||
message.type = NfcMessageTypeTx;
|
||||
message.data.data_bits = bit_buffer_get_size(tx_buffer);
|
||||
bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer));
|
||||
// Tx
|
||||
furi_check(furi_message_queue_put(listener_queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
// Rx
|
||||
FuriStatus status = furi_message_queue_get(poller_queue, &message, 50);
|
||||
|
||||
if(status == FuriStatusErrorTimeout) {
|
||||
error = NfcErrorTimeout;
|
||||
} else if(message.type == NfcMessageTypeTx) {
|
||||
bit_buffer_copy_bits(rx_buffer, message.data.data, message.data.data_bits);
|
||||
nfc_test_print(
|
||||
NfcTransportLogLevelWarning, "TAG", message.data.data, message.data.data_bits);
|
||||
} else if(message.type == NfcMessageTypeTimeout) {
|
||||
error = NfcErrorTimeout;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
NfcError nfc_iso14443a_poller_trx_custom_parity(
|
||||
Nfc* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);
|
||||
}
|
||||
|
||||
// Technology specific API
|
||||
|
||||
NfcError nfc_iso14443a_poller_trx_short_frame(
|
||||
Nfc* instance,
|
||||
NfcIso14443aShortFrame frame,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
UNUSED(frame);
|
||||
|
||||
BitBuffer* tx_buffer = bit_buffer_alloc(32);
|
||||
bit_buffer_set_size(tx_buffer, 7);
|
||||
bit_buffer_set_byte(tx_buffer, 0, 0x52);
|
||||
|
||||
NfcError error = nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);
|
||||
|
||||
bit_buffer_free(tx_buffer);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
NfcError nfc_iso14443a_poller_trx_sdd_frame(
|
||||
Nfc* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);
|
||||
}
|
||||
|
||||
NfcError nfc_iso15693_listener_tx_sof(Nfc* instance) {
|
||||
UNUSED(instance);
|
||||
|
||||
return NfcErrorNone;
|
||||
}
|
||||
|
||||
NfcError nfc_felica_listener_set_sensf_res_data(
|
||||
Nfc* instance,
|
||||
const uint8_t* idm,
|
||||
const uint8_t idm_len,
|
||||
const uint8_t* pmm,
|
||||
const uint8_t pmm_len) {
|
||||
furi_assert(instance);
|
||||
furi_assert(idm);
|
||||
furi_assert(pmm);
|
||||
furi_assert(idm_len == 8);
|
||||
furi_assert(pmm_len == 8);
|
||||
|
||||
return NfcErrorNone;
|
||||
}
|
||||
|
||||
#endif
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,168 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "minunit_vars.h"
|
||||
#include <notification/notification_messages.h>
|
||||
#include <cli/cli.h>
|
||||
#include <loader/loader.h>
|
||||
|
||||
#define TAG "UnitTests"
|
||||
|
||||
int run_minunit_test_furi(void);
|
||||
int run_minunit_test_furi_hal(void);
|
||||
int run_minunit_test_furi_hal_crypto(void);
|
||||
int run_minunit_test_furi_string(void);
|
||||
int run_minunit_test_infrared(void);
|
||||
int run_minunit_test_rpc(void);
|
||||
int run_minunit_test_manifest(void);
|
||||
int run_minunit_test_flipper_format(void);
|
||||
int run_minunit_test_flipper_format_string(void);
|
||||
int run_minunit_test_stream(void);
|
||||
int run_minunit_test_storage(void);
|
||||
int run_minunit_test_subghz(void);
|
||||
int run_minunit_test_dirwalk(void);
|
||||
int run_minunit_test_power(void);
|
||||
int run_minunit_test_protocol_dict(void);
|
||||
int run_minunit_test_lfrfid_protocols(void);
|
||||
int run_minunit_test_nfc(void);
|
||||
int run_minunit_test_bit_lib(void);
|
||||
int run_minunit_test_datetime(void);
|
||||
int run_minunit_test_float_tools(void);
|
||||
int run_minunit_test_bt(void);
|
||||
int run_minunit_test_dialogs_file_browser_options(void);
|
||||
int run_minunit_test_expansion(void);
|
||||
|
||||
typedef int (*UnitTestEntry)(void);
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
const UnitTestEntry entry;
|
||||
} UnitTest;
|
||||
|
||||
const UnitTest unit_tests[] = {
|
||||
{.name = "furi", .entry = run_minunit_test_furi},
|
||||
{.name = "furi_hal", .entry = run_minunit_test_furi_hal},
|
||||
{.name = "furi_hal_crypto", .entry = run_minunit_test_furi_hal_crypto},
|
||||
{.name = "furi_string", .entry = run_minunit_test_furi_string},
|
||||
{.name = "storage", .entry = run_minunit_test_storage},
|
||||
{.name = "stream", .entry = run_minunit_test_stream},
|
||||
{.name = "dirwalk", .entry = run_minunit_test_dirwalk},
|
||||
{.name = "manifest", .entry = run_minunit_test_manifest},
|
||||
{.name = "flipper_format", .entry = run_minunit_test_flipper_format},
|
||||
{.name = "flipper_format_string", .entry = run_minunit_test_flipper_format_string},
|
||||
{.name = "rpc", .entry = run_minunit_test_rpc},
|
||||
{.name = "subghz", .entry = run_minunit_test_subghz},
|
||||
{.name = "infrared", .entry = run_minunit_test_infrared},
|
||||
{.name = "nfc", .entry = run_minunit_test_nfc},
|
||||
{.name = "power", .entry = run_minunit_test_power},
|
||||
{.name = "protocol_dict", .entry = run_minunit_test_protocol_dict},
|
||||
{.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols},
|
||||
{.name = "bit_lib", .entry = run_minunit_test_bit_lib},
|
||||
{.name = "datetime", .entry = run_minunit_test_datetime},
|
||||
{.name = "float_tools", .entry = run_minunit_test_float_tools},
|
||||
{.name = "bt", .entry = run_minunit_test_bt},
|
||||
{.name = "dialogs_file_browser_options",
|
||||
.entry = run_minunit_test_dialogs_file_browser_options},
|
||||
{.name = "expansion", .entry = run_minunit_test_expansion},
|
||||
};
|
||||
|
||||
void minunit_print_progress(void) {
|
||||
static const char progress[] = {'\\', '|', '/', '-'};
|
||||
static uint8_t progress_counter = 0;
|
||||
static uint32_t last_tick = 0;
|
||||
uint32_t current_tick = furi_get_tick();
|
||||
if(current_tick - last_tick > 20) {
|
||||
last_tick = current_tick;
|
||||
printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void minunit_print_fail(const char* str) {
|
||||
printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str);
|
||||
}
|
||||
|
||||
void minunit_printf_warning(const char* format, ...) {
|
||||
FuriString* str = furi_string_alloc();
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
furi_string_vprintf(str, format, args);
|
||||
va_end(args);
|
||||
printf(_FURI_LOG_CLR_W "%s\r\n" _FURI_LOG_CLR_RESET, furi_string_get_cstr(str));
|
||||
furi_string_free(str);
|
||||
}
|
||||
|
||||
void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
minunit_run = 0;
|
||||
minunit_assert = 0;
|
||||
minunit_fail = 0;
|
||||
minunit_status = 0;
|
||||
|
||||
Loader* loader = furi_record_open(RECORD_LOADER);
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
// TODO FL-3491: lock device while test running
|
||||
if(loader_is_locked(loader)) {
|
||||
printf("RPC: stop all applications to run tests\r\n");
|
||||
notification_message(notification, &sequence_blink_magenta_100);
|
||||
} else {
|
||||
notification_message_block(notification, &sequence_set_only_blue_255);
|
||||
|
||||
uint32_t heap_before = memmgr_get_free_heap();
|
||||
uint32_t cycle_counter = furi_get_tick();
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(unit_tests); i++) {
|
||||
if(cli_cmd_interrupt_received(cli)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_string_size(args)) {
|
||||
if(furi_string_cmp_str(args, unit_tests[i].name) == 0) {
|
||||
unit_tests[i].entry();
|
||||
} else {
|
||||
printf("Skipping %s\r\n", unit_tests[i].name);
|
||||
}
|
||||
} else {
|
||||
unit_tests[i].entry();
|
||||
}
|
||||
}
|
||||
|
||||
if(minunit_run != 0) {
|
||||
printf("\r\nFailed tests: %u\r\n", minunit_fail);
|
||||
|
||||
// Time report
|
||||
cycle_counter = (furi_get_tick() - cycle_counter);
|
||||
printf("Consumed: %lu ms\r\n", cycle_counter);
|
||||
|
||||
// Wait for tested services and apps to deallocate memory
|
||||
furi_delay_ms(200);
|
||||
uint32_t heap_after = memmgr_get_free_heap();
|
||||
printf("Leaked: %ld\r\n", heap_before - heap_after);
|
||||
|
||||
// Final Report
|
||||
if(minunit_fail == 0) {
|
||||
notification_message(notification, &sequence_success);
|
||||
printf("Status: PASSED\r\n");
|
||||
} else {
|
||||
notification_message(notification, &sequence_error);
|
||||
printf("Status: FAILED\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_LOADER);
|
||||
}
|
||||
|
||||
void unit_tests_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
|
||||
// We need to launch apps from tests, so we cannot lock loader
|
||||
cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#endif
|
||||
}
|
||||
216
applications/debug/unit_tests/test_runner.c
Normal file
216
applications/debug/unit_tests/test_runner.c
Normal file
@@ -0,0 +1,216 @@
|
||||
#include "test_runner.h"
|
||||
|
||||
#include "tests/test_api.h"
|
||||
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <loader/loader.h>
|
||||
#include <storage/storage.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <loader/firmware_api/firmware_api.h>
|
||||
#include <flipper_application/flipper_application.h>
|
||||
#include <flipper_application/api_hashtable/api_hashtable.h>
|
||||
#include <flipper_application/plugins/composite_resolver.h>
|
||||
|
||||
extern const ElfApiInterface* const unit_tests_api_interface;
|
||||
|
||||
#define TAG "TestRunner"
|
||||
|
||||
#define PLUGINS_PATH "/ext/apps_data/unit_tests/plugins"
|
||||
|
||||
struct TestRunner {
|
||||
Storage* storage;
|
||||
Loader* loader;
|
||||
NotificationApp* notification;
|
||||
|
||||
// Temporary used things
|
||||
Cli* cli;
|
||||
FuriString* args;
|
||||
|
||||
// ELF related stuff
|
||||
CompositeApiResolver* composite_resolver;
|
||||
|
||||
// Report data
|
||||
int minunit_run;
|
||||
int minunit_assert;
|
||||
int minunit_fail;
|
||||
int minunit_status;
|
||||
};
|
||||
|
||||
TestRunner* test_runner_alloc(Cli* cli, FuriString* args) {
|
||||
TestRunner* instance = malloc(sizeof(TestRunner));
|
||||
|
||||
instance->storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->loader = furi_record_open(RECORD_LOADER);
|
||||
instance->notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
instance->cli = cli;
|
||||
instance->args = args;
|
||||
|
||||
instance->composite_resolver = composite_api_resolver_alloc();
|
||||
composite_api_resolver_add(instance->composite_resolver, firmware_api_interface);
|
||||
composite_api_resolver_add(instance->composite_resolver, unit_tests_api_interface);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void test_runner_free(TestRunner* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
composite_api_resolver_free(instance->composite_resolver);
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
instance->notification = NULL;
|
||||
|
||||
furi_record_close(RECORD_LOADER);
|
||||
instance->loader = NULL;
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
instance->storage = NULL;
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static bool test_runner_run_plugin(TestRunner* instance, const char* path) {
|
||||
furi_assert(instance);
|
||||
|
||||
FURI_LOG_D(TAG, "Loading %s", path);
|
||||
FlipperApplication* lib = flipper_application_alloc(
|
||||
instance->storage, composite_api_resolver_get(instance->composite_resolver));
|
||||
|
||||
bool result = false;
|
||||
instance->minunit_fail = -1;
|
||||
do {
|
||||
FlipperApplicationPreloadStatus preload_res = flipper_application_preload(lib, path);
|
||||
|
||||
if(preload_res != FlipperApplicationPreloadStatusSuccess) {
|
||||
FURI_LOG_E(TAG, "Failed to preload %s, %d", path, preload_res);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_application_is_plugin(lib)) {
|
||||
FURI_LOG_E(TAG, "Not a plugin %s", path);
|
||||
break;
|
||||
}
|
||||
|
||||
FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(lib);
|
||||
if(load_status != FlipperApplicationLoadStatusSuccess) {
|
||||
FURI_LOG_E(TAG, "Failed to load %s", path);
|
||||
break;
|
||||
}
|
||||
|
||||
const FlipperAppPluginDescriptor* app_descriptor =
|
||||
flipper_application_plugin_get_descriptor(lib);
|
||||
|
||||
const TestApi* test = app_descriptor->entry_point;
|
||||
|
||||
instance->minunit_fail = test->run();
|
||||
|
||||
instance->minunit_run += test->get_minunit_run();
|
||||
instance->minunit_assert += test->get_minunit_assert();
|
||||
instance->minunit_status += test->get_minunit_status();
|
||||
|
||||
result = (instance->minunit_fail == 0);
|
||||
} while(false);
|
||||
|
||||
flipper_application_free(lib);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void test_runner_run_internal(TestRunner* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
char file_name_buffer[256];
|
||||
FuriString* file_name = furi_string_alloc();
|
||||
FuriString* file_basename = furi_string_alloc();
|
||||
File* directory = storage_file_alloc(instance->storage);
|
||||
|
||||
do {
|
||||
if(!storage_dir_open(directory, PLUGINS_PATH)) {
|
||||
FURI_LOG_E(TAG, "Failed to open directory %s", PLUGINS_PATH);
|
||||
break;
|
||||
}
|
||||
|
||||
while(true) {
|
||||
if(cli_cmd_interrupt_received(instance->cli)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(!storage_dir_read(directory, NULL, file_name_buffer, sizeof(file_name_buffer))) {
|
||||
break;
|
||||
}
|
||||
|
||||
furi_string_set(file_name, file_name_buffer);
|
||||
if(!furi_string_end_with_str(file_name, ".fal")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
path_concat(PLUGINS_PATH, file_name_buffer, file_name);
|
||||
|
||||
path_extract_filename(file_name, file_basename, true);
|
||||
const char* file_basename_cstr = furi_string_get_cstr(file_basename);
|
||||
|
||||
bool result = true;
|
||||
if(furi_string_size(instance->args)) {
|
||||
if(furi_string_cmp_str(instance->args, file_basename_cstr) == 0) {
|
||||
result = test_runner_run_plugin(instance, furi_string_get_cstr(file_name));
|
||||
} else {
|
||||
printf("Skipping %s\r\n", file_basename_cstr);
|
||||
}
|
||||
} else {
|
||||
result = test_runner_run_plugin(instance, furi_string_get_cstr(file_name));
|
||||
}
|
||||
|
||||
if(!result) {
|
||||
printf("Failed to execute test: %s\r\n", file_basename_cstr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(false);
|
||||
|
||||
storage_dir_close(directory);
|
||||
storage_file_free(directory);
|
||||
furi_string_free(file_name);
|
||||
furi_string_free(file_basename);
|
||||
}
|
||||
|
||||
void test_runner_run(TestRunner* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
// TODO FL-3491: lock device while test running
|
||||
if(loader_is_locked(instance->loader)) {
|
||||
printf("RPC: stop all applications to run tests\r\n");
|
||||
notification_message(instance->notification, &sequence_blink_magenta_100);
|
||||
} else {
|
||||
notification_message_block(instance->notification, &sequence_set_only_blue_255);
|
||||
|
||||
uint32_t heap_before = memmgr_get_free_heap();
|
||||
uint32_t cycle_counter = furi_get_tick();
|
||||
|
||||
test_runner_run_internal(instance);
|
||||
|
||||
if(instance->minunit_run != 0) {
|
||||
printf("\r\nFailed tests: %d\r\n", instance->minunit_fail);
|
||||
|
||||
// Time report
|
||||
cycle_counter = (furi_get_tick() - cycle_counter);
|
||||
printf("Consumed: %lu ms\r\n", cycle_counter);
|
||||
|
||||
// Wait for tested services and apps to deallocate memory
|
||||
furi_delay_ms(200);
|
||||
uint32_t heap_after = memmgr_get_free_heap();
|
||||
printf("Leaked: %ld\r\n", heap_before - heap_after);
|
||||
|
||||
// Final Report
|
||||
if(instance->minunit_fail == 0) {
|
||||
notification_message(instance->notification, &sequence_success);
|
||||
printf("Status: PASSED\r\n");
|
||||
} else {
|
||||
notification_message(instance->notification, &sequence_error);
|
||||
printf("Status: FAILED\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
applications/debug/unit_tests/test_runner.h
Normal file
12
applications/debug/unit_tests/test_runner.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
typedef struct TestRunner TestRunner;
|
||||
typedef struct Cli Cli;
|
||||
|
||||
TestRunner* test_runner_alloc(Cli* cli, FuriString* args);
|
||||
|
||||
void test_runner_free(TestRunner* isntance);
|
||||
|
||||
void test_runner_run(TestRunner* isntance);
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
#include <bit_lib/bit_lib.h>
|
||||
|
||||
MU_TEST(test_bit_lib_increment_index) {
|
||||
@@ -737,4 +737,6 @@ MU_TEST_SUITE(test_bit_lib) {
|
||||
int run_minunit_test_bit_lib(void) {
|
||||
MU_RUN_SUITE(test_bit_lib);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_bit_lib)
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
#include <bt/bt_service/bt_keys_storage.h>
|
||||
#include <storage/storage.h>
|
||||
@@ -108,3 +108,5 @@ int run_minunit_test_bt(void) {
|
||||
MU_RUN_SUITE(test_bt);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_bt)
|
||||
42
applications/debug/unit_tests/tests/common/common.c
Normal file
42
applications/debug/unit_tests/tests/common/common.c
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "../test.h"
|
||||
#include "../minunit_vars.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
void minunit_print_progress(void) {
|
||||
static const char progress[] = {'\\', '|', '/', '-'};
|
||||
static uint8_t progress_counter = 0;
|
||||
static uint32_t last_tick = 0;
|
||||
uint32_t current_tick = furi_get_tick();
|
||||
if(current_tick - last_tick > 20) {
|
||||
last_tick = current_tick;
|
||||
printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void minunit_print_fail(const char* str) {
|
||||
printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str);
|
||||
}
|
||||
|
||||
void minunit_printf_warning(const char* format, ...) {
|
||||
FuriString* str = furi_string_alloc();
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
furi_string_vprintf(str, format, args);
|
||||
va_end(args);
|
||||
printf(_FURI_LOG_CLR_W "%s\r\n" _FURI_LOG_CLR_RESET, furi_string_get_cstr(str));
|
||||
furi_string_free(str);
|
||||
}
|
||||
|
||||
int get_minunit_run(void) {
|
||||
return minunit_run;
|
||||
}
|
||||
|
||||
int get_minunit_assert(void) {
|
||||
return minunit_assert;
|
||||
}
|
||||
|
||||
int get_minunit_status(void) {
|
||||
return minunit_status;
|
||||
}
|
||||
159
applications/debug/unit_tests/tests/compress/compress_test.c
Normal file
159
applications/debug/unit_tests/tests/compress/compress_test.c
Normal file
@@ -0,0 +1,159 @@
|
||||
#include "../test.h"
|
||||
|
||||
#include <toolbox/compress.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_random.h>
|
||||
|
||||
#include <storage/storage.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define COMPRESS_UNIT_TESTS_PATH(path) EXT_PATH("unit_tests/compress/" path)
|
||||
|
||||
static void compress_test_reference_comp_decomp() {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
File* compressed_file = storage_file_alloc(storage);
|
||||
File* decompressed_file = storage_file_alloc(storage);
|
||||
|
||||
mu_assert(
|
||||
storage_file_open(
|
||||
compressed_file,
|
||||
COMPRESS_UNIT_TESTS_PATH("compressed.bin"),
|
||||
FSAM_READ,
|
||||
FSOM_OPEN_EXISTING),
|
||||
"Failed to open compressed file");
|
||||
mu_assert(
|
||||
storage_file_open(
|
||||
decompressed_file,
|
||||
COMPRESS_UNIT_TESTS_PATH("uncompressed.bin"),
|
||||
FSAM_READ,
|
||||
FSOM_OPEN_EXISTING),
|
||||
"Failed to open decompressed file");
|
||||
|
||||
uint64_t compressed_ref_size = storage_file_size(compressed_file);
|
||||
uint64_t decompressed_ref_size = storage_file_size(decompressed_file);
|
||||
|
||||
mu_assert(compressed_ref_size > 0 && decompressed_ref_size > 0, "Invalid file sizes");
|
||||
|
||||
uint8_t* compressed_ref_buff = malloc(compressed_ref_size);
|
||||
uint8_t* decompressed_ref_buff = malloc(decompressed_ref_size);
|
||||
|
||||
mu_assert(
|
||||
storage_file_read(compressed_file, compressed_ref_buff, compressed_ref_size) ==
|
||||
compressed_ref_size,
|
||||
"Failed to read compressed file");
|
||||
|
||||
mu_assert(
|
||||
storage_file_read(decompressed_file, decompressed_ref_buff, decompressed_ref_size) ==
|
||||
decompressed_ref_size,
|
||||
"Failed to read decompressed file");
|
||||
|
||||
storage_file_free(compressed_file);
|
||||
storage_file_free(decompressed_file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
uint8_t* temp_buffer = malloc(1024);
|
||||
Compress* comp = compress_alloc(1024);
|
||||
|
||||
size_t encoded_size = 0;
|
||||
mu_assert(
|
||||
compress_encode(
|
||||
comp, decompressed_ref_buff, decompressed_ref_size, temp_buffer, 1024, &encoded_size),
|
||||
"Compress failed");
|
||||
|
||||
mu_assert(encoded_size == compressed_ref_size, "Encoded size is not equal to reference size");
|
||||
|
||||
mu_assert(
|
||||
memcmp(temp_buffer, compressed_ref_buff, compressed_ref_size) == 0,
|
||||
"Encoded buffer is not equal to reference");
|
||||
|
||||
size_t decoded_size = 0;
|
||||
mu_assert(
|
||||
compress_decode(
|
||||
comp, compressed_ref_buff, compressed_ref_size, temp_buffer, 1024, &decoded_size),
|
||||
"Decompress failed");
|
||||
|
||||
mu_assert(
|
||||
decoded_size == decompressed_ref_size, "Decoded size is not equal to reference size");
|
||||
|
||||
mu_assert(
|
||||
memcmp(temp_buffer, decompressed_ref_buff, decompressed_ref_size) == 0,
|
||||
"Decoded buffer is not equal to reference");
|
||||
|
||||
compress_free(comp);
|
||||
|
||||
free(temp_buffer);
|
||||
free(compressed_ref_buff);
|
||||
free(decompressed_ref_buff);
|
||||
}
|
||||
|
||||
static void compress_test_random_comp_decomp() {
|
||||
static const size_t src_buffer_size = 1024;
|
||||
static const size_t encoded_buffer_size = 1024;
|
||||
static const size_t small_buffer_size = src_buffer_size / 32;
|
||||
|
||||
// We only fill half of the buffer with random data, so if anything goes wrong, there's no overflow
|
||||
static const size_t src_data_size = src_buffer_size / 2;
|
||||
|
||||
Compress* comp = compress_alloc(src_buffer_size);
|
||||
uint8_t* src_buff = malloc(src_buffer_size);
|
||||
uint8_t* encoded_buff = malloc(encoded_buffer_size);
|
||||
uint8_t* decoded_buff = malloc(src_buffer_size);
|
||||
uint8_t* small_buff = malloc(small_buffer_size);
|
||||
|
||||
furi_hal_random_fill_buf(src_buff, src_data_size);
|
||||
|
||||
size_t encoded_size = 0;
|
||||
|
||||
mu_assert(
|
||||
compress_encode(
|
||||
comp, src_buff, src_data_size, encoded_buff, encoded_buffer_size, &encoded_size),
|
||||
"Compress failed");
|
||||
|
||||
mu_assert(encoded_size > 0, "Encoded size is zero");
|
||||
|
||||
size_t small_enc_dec_size = 0;
|
||||
mu_assert(
|
||||
compress_encode(
|
||||
comp, src_buff, src_data_size, small_buff, small_buffer_size, &small_enc_dec_size) ==
|
||||
false,
|
||||
"Compress to small buffer failed");
|
||||
|
||||
size_t decoded_size = 0;
|
||||
mu_assert(
|
||||
compress_decode(
|
||||
comp, encoded_buff, encoded_size, decoded_buff, src_buffer_size, &decoded_size),
|
||||
"Decompress failed");
|
||||
mu_assert(decoded_size == src_data_size, "Decoded size is not equal to source size");
|
||||
|
||||
mu_assert(
|
||||
memcmp(src_buff, decoded_buff, src_data_size) == 0,
|
||||
"Decoded buffer is not equal to source");
|
||||
|
||||
mu_assert(
|
||||
compress_decode(
|
||||
comp, encoded_buff, encoded_size, small_buff, small_buffer_size, &small_enc_dec_size) ==
|
||||
false,
|
||||
"Decompress to small buffer failed");
|
||||
|
||||
free(small_buff);
|
||||
free(src_buff);
|
||||
free(encoded_buff);
|
||||
free(decoded_buff);
|
||||
compress_free(comp);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_compress) {
|
||||
MU_RUN_TEST(compress_test_random_comp_decomp);
|
||||
MU_RUN_TEST(compress_test_reference_comp_decomp);
|
||||
}
|
||||
|
||||
int run_minunit_test_compress(void) {
|
||||
MU_RUN_SUITE(test_compress);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_compress)
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
#include <datetime/datetime.h>
|
||||
|
||||
@@ -188,4 +188,6 @@ int run_minunit_test_datetime(void) {
|
||||
MU_RUN_SUITE(test_datetime_validate_datetime);
|
||||
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_datetime)
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
MU_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields) {
|
||||
mu_assert(
|
||||
@@ -30,3 +30,5 @@ int run_minunit_test_dialogs_file_browser_options(void) {
|
||||
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_dialogs_file_browser_options)
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
#include <furi.h>
|
||||
#include <m-dict.h>
|
||||
#include <toolbox/dir_walk.h>
|
||||
@@ -269,4 +269,6 @@ MU_TEST_SUITE(test_dirwalk_suite) {
|
||||
int run_minunit_test_dirwalk(void) {
|
||||
MU_RUN_SUITE(test_dirwalk_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_dirwalk)
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal_random.h>
|
||||
@@ -198,3 +198,5 @@ int run_minunit_test_expansion(void) {
|
||||
MU_RUN_SUITE(test_expansion_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_expansion)
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
#include <toolbox/stream/stream.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
#define TEST_DIR TEST_DIR_NAME "/"
|
||||
#define TEST_DIR_NAME EXT_PATH("unit_tests_tmp")
|
||||
@@ -549,3 +549,5 @@ int run_minunit_test_flipper_format(void) {
|
||||
MU_RUN_SUITE(flipper_format);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_flipper_format)
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
#include <toolbox/stream/stream.h>
|
||||
#include <storage/storage.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
static const char* test_filetype = "Flipper Format test";
|
||||
static const uint32_t test_version = 666;
|
||||
@@ -335,3 +335,5 @@ int run_minunit_test_flipper_format_string(void) {
|
||||
MU_RUN_SUITE(flipper_format_string_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_flipper_format_string)
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <float.h>
|
||||
#include <float_tools.h>
|
||||
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
MU_TEST(float_tools_equal_test) {
|
||||
mu_check(float_is_equal(FLT_MAX, FLT_MAX));
|
||||
@@ -58,3 +58,5 @@ int run_minunit_test_float_tools(void) {
|
||||
MU_RUN_SUITE(float_tools_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_float_tools)
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
const uint32_t context_value = 0xdeadbeef;
|
||||
const uint32_t notify_value_0 = 0x12345678;
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
#define TEST_RECORD_NAME "test/holding"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
// v2 tests
|
||||
void test_furi_create_open(void);
|
||||
@@ -55,3 +55,5 @@ int run_minunit_test_furi(void) {
|
||||
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_furi)
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <lp5562_reg.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DATA_SIZE 4
|
||||
@@ -232,3 +232,5 @@ int run_minunit_test_furi_hal(void) {
|
||||
MU_RUN_SUITE(furi_hal_i2c_ext_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_furi_hal)
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
static const uint8_t key_ctr_1[32] = {
|
||||
0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C,
|
||||
@@ -600,3 +600,5 @@ int run_minunit_test_furi_hal_crypto(void) {
|
||||
MU_RUN_SUITE(furi_hal_crypto_gcm_test);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_furi_hal_crypto)
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
static void test_setup(void) {
|
||||
}
|
||||
@@ -466,4 +466,6 @@ int run_minunit_test_furi_string(void) {
|
||||
MU_RUN_SUITE(test_suite);
|
||||
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_furi_string)
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <flipper_format.h>
|
||||
#include <infrared.h>
|
||||
#include <common/infrared_common_i.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
#define IR_TEST_FILES_DIR EXT_PATH("unit_tests/infrared/")
|
||||
#define IR_TEST_FILE_PREFIX "test_"
|
||||
@@ -549,3 +549,5 @@ int run_minunit_test_infrared(void) {
|
||||
MU_RUN_SUITE(infrared_test);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_infrared)
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
#include <toolbox/protocols/protocol_dict.h>
|
||||
#include <lfrfid/protocols/lfrfid_protocols.h>
|
||||
#include <toolbox/pulse_protocols/pulse_glue.h>
|
||||
@@ -550,4 +550,6 @@ MU_TEST_SUITE(test_lfrfid_protocols_suite) {
|
||||
int run_minunit_test_lfrfid_protocols(void) {
|
||||
MU_RUN_SUITE(test_lfrfid_protocols_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_lfrfid_protocols)
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <furi.c>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
#include <update_util/resources/manifest.h>
|
||||
|
||||
#define TAG "Manifest"
|
||||
@@ -72,4 +72,6 @@ MU_TEST_SUITE(manifest_suite) {
|
||||
int run_minunit_test_manifest(void) {
|
||||
MU_RUN_SUITE(manifest_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_manifest)
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <toolbox/keys_dict.h>
|
||||
#include <nfc/nfc.h>
|
||||
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
#define TAG "NfcTest"
|
||||
|
||||
@@ -820,3 +820,5 @@ int run_minunit_test_nfc(void) {
|
||||
MU_RUN_SUITE(nfc);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_nfc)
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
static void power_test_deinit(void) {
|
||||
// Try to reset to default charge voltage limit
|
||||
@@ -67,3 +67,5 @@ int run_minunit_test_power(void) {
|
||||
MU_RUN_SUITE(test_power_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_power)
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
#include <toolbox/protocols/protocol_dict.h>
|
||||
|
||||
typedef enum {
|
||||
@@ -219,4 +219,6 @@ MU_TEST_SUITE(test_protocol_dict_suite) {
|
||||
int run_minunit_test_protocol_dict(void) {
|
||||
MU_RUN_SUITE(test_protocol_dict_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_protocol_dict)
|
||||
@@ -17,7 +17,7 @@
|
||||
#include <lib/toolbox/path.h>
|
||||
|
||||
#include <m-list.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
#include <protobuf_version.h>
|
||||
#include <pb.h>
|
||||
@@ -1864,3 +1864,5 @@ int32_t delay_test_app(void* p) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_rpc)
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
#include <furi.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
@@ -712,3 +712,5 @@ int run_minunit_test_storage(void) {
|
||||
MU_RUN_SUITE(test_md5_calc_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_storage)
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
#include <toolbox/stream/buffered_file_stream.h>
|
||||
#include <storage/storage.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
|
||||
static const char* stream_test_data = "I write differently from what I speak, "
|
||||
"I speak differently from what I think, "
|
||||
@@ -530,3 +530,5 @@ int run_minunit_test_stream(void) {
|
||||
MU_RUN_SUITE(stream_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_stream)
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../minunit.h"
|
||||
#include "../test.h"
|
||||
#include <lib/subghz/receiver.h>
|
||||
#include <lib/subghz/transmitter.h>
|
||||
#include <lib/subghz/subghz_keystore.h>
|
||||
@@ -906,3 +906,5 @@ int run_minunit_test_subghz(void) {
|
||||
MU_RUN_SUITE(subghz);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_subghz)
|
||||
12
applications/debug/unit_tests/tests/test.h
Normal file
12
applications/debug/unit_tests/tests/test.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
// Framework
|
||||
#include "minunit.h"
|
||||
|
||||
#include "test_api.h"
|
||||
|
||||
int get_minunit_run(void);
|
||||
|
||||
int get_minunit_assert(void);
|
||||
|
||||
int get_minunit_status(void);
|
||||
29
applications/debug/unit_tests/tests/test_api.h
Normal file
29
applications/debug/unit_tests/tests/test_api.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <flipper_application/flipper_application.h>
|
||||
|
||||
#define APPID "UnitTest"
|
||||
#define API_VERSION (0u)
|
||||
|
||||
typedef struct {
|
||||
int (*run)(void);
|
||||
int (*get_minunit_run)(void);
|
||||
int (*get_minunit_assert)(void);
|
||||
int (*get_minunit_status)(void);
|
||||
} TestApi;
|
||||
|
||||
#define TEST_API_DEFINE(entrypoint) \
|
||||
const TestApi test_api = { \
|
||||
.run = entrypoint, \
|
||||
.get_minunit_run = get_minunit_run, \
|
||||
.get_minunit_assert = get_minunit_assert, \
|
||||
.get_minunit_status = get_minunit_status, \
|
||||
}; \
|
||||
const FlipperAppPluginDescriptor app_descriptor = { \
|
||||
.appid = APPID, \
|
||||
.ep_api_version = API_VERSION, \
|
||||
.entry_point = &test_api, \
|
||||
}; \
|
||||
const FlipperAppPluginDescriptor* get_api(void) { \
|
||||
return &app_descriptor; \
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../minunit.h"
|
||||
|
||||
#include "../test.h"
|
||||
|
||||
#include <toolbox/varint.h>
|
||||
#include <toolbox/profiler.h>
|
||||
|
||||
@@ -85,4 +87,6 @@ MU_TEST_SUITE(test_varint_suite) {
|
||||
int run_minunit_test_varint(void) {
|
||||
MU_RUN_SUITE(test_varint_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_API_DEFINE(run_minunit_test_varint)
|
||||
19
applications/debug/unit_tests/unit_test_api_table.cpp
Normal file
19
applications/debug/unit_tests/unit_test_api_table.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <flipper_application/api_hashtable/api_hashtable.h>
|
||||
#include <flipper_application/api_hashtable/compilesort.hpp>
|
||||
|
||||
#include "unit_test_api_table_i.h"
|
||||
|
||||
static_assert(!has_hash_collisions(unit_tests_api_table), "Detected API method hash collision!");
|
||||
|
||||
constexpr HashtableApiInterface unit_tests_hashtable_api_interface{
|
||||
{
|
||||
.api_version_major = 0,
|
||||
.api_version_minor = 0,
|
||||
.resolver_callback = &elf_resolve_from_hashtable,
|
||||
},
|
||||
unit_tests_api_table.cbegin(),
|
||||
unit_tests_api_table.cend(),
|
||||
};
|
||||
|
||||
extern "C" const ElfApiInterface* const unit_tests_api_interface =
|
||||
&unit_tests_hashtable_api_interface;
|
||||
29
applications/debug/unit_tests/unit_test_api_table_i.h
Normal file
29
applications/debug/unit_tests/unit_test_api_table_i.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <update_util/resources/manifest.h>
|
||||
#include <nfc/protocols/slix/slix_i.h>
|
||||
#include <nfc/protocols/iso15693_3/iso15693_3_poller_i.h>
|
||||
#include <FreeRTOS.h>
|
||||
#include <FreeRTOS-Kernel/include/queue.h>
|
||||
|
||||
#include <rpc/rpc_i.h>
|
||||
#include <flipper.pb.h>
|
||||
|
||||
static constexpr auto unit_tests_api_table = sort(create_array_t<sym_entry>(
|
||||
API_METHOD(resource_manifest_reader_alloc, ResourceManifestReader*, (Storage*)),
|
||||
API_METHOD(resource_manifest_reader_free, void, (ResourceManifestReader*)),
|
||||
API_METHOD(resource_manifest_reader_open, bool, (ResourceManifestReader*, const char* filename)),
|
||||
API_METHOD(resource_manifest_reader_next, ResourceManifestEntry*, (ResourceManifestReader*)),
|
||||
API_METHOD(resource_manifest_reader_previous, ResourceManifestEntry*, (ResourceManifestReader*)),
|
||||
API_METHOD(slix_process_iso15693_3_error, SlixError, (Iso15693_3Error)),
|
||||
API_METHOD(iso15693_3_poller_get_data, const Iso15693_3Data*, (Iso15693_3Poller*)),
|
||||
API_METHOD(rpc_system_storage_get_error, PB_CommandStatus, (FS_Error)),
|
||||
API_METHOD(xQueueSemaphoreTake, BaseType_t, (QueueHandle_t, TickType_t)),
|
||||
API_METHOD(vQueueDelete, void, (QueueHandle_t)),
|
||||
API_METHOD(
|
||||
xQueueGenericCreate,
|
||||
QueueHandle_t,
|
||||
(const UBaseType_t, const UBaseType_t, const uint8_t)),
|
||||
API_METHOD(
|
||||
xQueueGenericSend,
|
||||
BaseType_t,
|
||||
(QueueHandle_t, const void* const, TickType_t, const BaseType_t)),
|
||||
API_VARIABLE(PB_Main_msg, PB_Main_msg_t)));
|
||||
21
applications/debug/unit_tests/unit_tests.c
Normal file
21
applications/debug/unit_tests/unit_tests.c
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
|
||||
#include "test_runner.h"
|
||||
|
||||
void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
UNUSED(context);
|
||||
|
||||
TestRunner* test_runner = test_runner_alloc(cli, args);
|
||||
test_runner_run(test_runner);
|
||||
test_runner_free(test_runner);
|
||||
}
|
||||
|
||||
void unit_tests_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user