1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 20:49:49 +04:00

Merge remote-tracking branch 'OFW/dev' into dev

This commit is contained in:
MX
2024-12-21 17:37:08 +03:00
18 changed files with 450 additions and 94 deletions

View File

@@ -0,0 +1,108 @@
#include <furi.h>
#include <errno.h>
#include <stdio.h>
#include "../test.h" // IWYU pragma: keep
#define TAG "StdioTest"
#define CONTEXT_MAGIC ((void*)0xDEADBEEF)
// stdin
static char mock_in[256];
static size_t mock_in_len, mock_in_pos;
static void set_mock_in(const char* str) {
size_t len = strlen(str);
strcpy(mock_in, str);
mock_in_len = len;
mock_in_pos = 0;
}
static size_t mock_in_cb(char* buffer, size_t size, FuriWait wait, void* context) {
UNUSED(wait);
furi_check(context == CONTEXT_MAGIC);
size_t remaining = mock_in_len - mock_in_pos;
size = MIN(remaining, size);
memcpy(buffer, mock_in + mock_in_pos, size);
mock_in_pos += size;
return size;
}
void test_stdin(void) {
FuriThreadStdinReadCallback in_cb = furi_thread_get_stdin_callback();
furi_thread_set_stdin_callback(mock_in_cb, CONTEXT_MAGIC);
char buf[256];
// plain in
set_mock_in("Hello, World!\n");
fgets(buf, sizeof(buf), stdin);
mu_assert_string_eq("Hello, World!\n", buf);
mu_assert_int_eq(EOF, getchar());
// ungetc
ungetc('i', stdin);
ungetc('H', stdin);
fgets(buf, sizeof(buf), stdin);
mu_assert_string_eq("Hi", buf);
mu_assert_int_eq(EOF, getchar());
// ungetc + plain in
set_mock_in(" World");
ungetc('i', stdin);
ungetc('H', stdin);
fgets(buf, sizeof(buf), stdin);
mu_assert_string_eq("Hi World", buf);
mu_assert_int_eq(EOF, getchar());
// partial plain in
set_mock_in("Hello, World!\n");
fgets(buf, strlen("Hello") + 1, stdin);
mu_assert_string_eq("Hello", buf);
mu_assert_int_eq(',', getchar());
fgets(buf, sizeof(buf), stdin);
mu_assert_string_eq(" World!\n", buf);
furi_thread_set_stdin_callback(in_cb, CONTEXT_MAGIC);
}
// stdout
static FuriString* mock_out;
FuriThreadStdoutWriteCallback original_out_cb;
static void mock_out_cb(const char* data, size_t size, void* context) {
furi_check(context == CONTEXT_MAGIC);
// there's no furi_string_cat_strn :(
for(size_t i = 0; i < size; i++) {
furi_string_push_back(mock_out, data[i]);
}
}
static void assert_and_clear_mock_out(const char* expected) {
// return the original stdout callback for the duration of the check
// if the check fails, we don't want the error to end up in our buffer,
// we want to be able to see it!
furi_thread_set_stdout_callback(original_out_cb, CONTEXT_MAGIC);
mu_assert_string_eq(expected, furi_string_get_cstr(mock_out));
furi_thread_set_stdout_callback(mock_out_cb, CONTEXT_MAGIC);
furi_string_reset(mock_out);
}
void test_stdout(void) {
original_out_cb = furi_thread_get_stdout_callback();
furi_thread_set_stdout_callback(mock_out_cb, CONTEXT_MAGIC);
mock_out = furi_string_alloc();
puts("Hello, World!");
assert_and_clear_mock_out("Hello, World!\n");
printf("He");
printf("llo!");
fflush(stdout);
assert_and_clear_mock_out("Hello!");
furi_string_free(mock_out);
furi_thread_set_stdout_callback(original_out_cb, CONTEXT_MAGIC);
}

View File

@@ -10,6 +10,8 @@ void test_furi_memmgr(void);
void test_furi_event_loop(void); void test_furi_event_loop(void);
void test_errno_saving(void); void test_errno_saving(void);
void test_furi_primitives(void); void test_furi_primitives(void);
void test_stdin(void);
void test_stdout(void);
static int foo = 0; static int foo = 0;
@@ -52,6 +54,11 @@ MU_TEST(mu_test_furi_primitives) {
test_furi_primitives(); test_furi_primitives();
} }
MU_TEST(mu_test_stdio) {
test_stdin();
test_stdout();
}
MU_TEST_SUITE(test_suite) { MU_TEST_SUITE(test_suite) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown); MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_check); MU_RUN_TEST(test_check);
@@ -61,6 +68,7 @@ MU_TEST_SUITE(test_suite) {
MU_RUN_TEST(mu_test_furi_pubsub); MU_RUN_TEST(mu_test_furi_pubsub);
MU_RUN_TEST(mu_test_furi_memmgr); MU_RUN_TEST(mu_test_furi_memmgr);
MU_RUN_TEST(mu_test_furi_event_loop); MU_RUN_TEST(mu_test_furi_event_loop);
MU_RUN_TEST(mu_test_stdio);
MU_RUN_TEST(mu_test_errno_saving); MU_RUN_TEST(mu_test_errno_saving);
MU_RUN_TEST(mu_test_furi_primitives); MU_RUN_TEST(mu_test_furi_primitives);
} }

View File

@@ -435,9 +435,9 @@ void cli_session_open(Cli* cli, void* session) {
cli->session = session; cli->session = session;
if(cli->session != NULL) { if(cli->session != NULL) {
cli->session->init(); cli->session->init();
furi_thread_set_stdout_callback(cli->session->tx_stdout); furi_thread_set_stdout_callback(cli->session->tx_stdout, NULL);
} else { } else {
furi_thread_set_stdout_callback(NULL); furi_thread_set_stdout_callback(NULL, NULL);
} }
furi_semaphore_release(cli->idle_sem); furi_semaphore_release(cli->idle_sem);
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
@@ -451,7 +451,7 @@ void cli_session_close(Cli* cli) {
cli->session->deinit(); cli->session->deinit();
} }
cli->session = NULL; cli->session = NULL;
furi_thread_set_stdout_callback(NULL); furi_thread_set_stdout_callback(NULL, NULL);
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
} }
@@ -465,9 +465,9 @@ int32_t cli_srv(void* p) {
furi_record_create(RECORD_CLI, cli); furi_record_create(RECORD_CLI, cli);
if(cli->session != NULL) { if(cli->session != NULL) {
furi_thread_set_stdout_callback(cli->session->tx_stdout); furi_thread_set_stdout_callback(cli->session->tx_stdout, NULL);
} else { } else {
furi_thread_set_stdout_callback(NULL); furi_thread_set_stdout_callback(NULL, NULL);
} }
if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) {

View File

@@ -28,8 +28,9 @@ struct CliSession {
void (*init)(void); void (*init)(void);
void (*deinit)(void); void (*deinit)(void);
size_t (*rx)(uint8_t* buffer, size_t size, uint32_t timeout); size_t (*rx)(uint8_t* buffer, size_t size, uint32_t timeout);
size_t (*rx_stdin)(uint8_t* buffer, size_t size, uint32_t timeout, void* context);
void (*tx)(const uint8_t* buffer, size_t size); void (*tx)(const uint8_t* buffer, size_t size);
void (*tx_stdout)(const char* data, size_t size); void (*tx_stdout)(const char* data, size_t size, void* context);
bool (*is_connected)(void); bool (*is_connected)(void);
}; };

View File

@@ -243,6 +243,11 @@ static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) {
return rx_cnt; return rx_cnt;
} }
static size_t cli_vcp_rx_stdin(uint8_t* data, size_t size, uint32_t timeout, void* context) {
UNUSED(context);
return cli_vcp_rx(data, size, timeout);
}
static void cli_vcp_tx(const uint8_t* buffer, size_t size) { static void cli_vcp_tx(const uint8_t* buffer, size_t size) {
furi_assert(vcp); furi_assert(vcp);
furi_assert(buffer); furi_assert(buffer);
@@ -268,7 +273,8 @@ static void cli_vcp_tx(const uint8_t* buffer, size_t size) {
VCP_DEBUG("tx %u end", size); VCP_DEBUG("tx %u end", size);
} }
static void cli_vcp_tx_stdout(const char* data, size_t size) { static void cli_vcp_tx_stdout(const char* data, size_t size, void* context) {
UNUSED(context);
cli_vcp_tx((const uint8_t*)data, size); cli_vcp_tx((const uint8_t*)data, size);
} }
@@ -311,6 +317,7 @@ CliSession cli_vcp = {
cli_vcp_init, cli_vcp_init,
cli_vcp_deinit, cli_vcp_deinit,
cli_vcp_rx, cli_vcp_rx,
cli_vcp_rx_stdin,
cli_vcp_tx, cli_vcp_tx,
cli_vcp_tx_stdout, cli_vcp_tx_stdout,
cli_vcp_is_connected, cli_vcp_is_connected,

View File

@@ -43,7 +43,7 @@ To add unit tests for your protocol, follow these steps:
1. Create a file named `test_<your_protocol_name>.irtest` in the [assets](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/resources/unit_tests/infrared) directory. 1. Create a file named `test_<your_protocol_name>.irtest` in the [assets](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/resources/unit_tests/infrared) directory.
2. Fill it with the test data (more on it below). 2. Fill it with the test data (more on it below).
3. Add the test code to [infrared_test.c](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/debug/unit_tests/infrared/infrared_test.c). 3. Add the test code to [infrared_test.c](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/debug/unit_tests/tests/infrared/infrared_test.c).
4. Build and install firmware with resources, install it on your Flipper and run the tests to see if they pass. 4. Build and install firmware with resources, install it on your Flipper and run the tests to see if they pass.
##### Test data format ##### Test data format

View File

@@ -106,5 +106,7 @@ void* aligned_malloc(size_t size, size_t alignment) {
} }
void aligned_free(void* p) { void aligned_free(void* p) {
free(((void**)p)[-1]); if(p) {
free(((void**)p)[-1]);
}
} }

View File

@@ -129,12 +129,12 @@ void furi_string_swap(FuriString* string_1, FuriString* string_2);
/** Move string_2 content to string_1. /** Move string_2 content to string_1.
* *
* Set the string to the other one, and destroy the other one. * Copy data from one string to another and destroy the source.
* *
* @param string_1 The FuriString instance 1 * @param destination The destination FuriString
* @param string_2 The FuriString instance 2 * @param source The source FuriString
*/ */
void furi_string_move(FuriString* string_1, FuriString* string_2); void furi_string_move(FuriString* destination, FuriString* source);
/** Compute a hash for the string. /** Compute a hash for the string.
* *

View File

@@ -23,12 +23,17 @@
#define THREAD_MAX_STACK_SIZE (UINT16_MAX * sizeof(StackType_t)) #define THREAD_MAX_STACK_SIZE (UINT16_MAX * sizeof(StackType_t))
typedef struct FuriThreadStdout FuriThreadStdout; typedef struct {
struct FuriThreadStdout {
FuriThreadStdoutWriteCallback write_callback; FuriThreadStdoutWriteCallback write_callback;
FuriString* buffer; FuriString* buffer;
}; void* context;
} FuriThreadStdout;
typedef struct {
FuriThreadStdinReadCallback read_callback;
FuriString* unread_buffer; // <! stores data from `ungetc` and friends
void* context;
} FuriThreadStdin;
struct FuriThread { struct FuriThread {
StaticTask_t container; StaticTask_t container;
@@ -55,6 +60,7 @@ struct FuriThread {
size_t heap_size; size_t heap_size;
FuriThreadStdout output; FuriThreadStdout output;
FuriThreadStdin input;
// Keep all non-alignable byte types in one place, // Keep all non-alignable byte types in one place,
// this ensures that the size of this structure is minimal // this ensures that the size of this structure is minimal
@@ -136,6 +142,7 @@ static void furi_thread_body(void* context) {
static void furi_thread_init_common(FuriThread* thread) { static void furi_thread_init_common(FuriThread* thread) {
thread->output.buffer = furi_string_alloc(); thread->output.buffer = furi_string_alloc();
thread->input.unread_buffer = furi_string_alloc();
FuriThread* parent = NULL; FuriThread* parent = NULL;
if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
@@ -245,6 +252,7 @@ void furi_thread_free(FuriThread* thread) {
} }
furi_string_free(thread->output.buffer); furi_string_free(thread->output.buffer);
furi_string_free(thread->input.unread_buffer);
free(thread); free(thread);
} }
@@ -710,13 +718,22 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) {
static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) { static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) {
if(thread->output.write_callback != NULL) { if(thread->output.write_callback != NULL) {
thread->output.write_callback(data, size); thread->output.write_callback(data, size, thread->output.context);
} else { } else {
furi_log_tx((const uint8_t*)data, size); furi_log_tx((const uint8_t*)data, size);
} }
return size; return size;
} }
static size_t
__furi_thread_stdin_read(FuriThread* thread, char* data, size_t size, FuriWait timeout) {
if(thread->input.read_callback != NULL) {
return thread->input.read_callback(data, size, timeout, thread->input.context);
} else {
return 0;
}
}
static int32_t __furi_thread_stdout_flush(FuriThread* thread) { static int32_t __furi_thread_stdout_flush(FuriThread* thread) {
FuriString* buffer = thread->output.buffer; FuriString* buffer = thread->output.buffer;
size_t size = furi_string_size(buffer); size_t size = furi_string_size(buffer);
@@ -727,19 +744,33 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread) {
return 0; return 0;
} }
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
__furi_thread_stdout_flush(thread);
thread->output.write_callback = callback;
}
FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void) { FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void) {
FuriThread* thread = furi_thread_get_current(); FuriThread* thread = furi_thread_get_current();
furi_check(thread); furi_check(thread);
return thread->output.write_callback; return thread->output.write_callback;
} }
FuriThreadStdinReadCallback furi_thread_get_stdin_callback(void) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
return thread->input.read_callback;
}
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback, void* context) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
__furi_thread_stdout_flush(thread);
thread->output.write_callback = callback;
thread->output.context = context;
}
void furi_thread_set_stdin_callback(FuriThreadStdinReadCallback callback, void* context) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
thread->input.read_callback = callback;
thread->input.context = context;
}
size_t furi_thread_stdout_write(const char* data, size_t size) { size_t furi_thread_stdout_write(const char* data, size_t size) {
FuriThread* thread = furi_thread_get_current(); FuriThread* thread = furi_thread_get_current();
furi_check(thread); furi_check(thread);
@@ -772,6 +803,31 @@ int32_t furi_thread_stdout_flush(void) {
return __furi_thread_stdout_flush(thread); return __furi_thread_stdout_flush(thread);
} }
size_t furi_thread_stdin_read(char* buffer, size_t size, FuriWait timeout) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
size_t from_buffer = MIN(furi_string_size(thread->input.unread_buffer), size);
size_t from_input = size - from_buffer;
size_t from_input_actual =
__furi_thread_stdin_read(thread, buffer + from_buffer, from_input, timeout);
memcpy(buffer, furi_string_get_cstr(thread->input.unread_buffer), from_buffer);
furi_string_right(thread->input.unread_buffer, from_buffer);
return from_buffer + from_input_actual;
}
void furi_thread_stdin_unread(char* buffer, size_t size) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
FuriString* new_buf = furi_string_alloc(); // there's no furi_string_alloc_set_strn :(
furi_string_set_strn(new_buf, buffer, size);
furi_string_cat(new_buf, thread->input.unread_buffer);
furi_string_free(thread->input.unread_buffer);
thread->input.unread_buffer = new_buf;
}
void furi_thread_suspend(FuriThreadId thread_id) { void furi_thread_suspend(FuriThreadId thread_id) {
furi_check(thread_id); furi_check(thread_id);

View File

@@ -74,8 +74,23 @@ typedef int32_t (*FuriThreadCallback)(void* context);
* *
* @param[in] data pointer to the data to be written to the standard out * @param[in] data pointer to the data to be written to the standard out
* @param[in] size size of the data in bytes * @param[in] size size of the data in bytes
* @param[in] context optional context
*/ */
typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size); typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size, void* context);
/**
* @brief Standard input callback function pointer type
*
* The function to be used as a standard input callback MUST follow this signature.
*
* @param[out] buffer buffer to read data into
* @param[in] size maximum number of bytes to read into the buffer
* @param[in] timeout how long to wait for (in ticks) before giving up
* @param[in] context optional context
* @returns number of bytes that was actually read into the buffer
*/
typedef size_t (
*FuriThreadStdinReadCallback)(char* buffer, size_t size, FuriWait timeout, void* context);
/** /**
* @brief State change callback function pointer type. * @brief State change callback function pointer type.
@@ -468,13 +483,30 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id);
*/ */
FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void); FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void);
/**
* @brief Get the standard input callback for the current thead.
*
* @return pointer to the standard in callback function
*/
FuriThreadStdinReadCallback furi_thread_get_stdin_callback(void);
/** Set standard output callback for the current thread. /** Set standard output callback for the current thread.
* *
* @param[in] callback pointer to the callback function or NULL to clear * @param[in] callback pointer to the callback function or NULL to clear
* @param[in] context context to be passed to the callback
*/ */
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback); void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback, void* context);
/** Set standard input callback for the current thread.
*
* @param[in] callback pointer to the callback function or NULL to clear
* @param[in] context context to be passed to the callback
*/
void furi_thread_set_stdin_callback(FuriThreadStdinReadCallback callback, void* context);
/** Write data to buffered standard output. /** Write data to buffered standard output.
*
* @note You can also use the standard C `putc`, `puts`, `printf` and friends.
* *
* @param[in] data pointer to the data to be written * @param[in] data pointer to the data to be written
* @param[in] size data size in bytes * @param[in] size data size in bytes
@@ -489,6 +521,29 @@ size_t furi_thread_stdout_write(const char* data, size_t size);
*/ */
int32_t furi_thread_stdout_flush(void); int32_t furi_thread_stdout_flush(void);
/** Read data from the standard input
*
* @note You can also use the standard C `getc`, `gets` and friends.
*
* @param[in] buffer pointer to the buffer to read data into
* @param[in] size how many bytes to read into the buffer
* @param[in] timeout how long to wait for (in ticks) before giving up
* @return number of bytes that was actually read
*/
size_t furi_thread_stdin_read(char* buffer, size_t size, FuriWait timeout);
/** Puts data back into the standard input buffer
*
* `furi_thread_stdin_read` will return the bytes in the same order that they
* were supplied to this function.
*
* @note You can also use the standard C `ungetc`.
*
* @param[in] buffer pointer to the buffer to get data from
* @param[in] size how many bytes to read from the buffer
*/
void furi_thread_stdin_unread(char* buffer, size_t size);
/** /**
* @brief Suspend a thread. * @brief Suspend a thread.
* *

View File

@@ -830,9 +830,7 @@ void elf_file_free(ELFFile* elf) {
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it);
ELFSectionDict_next(it)) { ELFSectionDict_next(it)) {
const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it); const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it);
if(itref->value.data) { aligned_free(itref->value.data);
aligned_free(itref->value.data);
}
if(itref->value.fast_rel) { if(itref->value.fast_rel) {
if(itref->value.fast_rel->data) { if(itref->value.fast_rel->data) {
aligned_free(itref->value.fast_rel->data); aligned_free(itref->value.fast_rel->data);

View File

@@ -44,18 +44,24 @@ wrapped_fn_list = [
"vsiprintf", "vsiprintf",
"vsniprintf", "vsniprintf",
# #
# Scanf is not implemented 4 now # standard input
#
"fgetc",
"getc",
"getchar",
"fgets",
"ungetc",
#
# standard input, but unimplemented
#
"gets",
#
# scanf, not implemented for now
# #
# "fscanf", # "fscanf",
# "scanf", # "scanf",
# "sscanf", # "sscanf",
# "vsprintf", # "vsprintf",
# "fgetc",
# "fgets",
# "getc",
# "getchar",
# "gets",
# "ungetc",
# "vfscanf", # "vfscanf",
# "vscanf", # "vscanf",
# "vsscanf", # "vsscanf",

View File

@@ -51,11 +51,54 @@ int __wrap_snprintf(char* str, size_t size, const char* format, ...) {
} }
int __wrap_fflush(FILE* stream) { int __wrap_fflush(FILE* stream) {
UNUSED(stream); if(stream == stdout) furi_thread_stdout_flush();
furi_thread_stdout_flush();
return 0; return 0;
} }
int __wrap_fgetc(FILE* stream) {
if(stream != stdin) return EOF;
char c;
if(furi_thread_stdin_read(&c, 1, FuriWaitForever) == 0) return EOF;
return c;
}
int __wrap_getc(FILE* stream) {
return __wrap_fgetc(stream);
}
int __wrap_getchar(void) {
return __wrap_fgetc(stdin);
}
char* __wrap_fgets(char* str, size_t n, FILE* stream) {
// leave space for the zero terminator
furi_check(n >= 1);
n--;
if(stream != stdin) {
*str = '\0';
return str;
}
// read characters
int c;
do {
c = __wrap_fgetc(stdin);
if(c > 0) *(str++) = c;
} while(c != EOF && c != '\n' && --n);
// place zero terminator
*str = '\0';
return str;
}
int __wrap_ungetc(int ch, FILE* stream) {
char c = ch;
if(stream != stdin) return EOF;
furi_thread_stdin_unread(&c, 1);
return ch;
}
__attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e) { __attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e) {
UNUSED(file); UNUSED(file);
UNUSED(line); UNUSED(line);

View File

@@ -16,6 +16,12 @@ int __wrap_putc(int ch, FILE* stream);
int __wrap_snprintf(char* str, size_t size, const char* format, ...); int __wrap_snprintf(char* str, size_t size, const char* format, ...);
int __wrap_fflush(FILE* stream); int __wrap_fflush(FILE* stream);
int __wrap_fgetc(FILE* stream);
int __wrap_getc(FILE* stream);
int __wrap_getchar(void);
char* __wrap_fgets(char* str, size_t n, FILE* stream);
int __wrap_ungetc(int ch, FILE* stream);
__attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e); __attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e);
__attribute__((__noreturn__)) void __attribute__((__noreturn__)) void

View File

@@ -245,6 +245,8 @@ static Iso15693ParserCommand iso15693_parser_parse_1_out_of_256(Iso15693Parser*
instance->parsed_frame, instance->next_byte_part * 4 + j / 2); instance->parsed_frame, instance->next_byte_part * 4 + j / 2);
} }
} }
} else {
instance->zero_found = true;
} }
} }
instance->next_byte_part = (instance->next_byte_part + 1) % 64; instance->next_byte_part = (instance->next_byte_part + 1) % 64;

View File

@@ -1,8 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import re import re
import sys
import time import time
from datetime import datetime
from typing import Optional from typing import Optional
from flipper.app import App from flipper.app import App
@@ -11,7 +10,10 @@ from flipper.utils.cdc import resolve_port
class Main(App): class Main(App):
# this is basic use without sub-commands, simply to reboot flipper / power it off, not meant as a full CLI wrapper def __init__(self, no_exit=False):
super().__init__(no_exit)
self.test_results = None
def init(self): def init(self):
self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") self.parser.add_argument("-p", "--port", help="CDC Port", default="auto")
self.parser.add_argument( self.parser.add_argument(
@@ -67,64 +69,108 @@ class Main(App):
self.logger.info("Running unit tests") self.logger.info("Running unit tests")
flipper.send("unit_tests" + "\r") flipper.send("unit_tests" + "\r")
self.logger.info("Waiting for unit tests to complete") self.logger.info("Waiting for unit tests to complete")
data = flipper.read.until(">: ")
self.logger.info("Parsing result")
lines = data.decode().split("\r\n")
tests_re = r"Failed tests: \d{0,}"
time_re = r"Consumed: \d{0,}"
leak_re = r"Leaked: \d{0,}"
status_re = r"Status: \w{3,}"
tests_pattern = re.compile(tests_re)
time_pattern = re.compile(time_re)
leak_pattern = re.compile(leak_re)
status_pattern = re.compile(status_re)
tests, elapsed_time, leak, status = None, None, None, None tests, elapsed_time, leak, status = None, None, None, None
total = 0 total = 0
all_required_found = False
for line in lines: full_output = []
self.logger.info(line)
if "()" in line:
total += 1
if not tests: tests_pattern = re.compile(r"Failed tests: \d{0,}")
tests = re.match(tests_pattern, line) time_pattern = re.compile(r"Consumed: \d{0,}")
if not elapsed_time: leak_pattern = re.compile(r"Leaked: \d{0,}")
elapsed_time = re.match(time_pattern, line) status_pattern = re.compile(r"Status: \w{3,}")
if not leak:
leak = re.match(leak_pattern, line)
if not status:
status = re.match(status_pattern, line)
if None in (tests, elapsed_time, leak, status): try:
self.logger.error( while not all_required_found:
f"Failed to parse output: {tests} {elapsed_time} {leak} {status}" try:
line = flipper.read.until("\r\n", cut_eol=True).decode()
self.logger.info(line)
if "command not found," in line:
self.logger.error(f"Command not found: {line}")
return 1
if "()" in line:
total += 1
self.logger.debug(f"Test completed: {line}")
if not tests:
tests = tests_pattern.match(line)
if not elapsed_time:
elapsed_time = time_pattern.match(line)
if not leak:
leak = leak_pattern.match(line)
if not status:
status = status_pattern.match(line)
pattern = re.compile(
r"(\[-]|\[\\]|\[\|]|\[/-]|\[[^\]]*\]|\x1b\[\d+D)"
)
line_to_append = pattern.sub("", line)
pattern = re.compile(r"\[3D[^\]]*")
line_to_append = pattern.sub("", line_to_append)
line_to_append = f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')} {line_to_append}"
full_output.append(line_to_append)
if tests and elapsed_time and leak and status:
all_required_found = True
try:
remaining = flipper.read.until(">: ", cut_eol=True).decode()
if remaining.strip():
full_output.append(remaining)
except:
pass
break
except Exception as e:
self.logger.error(f"Error reading output: {e}")
raise
if None in (tests, elapsed_time, leak, status):
raise RuntimeError(
f"Failed to parse output: {tests} {elapsed_time} {leak} {status}"
)
leak = int(re.findall(r"[- ]\d+", leak.group(0))[0])
status = re.findall(r"\w+", status.group(0))[1]
tests = int(re.findall(r"\d+", tests.group(0))[0])
elapsed_time = int(re.findall(r"\d+", elapsed_time.group(0))[0])
test_results = {
"full_output": "\n".join(full_output),
"total_tests": total,
"failed_tests": tests,
"elapsed_time_ms": elapsed_time,
"memory_leak_bytes": leak,
"status": status,
}
self.test_results = test_results
output_file = "unit_tests_output.txt"
with open(output_file, "w") as f:
f.write(test_results["full_output"])
print(
f"::notice:: Total tests: {total} Failed tests: {tests} Status: {status} Elapsed time: {elapsed_time / 1000} s Memory leak: {leak} bytes"
) )
sys.exit(1)
leak = int(re.findall(r"[- ]\d+", leak.group(0))[0]) if tests > 0 or status != "PASSED":
status = re.findall(r"\w+", status.group(0))[1] self.logger.error(f"Got {tests} failed tests.")
tests = int(re.findall(r"\d+", tests.group(0))[0]) self.logger.error(f"Leaked (not failing on this stat): {leak}")
elapsed_time = int(re.findall(r"\d+", elapsed_time.group(0))[0]) self.logger.error(f"Status: {status}")
self.logger.error(f"Time: {elapsed_time / 1000} seconds")
return 1
if tests > 0 or status != "PASSED": self.logger.info(f"Leaked (not failing on this stat): {leak}")
self.logger.error(f"Got {tests} failed tests.") self.logger.info(
self.logger.error(f"Leaked (not failing on this stat): {leak}") f"Tests ran successfully! Time elapsed {elapsed_time / 1000} seconds. Passed {total} tests."
self.logger.error(f"Status: {status}") )
self.logger.error(f"Time: {elapsed_time/1000} seconds") return 0
finally:
flipper.stop() flipper.stop()
return 1
self.logger.info(f"Leaked (not failing on this stat): {leak}")
self.logger.info(
f"Tests ran successfully! Time elapsed {elapsed_time/1000} seconds. Passed {total} tests."
)
flipper.stop()
return 0
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,78.2,, Version,+,79.0,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
@@ -371,11 +371,16 @@ Function,-,__utoa,char*,"unsigned, char*, int"
Function,+,__wrap___assert,void,"const char*, int, const char*" Function,+,__wrap___assert,void,"const char*, int, const char*"
Function,+,__wrap___assert_func,void,"const char*, int, const char*, const char*" Function,+,__wrap___assert_func,void,"const char*, int, const char*, const char*"
Function,+,__wrap_fflush,int,FILE* Function,+,__wrap_fflush,int,FILE*
Function,+,__wrap_fgetc,int,FILE*
Function,+,__wrap_fgets,char*,"char*, size_t, FILE*"
Function,+,__wrap_getc,int,FILE*
Function,+,__wrap_getchar,int,
Function,+,__wrap_printf,int,"const char*, ..." Function,+,__wrap_printf,int,"const char*, ..."
Function,+,__wrap_putc,int,"int, FILE*" Function,+,__wrap_putc,int,"int, FILE*"
Function,+,__wrap_putchar,int,int Function,+,__wrap_putchar,int,int
Function,+,__wrap_puts,int,const char* Function,+,__wrap_puts,int,const char*
Function,+,__wrap_snprintf,int,"char*, size_t, const char*, ..." Function,+,__wrap_snprintf,int,"char*, size_t, const char*, ..."
Function,+,__wrap_ungetc,int,"int, FILE*"
Function,+,__wrap_vsnprintf,int,"char*, size_t, const char*, va_list" Function,+,__wrap_vsnprintf,int,"char*, size_t, const char*, va_list"
Function,-,_asiprintf_r,int,"_reent*, char**, const char*, ..." Function,-,_asiprintf_r,int,"_reent*, char**, const char*, ..."
Function,-,_asniprintf_r,char*,"_reent*, char*, size_t*, const char*, ..." Function,-,_asniprintf_r,char*,"_reent*, char*, size_t*, const char*, ..."
@@ -1655,6 +1660,7 @@ Function,+,furi_thread_get_return_code,int32_t,FuriThread*
Function,+,furi_thread_get_signal_callback,FuriThreadSignalCallback,const FuriThread* Function,+,furi_thread_get_signal_callback,FuriThreadSignalCallback,const FuriThread*
Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId
Function,+,furi_thread_get_state,FuriThreadState,FuriThread* Function,+,furi_thread_get_state,FuriThreadState,FuriThread*
Function,+,furi_thread_get_stdin_callback,FuriThreadStdinReadCallback,
Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback,
Function,+,furi_thread_is_suspended,_Bool,FuriThreadId Function,+,furi_thread_is_suspended,_Bool,FuriThreadId
Function,+,furi_thread_join,_Bool,FuriThread* Function,+,furi_thread_join,_Bool,FuriThread*
@@ -1675,9 +1681,12 @@ Function,+,furi_thread_set_signal_callback,void,"FuriThread*, FuriThreadSignalCa
Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t"
Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback" Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback"
Function,+,furi_thread_set_state_context,void,"FuriThread*, void*" Function,+,furi_thread_set_state_context,void,"FuriThread*, void*"
Function,+,furi_thread_set_stdout_callback,void,FuriThreadStdoutWriteCallback Function,+,furi_thread_set_stdin_callback,void,"FuriThreadStdinReadCallback, void*"
Function,+,furi_thread_set_stdout_callback,void,"FuriThreadStdoutWriteCallback, void*"
Function,+,furi_thread_signal,_Bool,"const FuriThread*, uint32_t, void*" Function,+,furi_thread_signal,_Bool,"const FuriThread*, uint32_t, void*"
Function,+,furi_thread_start,void,FuriThread* Function,+,furi_thread_start,void,FuriThread*
Function,+,furi_thread_stdin_read,size_t,"char*, size_t, FuriWait"
Function,+,furi_thread_stdin_unread,void,"char*, size_t"
Function,+,furi_thread_stdout_flush,int32_t, Function,+,furi_thread_stdout_flush,int32_t,
Function,+,furi_thread_stdout_write,size_t,"const char*, size_t" Function,+,furi_thread_stdout_write,size_t,"const char*, size_t"
Function,+,furi_thread_suspend,void,FuriThreadId Function,+,furi_thread_suspend,void,FuriThreadId
1 entry status name type params
2 Version + 78.2 79.0
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/bt/bt_service/bt_keys_storage.h
5 Header + applications/services/cli/cli.h
371 Function + __wrap___assert void const char*, int, const char*
372 Function + __wrap___assert_func void const char*, int, const char*, const char*
373 Function + __wrap_fflush int FILE*
374 Function + __wrap_fgetc int FILE*
375 Function + __wrap_fgets char* char*, size_t, FILE*
376 Function + __wrap_getc int FILE*
377 Function + __wrap_getchar int
378 Function + __wrap_printf int const char*, ...
379 Function + __wrap_putc int int, FILE*
380 Function + __wrap_putchar int int
381 Function + __wrap_puts int const char*
382 Function + __wrap_snprintf int char*, size_t, const char*, ...
383 Function + __wrap_ungetc int int, FILE*
384 Function + __wrap_vsnprintf int char*, size_t, const char*, va_list
385 Function - _asiprintf_r int _reent*, char**, const char*, ...
386 Function - _asniprintf_r char* _reent*, char*, size_t*, const char*, ...
1660 Function + furi_thread_get_signal_callback FuriThreadSignalCallback const FuriThread*
1661 Function + furi_thread_get_stack_space uint32_t FuriThreadId
1662 Function + furi_thread_get_state FuriThreadState FuriThread*
1663 Function + furi_thread_get_stdin_callback FuriThreadStdinReadCallback
1664 Function + furi_thread_get_stdout_callback FuriThreadStdoutWriteCallback
1665 Function + furi_thread_is_suspended _Bool FuriThreadId
1666 Function + furi_thread_join _Bool FuriThread*
1681 Function + furi_thread_set_stack_size void FuriThread*, size_t
1682 Function + furi_thread_set_state_callback void FuriThread*, FuriThreadStateCallback
1683 Function + furi_thread_set_state_context void FuriThread*, void*
1684 Function + furi_thread_set_stdout_callback furi_thread_set_stdin_callback void FuriThreadStdoutWriteCallback FuriThreadStdinReadCallback, void*
1685 Function + furi_thread_set_stdout_callback void FuriThreadStdoutWriteCallback, void*
1686 Function + furi_thread_signal _Bool const FuriThread*, uint32_t, void*
1687 Function + furi_thread_start void FuriThread*
1688 Function + furi_thread_stdin_read size_t char*, size_t, FuriWait
1689 Function + furi_thread_stdin_unread void char*, size_t
1690 Function + furi_thread_stdout_flush int32_t
1691 Function + furi_thread_stdout_write size_t const char*, size_t
1692 Function + furi_thread_suspend void FuriThreadId

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,78.2,, Version,+,79.0,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
@@ -451,11 +451,16 @@ Function,-,__utoa,char*,"unsigned, char*, int"
Function,+,__wrap___assert,void,"const char*, int, const char*" Function,+,__wrap___assert,void,"const char*, int, const char*"
Function,+,__wrap___assert_func,void,"const char*, int, const char*, const char*" Function,+,__wrap___assert_func,void,"const char*, int, const char*, const char*"
Function,+,__wrap_fflush,int,FILE* Function,+,__wrap_fflush,int,FILE*
Function,+,__wrap_fgetc,int,FILE*
Function,+,__wrap_fgets,char*,"char*, size_t, FILE*"
Function,+,__wrap_getc,int,FILE*
Function,+,__wrap_getchar,int,
Function,+,__wrap_printf,int,"const char*, ..." Function,+,__wrap_printf,int,"const char*, ..."
Function,+,__wrap_putc,int,"int, FILE*" Function,+,__wrap_putc,int,"int, FILE*"
Function,+,__wrap_putchar,int,int Function,+,__wrap_putchar,int,int
Function,+,__wrap_puts,int,const char* Function,+,__wrap_puts,int,const char*
Function,+,__wrap_snprintf,int,"char*, size_t, const char*, ..." Function,+,__wrap_snprintf,int,"char*, size_t, const char*, ..."
Function,+,__wrap_ungetc,int,"int, FILE*"
Function,+,__wrap_vsnprintf,int,"char*, size_t, const char*, va_list" Function,+,__wrap_vsnprintf,int,"char*, size_t, const char*, va_list"
Function,-,_asiprintf_r,int,"_reent*, char**, const char*, ..." Function,-,_asiprintf_r,int,"_reent*, char**, const char*, ..."
Function,-,_asniprintf_r,char*,"_reent*, char*, size_t*, const char*, ..." Function,-,_asniprintf_r,char*,"_reent*, char*, size_t*, const char*, ..."
@@ -1918,6 +1923,7 @@ Function,+,furi_thread_get_return_code,int32_t,FuriThread*
Function,+,furi_thread_get_signal_callback,FuriThreadSignalCallback,const FuriThread* Function,+,furi_thread_get_signal_callback,FuriThreadSignalCallback,const FuriThread*
Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId
Function,+,furi_thread_get_state,FuriThreadState,FuriThread* Function,+,furi_thread_get_state,FuriThreadState,FuriThread*
Function,+,furi_thread_get_stdin_callback,FuriThreadStdinReadCallback,
Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback,
Function,+,furi_thread_is_suspended,_Bool,FuriThreadId Function,+,furi_thread_is_suspended,_Bool,FuriThreadId
Function,+,furi_thread_join,_Bool,FuriThread* Function,+,furi_thread_join,_Bool,FuriThread*
@@ -1938,9 +1944,12 @@ Function,+,furi_thread_set_signal_callback,void,"FuriThread*, FuriThreadSignalCa
Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t"
Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback" Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback"
Function,+,furi_thread_set_state_context,void,"FuriThread*, void*" Function,+,furi_thread_set_state_context,void,"FuriThread*, void*"
Function,+,furi_thread_set_stdout_callback,void,FuriThreadStdoutWriteCallback Function,+,furi_thread_set_stdin_callback,void,"FuriThreadStdinReadCallback, void*"
Function,+,furi_thread_set_stdout_callback,void,"FuriThreadStdoutWriteCallback, void*"
Function,+,furi_thread_signal,_Bool,"const FuriThread*, uint32_t, void*" Function,+,furi_thread_signal,_Bool,"const FuriThread*, uint32_t, void*"
Function,+,furi_thread_start,void,FuriThread* Function,+,furi_thread_start,void,FuriThread*
Function,+,furi_thread_stdin_read,size_t,"char*, size_t, FuriWait"
Function,+,furi_thread_stdin_unread,void,"char*, size_t"
Function,+,furi_thread_stdout_flush,int32_t, Function,+,furi_thread_stdout_flush,int32_t,
Function,+,furi_thread_stdout_write,size_t,"const char*, size_t" Function,+,furi_thread_stdout_write,size_t,"const char*, size_t"
Function,+,furi_thread_suspend,void,FuriThreadId Function,+,furi_thread_suspend,void,FuriThreadId
1 entry status name type params
2 Version + 78.2 79.0
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/services/bt/bt_service/bt.h
5 Header + applications/services/bt/bt_service/bt_keys_storage.h
451 Function + __wrap___assert void const char*, int, const char*
452 Function + __wrap___assert_func void const char*, int, const char*, const char*
453 Function + __wrap_fflush int FILE*
454 Function + __wrap_fgetc int FILE*
455 Function + __wrap_fgets char* char*, size_t, FILE*
456 Function + __wrap_getc int FILE*
457 Function + __wrap_getchar int
458 Function + __wrap_printf int const char*, ...
459 Function + __wrap_putc int int, FILE*
460 Function + __wrap_putchar int int
461 Function + __wrap_puts int const char*
462 Function + __wrap_snprintf int char*, size_t, const char*, ...
463 Function + __wrap_ungetc int int, FILE*
464 Function + __wrap_vsnprintf int char*, size_t, const char*, va_list
465 Function - _asiprintf_r int _reent*, char**, const char*, ...
466 Function - _asniprintf_r char* _reent*, char*, size_t*, const char*, ...
1923 Function + furi_thread_get_signal_callback FuriThreadSignalCallback const FuriThread*
1924 Function + furi_thread_get_stack_space uint32_t FuriThreadId
1925 Function + furi_thread_get_state FuriThreadState FuriThread*
1926 Function + furi_thread_get_stdin_callback FuriThreadStdinReadCallback
1927 Function + furi_thread_get_stdout_callback FuriThreadStdoutWriteCallback
1928 Function + furi_thread_is_suspended _Bool FuriThreadId
1929 Function + furi_thread_join _Bool FuriThread*
1944 Function + furi_thread_set_stack_size void FuriThread*, size_t
1945 Function + furi_thread_set_state_callback void FuriThread*, FuriThreadStateCallback
1946 Function + furi_thread_set_state_context void FuriThread*, void*
1947 Function + furi_thread_set_stdout_callback furi_thread_set_stdin_callback void FuriThreadStdoutWriteCallback FuriThreadStdinReadCallback, void*
1948 Function + furi_thread_set_stdout_callback void FuriThreadStdoutWriteCallback, void*
1949 Function + furi_thread_signal _Bool const FuriThread*, uint32_t, void*
1950 Function + furi_thread_start void FuriThread*
1951 Function + furi_thread_stdin_read size_t char*, size_t, FuriWait
1952 Function + furi_thread_stdin_unread void char*, size_t
1953 Function + furi_thread_stdout_flush int32_t
1954 Function + furi_thread_stdout_write size_t const char*, size_t
1955 Function + furi_thread_suspend void FuriThreadId