[FL-2627] Flipper applications: SDK, build and debug system (#1387)

* Added support for running applications from SD card (FAPs - Flipper Application Packages)
* Added plugin_dist target for fbt to build FAPs
* All apps of type FlipperAppType.EXTERNAL and FlipperAppType.PLUGIN are built as FAPs by default
* Updated VSCode configuration for new fbt features - re-deploy stock configuration to use them
* Added debugging support for FAPs with fbt debug & VSCode
* Added public firmware API with automated versioning

Co-authored-by: hedger <hedger@users.noreply.github.com>
Co-authored-by: SG <who.just.the.doctor@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
SG
2022-09-15 02:11:38 +10:00
committed by Aleksandr Kutuzov
parent 0f6f9ad52e
commit b9a766d909
895 changed files with 8862 additions and 1465 deletions

View File

@@ -0,0 +1,18 @@
App(
appid="unit_tests",
apptype=FlipperAppType.STARTUP,
entry_point="unit_tests_on_system_start",
cdefines=["APP_UNIT_TESTS"],
provides=["delay_test"],
order=100,
)
App(
appid="delay_test",
name="Delay Test",
apptype=FlipperAppType.SYSTEM,
entry_point="delay_test_app",
stack_size=1 * 1024,
requires=["unit_tests"],
order=110,
)

View File

@@ -0,0 +1,337 @@
#include <furi.h>
#include <flipper_format/flipper_format.h>
#include <flipper_format/flipper_format_i.h>
#include <toolbox/stream/stream.h>
#include <storage/storage.h>
#include "../minunit.h"
static const char* test_filetype = "Flipper Format test";
static const uint32_t test_version = 666;
static const char* test_string_key = "String data";
static const char* test_string_data = "String";
static const char* test_string_updated_data = "New string";
static const char* test_string_updated_2_data = "And some more";
static const char* test_int_key = "Int32 data";
static const int32_t test_int_data[] = {1234, -6345, 7813, 0};
static const int32_t test_int_updated_data[] = {-1337, 69};
static const int32_t test_int_updated_2_data[] = {-3, -2, -1, 0, 1, 2, 3};
static const char* test_uint_key = "Uint32 data";
static const uint32_t test_uint_data[] = {1234, 0, 5678, 9098, 7654321};
static const uint32_t test_uint_updated_data[] = {8, 800, 555, 35, 35};
static const uint32_t test_uint_updated_2_data[] = {20, 21};
static const char* test_float_key = "Float data";
static const float test_float_data[] = {1.5f, 1000.0f};
static const float test_float_updated_data[] = {1.2f};
static const float test_float_updated_2_data[] = {0.01f, 0.0f, -51.6f};
static const char* test_hex_key = "Hex data";
static const uint8_t test_hex_data[] = {0xDE, 0xAD, 0xBE};
static const uint8_t test_hex_updated_data[] = {0xFE, 0xCA};
static const uint8_t test_hex_updated_2_data[] = {0xCA, 0xCA, 0x05};
static const char* test_hex_new_key = "New Hex data";
static const uint8_t test_hex_new_data[] = {0xFF, 0x6A, 0x91};
static const char* test_data_nix = "Filetype: Flipper Format test\n"
"Version: 666\n"
"# This is comment\n"
"String data: String\n"
"Int32 data: 1234 -6345 7813 0\n"
"Uint32 data: 1234 0 5678 9098 7654321\n"
"Float data: 1.5 1000.0\n"
"Hex data: DE AD BE";
static const char* test_data_win = "Filetype: Flipper Format test\r\n"
"Version: 666\r\n"
"# This is comment\r\n"
"String data: String\r\n"
"Int32 data: 1234 -6345 7813 0\r\n"
"Uint32 data: 1234 0 5678 9098 7654321\r\n"
"Float data: 1.5 1000.0\r\n"
"Hex data: DE AD BE";
#define ARRAY_W_COUNT(x) (x), (COUNT_OF(x))
#define ARRAY_W_BSIZE(x) (x), (sizeof(x))
MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {
string_t tmpstr;
uint32_t version;
uint32_t uint32_data[COUNT_OF(test_uint_data)];
int32_t int32_data[COUNT_OF(test_int_data)];
float float_data[COUNT_OF(test_float_data)];
uint8_t hex_data[COUNT_OF(test_hex_data)];
uint32_t count;
// key exist test
size_t position_before = stream_tell(flipper_format_get_raw_stream(flipper_format));
mu_check(flipper_format_key_exist(flipper_format, test_hex_key));
mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
mu_check(!flipper_format_key_exist(flipper_format, "invalid key"));
mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
// stream seek to end test
mu_check(flipper_format_seek_to_end(flipper_format));
mu_assert_int_eq(
stream_size(flipper_format_get_raw_stream(flipper_format)),
stream_tell(flipper_format_get_raw_stream(flipper_format)));
// key exist test
position_before = stream_tell(flipper_format_get_raw_stream(flipper_format));
mu_check(flipper_format_key_exist(flipper_format, test_hex_key));
mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
mu_check(!flipper_format_key_exist(flipper_format, "invalid key"));
mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
// rewind
mu_check(flipper_format_rewind(flipper_format));
// key exist test
position_before = stream_tell(flipper_format_get_raw_stream(flipper_format));
mu_check(flipper_format_key_exist(flipper_format, test_hex_key));
mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
mu_check(!flipper_format_key_exist(flipper_format, "invalid key"));
mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
// read test
string_init(tmpstr);
mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));
mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr));
mu_assert_int_eq(test_version, version);
mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr));
mu_assert_string_eq(test_string_data, string_get_cstr(tmpstr));
mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count));
mu_assert_int_eq(COUNT_OF(test_int_data), count);
mu_check(flipper_format_read_int32(flipper_format, test_int_key, ARRAY_W_COUNT(int32_data)));
mu_check(memcmp(test_int_data, ARRAY_W_BSIZE(int32_data)) == 0);
mu_check(flipper_format_get_value_count(flipper_format, test_uint_key, &count));
mu_assert_int_eq(COUNT_OF(test_uint_data), count);
mu_check(
flipper_format_read_uint32(flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_data)));
mu_check(memcmp(test_uint_data, ARRAY_W_BSIZE(uint32_data)) == 0);
mu_check(flipper_format_get_value_count(flipper_format, test_float_key, &count));
mu_assert_int_eq(COUNT_OF(test_float_data), count);
mu_check(flipper_format_read_float(flipper_format, test_float_key, ARRAY_W_COUNT(float_data)));
mu_check(memcmp(test_float_data, ARRAY_W_BSIZE(float_data)) == 0);
mu_check(flipper_format_get_value_count(flipper_format, test_hex_key, &count));
mu_assert_int_eq(COUNT_OF(test_hex_data), count);
mu_check(flipper_format_read_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(hex_data)));
mu_check(memcmp(test_hex_data, ARRAY_W_BSIZE(hex_data)) == 0);
mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr));
string_clear(tmpstr);
// update data
mu_check(flipper_format_rewind(flipper_format));
mu_check(flipper_format_update_string_cstr(
flipper_format, test_string_key, test_string_updated_data));
mu_check(flipper_format_update_int32(
flipper_format, test_int_key, ARRAY_W_COUNT(test_int_updated_data)));
mu_check(flipper_format_update_uint32(
flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_updated_data)));
mu_check(flipper_format_update_float(
flipper_format, test_float_key, ARRAY_W_COUNT(test_float_updated_data)));
mu_check(flipper_format_update_hex(
flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_updated_data)));
// read updated data test
uint32_t uint32_updated_data[COUNT_OF(test_uint_updated_data)];
int32_t int32_updated_data[COUNT_OF(test_int_updated_data)];
float float_updated_data[COUNT_OF(test_float_updated_data)];
uint8_t hex_updated_data[COUNT_OF(test_hex_updated_data)];
mu_check(flipper_format_rewind(flipper_format));
string_init(tmpstr);
mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));
mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr));
mu_assert_int_eq(test_version, version);
mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr));
mu_assert_string_eq(test_string_updated_data, string_get_cstr(tmpstr));
mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count));
mu_assert_int_eq(COUNT_OF(test_int_updated_data), count);
mu_check(flipper_format_read_int32(
flipper_format, test_int_key, ARRAY_W_COUNT(int32_updated_data)));
mu_check(memcmp(test_int_updated_data, ARRAY_W_BSIZE(int32_updated_data)) == 0);
mu_check(flipper_format_get_value_count(flipper_format, test_uint_key, &count));
mu_assert_int_eq(COUNT_OF(test_uint_updated_data), count);
mu_check(flipper_format_read_uint32(
flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_updated_data)));
mu_check(memcmp(test_uint_updated_data, ARRAY_W_BSIZE(uint32_updated_data)) == 0);
mu_check(flipper_format_get_value_count(flipper_format, test_float_key, &count));
mu_assert_int_eq(COUNT_OF(test_float_updated_data), count);
mu_check(flipper_format_read_float(
flipper_format, test_float_key, ARRAY_W_COUNT(float_updated_data)));
mu_check(memcmp(test_float_updated_data, ARRAY_W_BSIZE(float_updated_data)) == 0);
mu_check(flipper_format_get_value_count(flipper_format, test_hex_key, &count));
mu_assert_int_eq(COUNT_OF(test_hex_updated_data), count);
mu_check(
flipper_format_read_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(hex_updated_data)));
mu_check(memcmp(test_hex_updated_data, ARRAY_W_BSIZE(hex_updated_data)) == 0);
mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr));
string_clear(tmpstr);
// update data
mu_check(flipper_format_rewind(flipper_format));
mu_check(flipper_format_insert_or_update_string_cstr(
flipper_format, test_string_key, test_string_updated_2_data));
mu_check(flipper_format_insert_or_update_int32(
flipper_format, test_int_key, ARRAY_W_COUNT(test_int_updated_2_data)));
mu_check(flipper_format_insert_or_update_uint32(
flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_updated_2_data)));
mu_check(flipper_format_insert_or_update_float(
flipper_format, test_float_key, ARRAY_W_COUNT(test_float_updated_2_data)));
mu_check(flipper_format_insert_or_update_hex(
flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_updated_2_data)));
mu_check(flipper_format_insert_or_update_hex(
flipper_format, test_hex_new_key, ARRAY_W_COUNT(test_hex_new_data)));
uint32_t uint32_updated_2_data[COUNT_OF(test_uint_updated_2_data)];
int32_t int32_updated_2_data[COUNT_OF(test_int_updated_2_data)];
float float_updated_2_data[COUNT_OF(test_float_updated_2_data)];
uint8_t hex_updated_2_data[COUNT_OF(test_hex_updated_2_data)];
uint8_t hex_new_data[COUNT_OF(test_hex_new_data)];
mu_check(flipper_format_rewind(flipper_format));
string_init(tmpstr);
mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));
mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr));
mu_assert_int_eq(test_version, version);
mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr));
mu_assert_string_eq(test_string_updated_2_data, string_get_cstr(tmpstr));
mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count));
mu_assert_int_eq(COUNT_OF(test_int_updated_2_data), count);
mu_check(flipper_format_read_int32(
flipper_format, test_int_key, ARRAY_W_COUNT(int32_updated_2_data)));
mu_check(memcmp(test_int_updated_2_data, ARRAY_W_BSIZE(int32_updated_2_data)) == 0);
mu_check(flipper_format_get_value_count(flipper_format, test_uint_key, &count));
mu_assert_int_eq(COUNT_OF(test_uint_updated_2_data), count);
mu_check(flipper_format_read_uint32(
flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_updated_2_data)));
mu_check(memcmp(test_uint_updated_2_data, ARRAY_W_BSIZE(uint32_updated_2_data)) == 0);
mu_check(flipper_format_get_value_count(flipper_format, test_float_key, &count));
mu_assert_int_eq(COUNT_OF(test_float_updated_2_data), count);
mu_check(flipper_format_read_float(
flipper_format, test_float_key, ARRAY_W_COUNT(float_updated_2_data)));
mu_check(memcmp(test_float_updated_2_data, ARRAY_W_BSIZE(float_updated_2_data)) == 0);
mu_check(flipper_format_get_value_count(flipper_format, test_hex_key, &count));
mu_assert_int_eq(COUNT_OF(test_hex_updated_2_data), count);
mu_check(
flipper_format_read_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(hex_updated_2_data)));
mu_check(memcmp(test_hex_updated_2_data, ARRAY_W_BSIZE(hex_updated_2_data)) == 0);
mu_check(flipper_format_get_value_count(flipper_format, test_hex_new_key, &count));
mu_assert_int_eq(COUNT_OF(test_hex_new_data), count);
mu_check(
flipper_format_read_hex(flipper_format, test_hex_new_key, ARRAY_W_COUNT(hex_new_data)));
mu_check(memcmp(test_hex_new_data, ARRAY_W_BSIZE(hex_new_data)) == 0);
mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr));
string_clear(tmpstr);
// delete key test
mu_check(flipper_format_rewind(flipper_format));
mu_check(flipper_format_delete_key(flipper_format, test_uint_key));
// deleted key read test
mu_check(flipper_format_rewind(flipper_format));
mu_check(!flipper_format_read_uint32(
flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_updated_data)));
}
MU_TEST(flipper_format_string_test) {
FlipperFormat* flipper_format = flipper_format_string_alloc();
Stream* stream = flipper_format_get_raw_stream(flipper_format);
mu_check(flipper_format_write_header_cstr(flipper_format, test_filetype, test_version));
mu_check(flipper_format_write_comment_cstr(flipper_format, "This is comment"));
mu_check(flipper_format_write_string_cstr(flipper_format, test_string_key, test_string_data));
mu_check(
flipper_format_write_int32(flipper_format, test_int_key, ARRAY_W_COUNT(test_int_data)));
mu_check(
flipper_format_write_uint32(flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_data)));
mu_check(flipper_format_write_float(
flipper_format, test_float_key, ARRAY_W_COUNT(test_float_data)));
mu_check(flipper_format_write_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_data)));
MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);
stream_clean(stream);
stream_write_cstring(stream, test_data_nix);
MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);
stream_clean(stream);
stream_write_cstring(stream, test_data_win);
MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);
flipper_format_free(flipper_format);
}
MU_TEST(flipper_format_file_test) {
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* flipper_format = flipper_format_file_alloc(storage);
mu_check(flipper_format_file_open_always(flipper_format, EXT_PATH("flipper.fff")));
Stream* stream = flipper_format_get_raw_stream(flipper_format);
mu_check(flipper_format_write_header_cstr(flipper_format, test_filetype, test_version));
mu_check(flipper_format_write_comment_cstr(flipper_format, "This is comment"));
mu_check(flipper_format_write_string_cstr(flipper_format, test_string_key, test_string_data));
mu_check(
flipper_format_write_int32(flipper_format, test_int_key, ARRAY_W_COUNT(test_int_data)));
mu_check(
flipper_format_write_uint32(flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_data)));
mu_check(flipper_format_write_float(
flipper_format, test_float_key, ARRAY_W_COUNT(test_float_data)));
mu_check(flipper_format_write_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_data)));
MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);
stream_clean(stream);
stream_write_cstring(stream, test_data_nix);
MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);
stream_clean(stream);
stream_write_cstring(stream, test_data_win);
MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);
flipper_format_free(flipper_format);
furi_record_close(RECORD_STORAGE);
}
MU_TEST_SUITE(flipper_format_string_suite) {
MU_RUN_TEST(flipper_format_string_test);
MU_RUN_TEST(flipper_format_file_test);
}
int run_minunit_test_flipper_format_string() {
MU_RUN_SUITE(flipper_format_string_suite);
return MU_EXIT_CODE;
}

View File

@@ -0,0 +1,525 @@
#include <furi.h>
#include <flipper_format/flipper_format.h>
#include <flipper_format/flipper_format_i.h>
#include <toolbox/stream/stream.h>
#include "../minunit.h"
#define TEST_DIR TEST_DIR_NAME "/"
#define TEST_DIR_NAME EXT_PATH("unit_tests_tmp")
static const char* test_filetype = "Flipper File test";
static const uint32_t test_version = 666;
static const char* test_string_key = "String data";
static const char* test_string_data = "String";
static const char* test_string_updated_data = "New string";
static const char* test_int_key = "Int32 data";
static const int32_t test_int_data[] = {1234, -6345, 7813, 0};
static const int32_t test_int_updated_data[] = {-1337, 69};
static const char* test_uint_key = "Uint32 data";
static const uint32_t test_uint_data[] = {1234, 0, 5678, 9098, 7654321};
static const uint32_t test_uint_updated_data[] = {8, 800, 555, 35, 35};
static const char* test_float_key = "Float data";
static const float test_float_data[] = {1.5f, 1000.0f};
static const float test_float_updated_data[] = {1.2f};
static const char* test_bool_key = "Bool data";
static const bool test_bool_data[] = {true, false};
static const bool test_bool_updated_data[] = {false, true, true};
static const char* test_hex_key = "Hex data";
static const uint8_t test_hex_data[] = {0xDE, 0xAD, 0xBE};
static const uint8_t test_hex_updated_data[] = {0xFE, 0xCA};
#define READ_TEST_NIX "ff_nix.test"
static const char* test_data_nix = "Filetype: Flipper File test\n"
"Version: 666\n"
"# This is comment\n"
"String data: String\n"
"Int32 data: 1234 -6345 7813 0\n"
"Uint32 data: 1234 0 5678 9098 7654321\n"
"Float data: 1.5 1000.0\n"
"Bool data: true false\n"
"Hex data: DE AD BE";
#define READ_TEST_WIN "ff_win.test"
static const char* test_data_win = "Filetype: Flipper File test\r\n"
"Version: 666\r\n"
"# This is comment\r\n"
"String data: String\r\n"
"Int32 data: 1234 -6345 7813 0\r\n"
"Uint32 data: 1234 0 5678 9098 7654321\r\n"
"Float data: 1.5 1000.0\r\n"
"Bool data: true false\r\n"
"Hex data: DE AD BE";
#define READ_TEST_FLP "ff_flp.test"
// data created by user on linux machine
static const char* test_file_linux = TEST_DIR READ_TEST_NIX;
// data created by user on windows machine
static const char* test_file_windows = TEST_DIR READ_TEST_WIN;
// data created by flipper itself
static const char* test_file_flipper = TEST_DIR READ_TEST_FLP;
static bool storage_write_string(const char* path, const char* data) {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
bool result = false;
do {
if(!storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) break;
if(storage_file_write(file, data, strlen(data)) != strlen(data)) break;
result = true;
} while(false);
storage_file_close(file);
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
return result;
}
static void tests_setup() {
Storage* storage = furi_record_open(RECORD_STORAGE);
mu_assert(storage_simply_remove_recursive(storage, TEST_DIR_NAME), "Cannot clean data");
mu_assert(storage_simply_mkdir(storage, TEST_DIR_NAME), "Cannot create dir");
furi_record_close(RECORD_STORAGE);
}
static void tests_teardown() {
Storage* storage = furi_record_open(RECORD_STORAGE);
mu_assert(storage_simply_remove_recursive(storage, TEST_DIR_NAME), "Cannot clean data");
furi_record_close(RECORD_STORAGE);
}
static bool test_read(const char* file_name) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool result = false;
FlipperFormat* file = flipper_format_file_alloc(storage);
string_t string_value;
string_init(string_value);
uint32_t uint32_value;
void* scratchpad = malloc(512);
do {
if(!flipper_format_file_open_existing(file, file_name)) break;
if(!flipper_format_read_header(file, string_value, &uint32_value)) break;
if(string_cmp_str(string_value, test_filetype) != 0) break;
if(uint32_value != test_version) break;
if(!flipper_format_read_string(file, test_string_key, string_value)) break;
if(string_cmp_str(string_value, test_string_data) != 0) break;
if(!flipper_format_get_value_count(file, test_int_key, &uint32_value)) break;
if(uint32_value != COUNT_OF(test_int_data)) break;
if(!flipper_format_read_int32(file, test_int_key, scratchpad, uint32_value)) break;
if(memcmp(scratchpad, test_int_data, sizeof(int32_t) * COUNT_OF(test_int_data)) != 0)
break;
if(!flipper_format_get_value_count(file, test_uint_key, &uint32_value)) break;
if(uint32_value != COUNT_OF(test_uint_data)) break;
if(!flipper_format_read_uint32(file, test_uint_key, scratchpad, uint32_value)) break;
if(memcmp(scratchpad, test_uint_data, sizeof(uint32_t) * COUNT_OF(test_uint_data)) != 0)
break;
if(!flipper_format_get_value_count(file, test_float_key, &uint32_value)) break;
if(uint32_value != COUNT_OF(test_float_data)) break;
if(!flipper_format_read_float(file, test_float_key, scratchpad, uint32_value)) break;
if(memcmp(scratchpad, test_float_data, sizeof(float) * COUNT_OF(test_float_data)) != 0)
break;
if(!flipper_format_get_value_count(file, test_bool_key, &uint32_value)) break;
if(uint32_value != COUNT_OF(test_bool_data)) break;
if(!flipper_format_read_bool(file, test_bool_key, scratchpad, uint32_value)) break;
if(memcmp(scratchpad, test_bool_data, sizeof(bool) * COUNT_OF(test_bool_data)) != 0) break;
if(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break;
if(uint32_value != COUNT_OF(test_hex_data)) break;
if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) break;
if(memcmp(scratchpad, test_hex_data, sizeof(uint8_t) * COUNT_OF(test_hex_data)) != 0)
break;
result = true;
} while(false);
free(scratchpad);
string_clear(string_value);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
return result;
}
static bool test_read_updated(const char* file_name) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool result = false;
FlipperFormat* file = flipper_format_file_alloc(storage);
string_t string_value;
string_init(string_value);
uint32_t uint32_value;
void* scratchpad = malloc(512);
do {
if(!flipper_format_file_open_existing(file, file_name)) break;
if(!flipper_format_read_header(file, string_value, &uint32_value)) break;
if(string_cmp_str(string_value, test_filetype) != 0) break;
if(uint32_value != test_version) break;
if(!flipper_format_read_string(file, test_string_key, string_value)) break;
if(string_cmp_str(string_value, test_string_updated_data) != 0) break;
if(!flipper_format_get_value_count(file, test_int_key, &uint32_value)) break;
if(uint32_value != COUNT_OF(test_int_updated_data)) break;
if(!flipper_format_read_int32(file, test_int_key, scratchpad, uint32_value)) break;
if(memcmp(
scratchpad,
test_int_updated_data,
sizeof(int32_t) * COUNT_OF(test_int_updated_data)) != 0)
break;
if(!flipper_format_get_value_count(file, test_uint_key, &uint32_value)) break;
if(uint32_value != COUNT_OF(test_uint_updated_data)) break;
if(!flipper_format_read_uint32(file, test_uint_key, scratchpad, uint32_value)) break;
if(memcmp(
scratchpad,
test_uint_updated_data,
sizeof(uint32_t) * COUNT_OF(test_uint_updated_data)) != 0)
break;
if(!flipper_format_get_value_count(file, test_float_key, &uint32_value)) break;
if(uint32_value != COUNT_OF(test_float_updated_data)) break;
if(!flipper_format_read_float(file, test_float_key, scratchpad, uint32_value)) break;
if(memcmp(
scratchpad,
test_float_updated_data,
sizeof(float) * COUNT_OF(test_float_updated_data)) != 0)
break;
if(!flipper_format_get_value_count(file, test_bool_key, &uint32_value)) break;
if(uint32_value != COUNT_OF(test_bool_updated_data)) break;
if(!flipper_format_read_bool(file, test_bool_key, scratchpad, uint32_value)) break;
if(memcmp(
scratchpad,
test_bool_updated_data,
sizeof(bool) * COUNT_OF(test_bool_updated_data)) != 0)
break;
if(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break;
if(uint32_value != COUNT_OF(test_hex_updated_data)) break;
if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) break;
if(memcmp(
scratchpad,
test_hex_updated_data,
sizeof(uint8_t) * COUNT_OF(test_hex_updated_data)) != 0)
break;
result = true;
} while(false);
free(scratchpad);
string_clear(string_value);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
return result;
}
static bool test_write(const char* file_name) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool result = false;
FlipperFormat* file = flipper_format_file_alloc(storage);
do {
if(!flipper_format_file_open_always(file, file_name)) break;
if(!flipper_format_write_header_cstr(file, test_filetype, test_version)) break;
if(!flipper_format_write_comment_cstr(file, "This is comment")) break;
if(!flipper_format_write_string_cstr(file, test_string_key, test_string_data)) break;
if(!flipper_format_write_int32(file, test_int_key, test_int_data, COUNT_OF(test_int_data)))
break;
if(!flipper_format_write_uint32(
file, test_uint_key, test_uint_data, COUNT_OF(test_uint_data)))
break;
if(!flipper_format_write_float(
file, test_float_key, test_float_data, COUNT_OF(test_float_data)))
break;
if(!flipper_format_write_bool(
file, test_bool_key, test_bool_data, COUNT_OF(test_bool_data)))
break;
if(!flipper_format_write_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data)))
break;
result = true;
} while(false);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
return result;
}
static bool test_delete_last_key(const char* file_name) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool result = false;
FlipperFormat* file = flipper_format_file_alloc(storage);
do {
if(!flipper_format_file_open_existing(file, file_name)) break;
if(!flipper_format_delete_key(file, test_hex_key)) break;
result = true;
} while(false);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
return result;
}
static bool test_append_key(const char* file_name) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool result = false;
FlipperFormat* file = flipper_format_file_alloc(storage);
do {
if(!flipper_format_file_open_append(file, file_name)) break;
if(!flipper_format_write_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data)))
break;
result = true;
} while(false);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
return result;
}
static bool test_update(const char* file_name) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool result = false;
FlipperFormat* file = flipper_format_file_alloc(storage);
do {
if(!flipper_format_file_open_existing(file, file_name)) break;
if(!flipper_format_update_string_cstr(file, test_string_key, test_string_updated_data))
break;
if(!flipper_format_update_int32(
file, test_int_key, test_int_updated_data, COUNT_OF(test_int_updated_data)))
break;
if(!flipper_format_update_uint32(
file, test_uint_key, test_uint_updated_data, COUNT_OF(test_uint_updated_data)))
break;
if(!flipper_format_update_float(
file, test_float_key, test_float_updated_data, COUNT_OF(test_float_updated_data)))
break;
if(!flipper_format_update_bool(
file, test_bool_key, test_bool_updated_data, COUNT_OF(test_bool_updated_data)))
break;
if(!flipper_format_update_hex(
file, test_hex_key, test_hex_updated_data, COUNT_OF(test_hex_updated_data)))
break;
result = true;
} while(false);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
return result;
}
static bool test_update_backward(const char* file_name) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool result = false;
FlipperFormat* file = flipper_format_file_alloc(storage);
do {
if(!flipper_format_file_open_existing(file, file_name)) break;
if(!flipper_format_update_string_cstr(file, test_string_key, test_string_data)) break;
if(!flipper_format_update_int32(file, test_int_key, test_int_data, COUNT_OF(test_int_data)))
break;
if(!flipper_format_update_uint32(
file, test_uint_key, test_uint_data, COUNT_OF(test_uint_data)))
break;
if(!flipper_format_update_float(
file, test_float_key, test_float_data, COUNT_OF(test_float_data)))
break;
if(!flipper_format_update_bool(
file, test_bool_key, test_bool_data, COUNT_OF(test_bool_data)))
break;
if(!flipper_format_update_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data)))
break;
result = true;
} while(false);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
return result;
}
static bool test_write_multikey(const char* file_name) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool result = false;
FlipperFormat* file = flipper_format_file_alloc(storage);
do {
if(!flipper_format_file_open_always(file, file_name)) break;
if(!flipper_format_write_header_cstr(file, test_filetype, test_version)) break;
bool error = false;
for(uint8_t index = 0; index < 100; index++) {
if(!flipper_format_write_hex(file, test_hex_key, &index, 1)) {
error = true;
break;
}
}
if(error) break;
result = true;
} while(false);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
return result;
}
static bool test_read_multikey(const char* file_name) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool result = false;
FlipperFormat* file = flipper_format_file_alloc(storage);
string_t string_value;
string_init(string_value);
uint32_t uint32_value;
do {
if(!flipper_format_file_open_existing(file, file_name)) break;
if(!flipper_format_read_header(file, string_value, &uint32_value)) break;
if(string_cmp_str(string_value, test_filetype) != 0) break;
if(uint32_value != test_version) break;
bool error = false;
uint8_t uint8_value;
for(uint8_t index = 0; index < 100; index++) {
if(!flipper_format_read_hex(file, test_hex_key, &uint8_value, 1)) {
error = true;
break;
}
if(uint8_value != index) {
error = true;
break;
}
}
if(error) break;
result = true;
} while(false);
string_clear(string_value);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
return result;
}
MU_TEST(flipper_format_write_test) {
mu_assert(storage_write_string(test_file_linux, test_data_nix), "Write test error [Linux]");
mu_assert(
storage_write_string(test_file_windows, test_data_win), "Write test error [Windows]");
mu_assert(test_write(test_file_flipper), "Write test error [Flipper]");
}
MU_TEST(flipper_format_read_test) {
mu_assert(test_read(test_file_linux), "Read test error [Linux]");
mu_assert(test_read(test_file_windows), "Read test error [Windows]");
mu_assert(test_read(test_file_flipper), "Read test error [Flipper]");
}
MU_TEST(flipper_format_delete_test) {
mu_assert(test_delete_last_key(test_file_linux), "Cannot delete key [Linux]");
mu_assert(test_delete_last_key(test_file_windows), "Cannot delete key [Windows]");
mu_assert(test_delete_last_key(test_file_flipper), "Cannot delete key [Flipper]");
}
MU_TEST(flipper_format_delete_result_test) {
mu_assert(!test_read(test_file_linux), "Key deleted incorrectly [Linux]");
mu_assert(!test_read(test_file_windows), "Key deleted incorrectly [Windows]");
mu_assert(!test_read(test_file_flipper), "Key deleted incorrectly [Flipper]");
}
MU_TEST(flipper_format_append_test) {
mu_assert(test_append_key(test_file_linux), "Cannot append data [Linux]");
mu_assert(test_append_key(test_file_windows), "Cannot append data [Windows]");
mu_assert(test_append_key(test_file_flipper), "Cannot append data [Flipper]");
}
MU_TEST(flipper_format_append_result_test) {
mu_assert(test_read(test_file_linux), "Data appended incorrectly [Linux]");
mu_assert(test_read(test_file_windows), "Data appended incorrectly [Windows]");
mu_assert(test_read(test_file_flipper), "Data appended incorrectly [Flipper]");
}
MU_TEST(flipper_format_update_1_test) {
mu_assert(test_update(test_file_linux), "Cannot update data #1 [Linux]");
mu_assert(test_update(test_file_windows), "Cannot update data #1 [Windows]");
mu_assert(test_update(test_file_flipper), "Cannot update data #1 [Flipper]");
}
MU_TEST(flipper_format_update_1_result_test) {
mu_assert(test_read_updated(test_file_linux), "Data #1 updated incorrectly [Linux]");
mu_assert(test_read_updated(test_file_windows), "Data #1 updated incorrectly [Windows]");
mu_assert(test_read_updated(test_file_flipper), "Data #1 updated incorrectly [Flipper]");
}
MU_TEST(flipper_format_update_2_test) {
mu_assert(test_update_backward(test_file_linux), "Cannot update data #2 [Linux]");
mu_assert(test_update_backward(test_file_windows), "Cannot update data #2 [Windows]");
mu_assert(test_update_backward(test_file_flipper), "Cannot update data #2 [Flipper]");
}
MU_TEST(flipper_format_update_2_result_test) {
mu_assert(test_read(test_file_linux), "Data #2 updated incorrectly [Linux]");
mu_assert(test_read(test_file_windows), "Data #2 updated incorrectly [Windows]");
mu_assert(test_read(test_file_flipper), "Data #2 updated incorrectly [Flipper]");
}
MU_TEST(flipper_format_multikey_test) {
mu_assert(test_write_multikey(TEST_DIR "ff_multiline.test"), "Multikey write test error");
mu_assert(test_read_multikey(TEST_DIR "ff_multiline.test"), "Multikey read test error");
}
MU_TEST_SUITE(flipper_format) {
tests_setup();
MU_RUN_TEST(flipper_format_write_test);
MU_RUN_TEST(flipper_format_read_test);
MU_RUN_TEST(flipper_format_delete_test);
MU_RUN_TEST(flipper_format_delete_result_test);
MU_RUN_TEST(flipper_format_append_test);
MU_RUN_TEST(flipper_format_append_result_test);
MU_RUN_TEST(flipper_format_update_1_test);
MU_RUN_TEST(flipper_format_update_1_result_test);
MU_RUN_TEST(flipper_format_update_2_test);
MU_RUN_TEST(flipper_format_update_2_result_test);
MU_RUN_TEST(flipper_format_multikey_test);
tests_teardown();
}
int run_minunit_test_flipper_format() {
MU_RUN_SUITE(flipper_format);
return MU_EXIT_CODE;
}

View File

@@ -0,0 +1,100 @@
#include "../minunit.h"
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// this test is not accurate, but gives a basic understanding
// that memory management is working fine
// do not include memmgr.h here
// we also test that we are linking against stdlib
extern size_t memmgr_get_free_heap(void);
extern size_t memmgr_get_minimum_free_heap(void);
// current heap managment realization consume:
// X bytes after allocate and 0 bytes after allocate and free,
// where X = sizeof(void*) + sizeof(size_t), look to BlockLink_t
const size_t heap_overhead_max_size = sizeof(void*) + sizeof(size_t);
bool heap_equal(size_t heap_size, size_t heap_size_old) {
// heap borders with overhead
const size_t heap_low = heap_size_old - heap_overhead_max_size;
const size_t heap_high = heap_size_old + heap_overhead_max_size;
// not extact, so we must test it against bigger numbers than "overhead size"
const bool result = ((heap_size >= heap_low) && (heap_size <= heap_high));
// debug allocation info
if(!result) {
printf("\n(hl: %zu) <= (p: %zu) <= (hh: %zu)\n", heap_low, heap_size, heap_high);
}
return result;
}
void test_furi_memmgr() {
size_t heap_size = 0;
size_t heap_size_old = 0;
const int alloc_size = 128;
void* ptr = NULL;
void* original_ptr = NULL;
// do not include furi memmgr.h case
#ifdef FURI_MEMMGR_GUARD
mu_fail("do not link against furi memmgr.h");
#endif
// allocate memory case
heap_size_old = memmgr_get_free_heap();
ptr = malloc(alloc_size);
heap_size = memmgr_get_free_heap();
mu_assert_pointers_not_eq(ptr, NULL);
mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "allocate failed");
// free memory case
heap_size_old = memmgr_get_free_heap();
free(ptr);
ptr = NULL;
heap_size = memmgr_get_free_heap();
mu_assert(heap_equal(heap_size, heap_size_old + alloc_size), "free failed");
// reallocate memory case
// get filled array with some data
original_ptr = malloc(alloc_size);
mu_assert_pointers_not_eq(original_ptr, NULL);
for(int i = 0; i < alloc_size; i++) {
*(unsigned char*)(original_ptr + i) = i;
}
// malloc array and copy data
ptr = malloc(alloc_size);
mu_assert_pointers_not_eq(ptr, NULL);
memcpy(ptr, original_ptr, alloc_size);
// reallocate array
heap_size_old = memmgr_get_free_heap();
ptr = realloc(ptr, alloc_size * 2);
heap_size = memmgr_get_free_heap();
mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "reallocate failed");
mu_assert_int_eq(memcmp(original_ptr, ptr, alloc_size), 0);
free(original_ptr);
free(ptr);
// allocate and zero-initialize array (calloc)
original_ptr = malloc(alloc_size);
mu_assert_pointers_not_eq(original_ptr, NULL);
for(int i = 0; i < alloc_size; i++) {
*(unsigned char*)(original_ptr + i) = 0;
}
heap_size_old = memmgr_get_free_heap();
ptr = calloc(1, alloc_size);
heap_size = memmgr_get_free_heap();
mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "callocate failed");
mu_assert_int_eq(memcmp(original_ptr, ptr, alloc_size), 0);
free(original_ptr);
free(ptr);
}

View File

@@ -0,0 +1,45 @@
#include <stdio.h>
#include <string.h>
#include <furi.h>
#include "../minunit.h"
const uint32_t context_value = 0xdeadbeef;
const uint32_t notify_value_0 = 0x12345678;
const uint32_t notify_value_1 = 0x11223344;
uint32_t pubsub_value = 0;
uint32_t pubsub_context_value = 0;
void test_pubsub_handler(const void* arg, void* ctx) {
pubsub_value = *(uint32_t*)arg;
pubsub_context_value = *(uint32_t*)ctx;
}
void test_furi_pubsub() {
FuriPubSub* test_pubsub = NULL;
FuriPubSubSubscription* test_pubsub_subscription = NULL;
// init pubsub case
test_pubsub = furi_pubsub_alloc();
mu_assert_pointers_not_eq(test_pubsub, NULL);
// subscribe pubsub case
test_pubsub_subscription =
furi_pubsub_subscribe(test_pubsub, test_pubsub_handler, (void*)&context_value);
mu_assert_pointers_not_eq(test_pubsub_subscription, NULL);
/// notify pubsub case
furi_pubsub_publish(test_pubsub, (void*)&notify_value_0);
mu_assert_int_eq(pubsub_value, notify_value_0);
mu_assert_int_eq(pubsub_context_value, context_value);
// unsubscribe pubsub case
furi_pubsub_unsubscribe(test_pubsub, test_pubsub_subscription);
/// notify unsubscribed pubsub case
furi_pubsub_publish(test_pubsub, (void*)&notify_value_1);
mu_assert_int_not_eq(pubsub_value, notify_value_1);
// delete pubsub case
furi_pubsub_free(test_pubsub);
}

View File

@@ -0,0 +1,20 @@
#include <stdio.h>
#include <string.h>
#include <furi.h>
#include "../minunit.h"
void test_furi_create_open() {
// 1. Create record
uint8_t test_data = 0;
furi_record_create("test/holding", (void*)&test_data);
// 2. Open it
void* record = furi_record_open("test/holding");
mu_assert_pointers_eq(record, &test_data);
// 3. Close it
furi_record_close("test/holding");
// 4. Clean up
furi_record_destroy("test/holding");
}

View File

@@ -0,0 +1,63 @@
#include <stdio.h>
#include <furi.h>
#include <furi_hal.h>
#include "../minunit.h"
// v2 tests
void test_furi_create_open();
void test_furi_valuemutex();
void test_furi_concurrent_access();
void test_furi_pubsub();
void test_furi_memmgr();
static int foo = 0;
void test_setup(void) {
foo = 7;
}
void test_teardown(void) {
/* Nothing */
}
MU_TEST(test_check) {
mu_check(foo != 6);
}
// v2 tests
MU_TEST(mu_test_furi_create_open) {
test_furi_create_open();
}
MU_TEST(mu_test_furi_valuemutex) {
test_furi_valuemutex();
}
MU_TEST(mu_test_furi_pubsub) {
test_furi_pubsub();
}
MU_TEST(mu_test_furi_memmgr) {
// this test is not accurate, but gives a basic understanding
// that memory management is working fine
test_furi_memmgr();
}
MU_TEST_SUITE(test_suite) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_check);
// v2 tests
MU_RUN_TEST(mu_test_furi_create_open);
MU_RUN_TEST(mu_test_furi_valuemutex);
MU_RUN_TEST(mu_test_furi_pubsub);
MU_RUN_TEST(mu_test_furi_memmgr);
}
int run_minunit_test_furi() {
MU_RUN_SUITE(test_suite);
return MU_EXIT_CODE;
}

View File

@@ -0,0 +1,41 @@
#include <stdio.h>
#include <string.h>
#include <furi.h>
#include "../minunit.h"
void test_furi_valuemutex() {
const int init_value = 0xdeadbeef;
const int changed_value = 0x12345678;
int value = init_value;
bool result;
ValueMutex valuemutex;
// init mutex case
result = init_mutex(&valuemutex, &value, sizeof(value));
mu_assert(result, "init mutex failed");
// acquire mutex case
int* value_pointer = acquire_mutex(&valuemutex, 100);
mu_assert_pointers_eq(value_pointer, &value);
// second acquire mutex case
int* value_pointer_second = acquire_mutex(&valuemutex, 100);
mu_assert_pointers_eq(value_pointer_second, NULL);
// change value case
*value_pointer = changed_value;
mu_assert_int_eq(value, changed_value);
// release mutex case
result = release_mutex(&valuemutex, &value);
mu_assert(result, "release mutex failed");
// TODO
//acquire mutex blocking case
//write mutex blocking case
//read mutex blocking case
mu_check(delete_mutex(&valuemutex));
}

View File

@@ -0,0 +1,521 @@
#include <furi.h>
#include <flipper_format.h>
#include <infrared.h>
#include <common/infrared_common_i.h>
#include "../minunit.h"
#define IR_TEST_FILES_DIR EXT_PATH("unit_tests/infrared/")
#define IR_TEST_FILE_PREFIX "test_"
#define IR_TEST_FILE_SUFFIX ".irtest"
typedef struct {
InfraredDecoderHandler* decoder_handler;
InfraredEncoderHandler* encoder_handler;
string_t file_path;
FlipperFormat* ff;
} InfraredTest;
static InfraredTest* test;
static void infrared_test_alloc() {
Storage* storage = furi_record_open(RECORD_STORAGE);
test = malloc(sizeof(InfraredTest));
test->decoder_handler = infrared_alloc_decoder();
test->encoder_handler = infrared_alloc_encoder();
test->ff = flipper_format_buffered_file_alloc(storage);
string_init(test->file_path);
}
static void infrared_test_free() {
furi_assert(test);
infrared_free_decoder(test->decoder_handler);
infrared_free_encoder(test->encoder_handler);
flipper_format_free(test->ff);
string_clear(test->file_path);
furi_record_close(RECORD_STORAGE);
free(test);
test = NULL;
}
static bool infrared_test_prepare_file(const char* protocol_name) {
string_t file_type;
string_init(file_type);
bool success = false;
string_printf(
test->file_path,
"%s%s%s%s",
IR_TEST_FILES_DIR,
IR_TEST_FILE_PREFIX,
protocol_name,
IR_TEST_FILE_SUFFIX);
do {
uint32_t format_version;
if(!flipper_format_buffered_file_open_existing(test->ff, string_get_cstr(test->file_path)))
break;
if(!flipper_format_read_header(test->ff, file_type, &format_version)) break;
if(string_cmp_str(file_type, "IR tests file") || format_version != 1) break;
success = true;
} while(false);
string_clear(file_type);
return success;
}
static bool infrared_test_load_raw_signal(
FlipperFormat* ff,
const char* signal_name,
uint32_t** timings,
uint32_t* timings_count) {
string_t buf;
string_init(buf);
bool success = false;
do {
bool is_name_found = false;
for(; !is_name_found && flipper_format_read_string(ff, "name", buf);
is_name_found = !string_cmp_str(buf, signal_name))
;
if(!is_name_found) break;
if(!flipper_format_read_string(ff, "type", buf) || string_cmp_str(buf, "raw")) break;
if(!flipper_format_get_value_count(ff, "data", timings_count)) break;
if(!*timings_count) break;
*timings = malloc(*timings_count * sizeof(uint32_t*));
if(!flipper_format_read_uint32(ff, "data", *timings, *timings_count)) {
free(*timings);
break;
}
success = true;
} while(false);
string_clear(buf);
return success;
}
static bool infrared_test_read_message(FlipperFormat* ff, InfraredMessage* message) {
string_t buf;
string_init(buf);
bool success = false;
do {
if(!flipper_format_read_string(ff, "protocol", buf)) break;
message->protocol = infrared_get_protocol_by_name(string_get_cstr(buf));
if(!infrared_is_protocol_valid(message->protocol)) break;
if(!flipper_format_read_hex(ff, "address", (uint8_t*)&message->address, sizeof(uint32_t)))
break;
if(!flipper_format_read_hex(ff, "command", (uint8_t*)&message->command, sizeof(uint32_t)))
break;
if(!flipper_format_read_bool(ff, "repeat", &message->repeat, 1)) break;
success = true;
} while(false);
string_clear(buf);
return success;
}
static bool infrared_test_load_messages(
FlipperFormat* ff,
const char* signal_name,
InfraredMessage** messages,
uint32_t* messages_count) {
string_t buf;
string_init(buf);
bool success = false;
do {
bool is_name_found = false;
for(; !is_name_found && flipper_format_read_string(ff, "name", buf);
is_name_found = !string_cmp_str(buf, signal_name))
;
if(!is_name_found) break;
if(!flipper_format_read_string(ff, "type", buf) || string_cmp_str(buf, "parsed_array"))
break;
if(!flipper_format_read_uint32(ff, "count", messages_count, 1)) break;
if(!*messages_count) break;
*messages = malloc(*messages_count * sizeof(InfraredMessage));
uint32_t i;
for(i = 0; i < *messages_count; ++i) {
if(!infrared_test_read_message(ff, (*messages) + i)) {
break;
}
}
if(*messages_count != i) {
free(*messages);
break;
}
success = true;
} while(false);
string_clear(buf);
return success;
}
static void infrared_test_compare_message_results(
const InfraredMessage* message_decoded,
const InfraredMessage* message_expected) {
mu_check(message_decoded->protocol == message_expected->protocol);
mu_check(message_decoded->command == message_expected->command);
mu_check(message_decoded->address == message_expected->address);
if((message_expected->protocol == InfraredProtocolSIRC) ||
(message_expected->protocol == InfraredProtocolSIRC15) ||
(message_expected->protocol == InfraredProtocolSIRC20)) {
mu_check(message_decoded->repeat == false);
} else {
mu_check(message_decoded->repeat == message_expected->repeat);
}
}
/* Encodes signal and merges same levels (high+high, low+low) */
static void infrared_test_run_encoder_fill_array(
InfraredEncoderHandler* handler,
uint32_t* timings,
uint32_t* timings_len,
bool* start_level) {
uint32_t duration = 0;
bool level = false;
bool level_read;
InfraredStatus status = InfraredStatusError;
size_t i = 0;
bool first = true;
while(1) {
status = infrared_encode(handler, &duration, &level_read);
if(first) {
if(start_level) *start_level = level_read;
first = false;
timings[0] = 0;
} else if(level_read != level) {
++i;
furi_check(i < *timings_len);
timings[i] = 0;
}
level = level_read;
timings[i] += duration;
furi_check((status == InfraredStatusOk) || (status == InfraredStatusDone));
if(status == InfraredStatusDone) break;
}
*timings_len = i + 1;
}
// messages in input array for encoder should have one protocol
static void infrared_test_run_encoder(InfraredProtocol protocol, uint32_t test_index) {
uint32_t* timings;
uint32_t timings_count = 200;
uint32_t* expected_timings;
uint32_t expected_timings_count;
InfraredMessage* input_messages;
uint32_t input_messages_count;
string_t buf;
string_init(buf);
const char* protocol_name = infrared_get_protocol_name(protocol);
mu_assert(infrared_test_prepare_file(protocol_name), "Failed to prepare test file");
string_printf(buf, "encoder_input%d", test_index);
mu_assert(
infrared_test_load_messages(
test->ff, string_get_cstr(buf), &input_messages, &input_messages_count),
"Failed to load messages from file");
string_printf(buf, "encoder_expected%d", test_index);
mu_assert(
infrared_test_load_raw_signal(
test->ff, string_get_cstr(buf), &expected_timings, &expected_timings_count),
"Failed to load raw signal from file");
flipper_format_buffered_file_close(test->ff);
string_clear(buf);
uint32_t j = 0;
timings = malloc(sizeof(uint32_t) * timings_count);
for(uint32_t message_counter = 0; message_counter < input_messages_count; ++message_counter) {
const InfraredMessage* message = &input_messages[message_counter];
if(!message->repeat) {
infrared_reset_encoder(test->encoder_handler, message);
}
timings_count = 200;
infrared_test_run_encoder_fill_array(test->encoder_handler, timings, &timings_count, NULL);
furi_check(timings_count <= 200);
for(size_t i = 0; i < timings_count; ++i, ++j) {
mu_check(MATCH_TIMING(timings[i], expected_timings[j], 120));
mu_assert(j < expected_timings_count, "encoded more timings than expected");
}
}
free(input_messages);
free(expected_timings);
free(timings);
mu_assert(j == expected_timings_count, "encoded less timings than expected");
}
static void infrared_test_run_encoder_decoder(InfraredProtocol protocol, uint32_t test_index) {
uint32_t* timings = 0;
uint32_t timings_count = 200;
InfraredMessage* input_messages;
uint32_t input_messages_count;
bool level = false;
string_t buf;
string_init(buf);
timings = malloc(sizeof(uint32_t) * timings_count);
const char* protocol_name = infrared_get_protocol_name(protocol);
mu_assert(infrared_test_prepare_file(protocol_name), "Failed to prepare test file");
string_printf(buf, "encoder_decoder_input%d", test_index);
mu_assert(
infrared_test_load_messages(
test->ff, string_get_cstr(buf), &input_messages, &input_messages_count),
"Failed to load messages from file");
flipper_format_buffered_file_close(test->ff);
string_clear(buf);
for(uint32_t message_counter = 0; message_counter < input_messages_count; ++message_counter) {
const InfraredMessage* message_encoded = &input_messages[message_counter];
if(!message_encoded->repeat) {
infrared_reset_encoder(test->encoder_handler, message_encoded);
}
timings_count = 200;
infrared_test_run_encoder_fill_array(
test->encoder_handler, timings, &timings_count, &level);
furi_check(timings_count <= 200);
const InfraredMessage* message_decoded = 0;
for(size_t i = 0; i < timings_count; ++i) {
message_decoded = infrared_decode(test->decoder_handler, level, timings[i]);
if((i == timings_count - 2) && level && message_decoded) {
/* In case we end with space timing - message can be decoded at last mark */
break;
} else if(i < timings_count - 1) {
mu_check(!message_decoded);
} else {
if(!message_decoded) {
message_decoded = infrared_check_decoder_ready(test->decoder_handler);
}
mu_check(message_decoded);
}
level = !level;
}
if(message_decoded) {
infrared_test_compare_message_results(message_decoded, message_encoded);
} else {
mu_check(0);
}
}
free(input_messages);
free(timings);
}
static void infrared_test_run_decoder(InfraredProtocol protocol, uint32_t test_index) {
uint32_t* timings;
uint32_t timings_count;
InfraredMessage* messages;
uint32_t messages_count;
string_t buf;
string_init(buf);
mu_assert(
infrared_test_prepare_file(infrared_get_protocol_name(protocol)),
"Failed to prepare test file");
string_printf(buf, "decoder_input%d", test_index);
mu_assert(
infrared_test_load_raw_signal(test->ff, string_get_cstr(buf), &timings, &timings_count),
"Failed to load raw signal from file");
string_printf(buf, "decoder_expected%d", test_index);
mu_assert(
infrared_test_load_messages(test->ff, string_get_cstr(buf), &messages, &messages_count),
"Failed to load messages from file");
flipper_format_buffered_file_close(test->ff);
string_clear(buf);
InfraredMessage message_decoded_check_local;
bool level = 0;
uint32_t message_counter = 0;
const InfraredMessage* message_decoded = 0;
for(uint32_t i = 0; i < timings_count; ++i) {
const InfraredMessage* message_decoded_check = 0;
if(timings[i] > INFRARED_RAW_RX_TIMING_DELAY_US) {
message_decoded_check = infrared_check_decoder_ready(test->decoder_handler);
if(message_decoded_check) {
/* infrared_decode() can reset message, but we have to call infrared_decode() to perform real
* simulation: infrared_check() by timeout, then infrared_decode() when meet edge */
message_decoded_check_local = *message_decoded_check;
message_decoded_check = &message_decoded_check_local;
}
}
message_decoded = infrared_decode(test->decoder_handler, level, timings[i]);
if(message_decoded_check || message_decoded) {
mu_assert(
!(message_decoded_check && message_decoded),
"both messages decoded: check_ready() and infrared_decode()");
if(message_decoded_check) {
message_decoded = message_decoded_check;
}
mu_assert(message_counter < messages_count, "decoded more than expected");
infrared_test_compare_message_results(message_decoded, &messages[message_counter]);
++message_counter;
}
level = !level;
}
message_decoded = infrared_check_decoder_ready(test->decoder_handler);
if(message_decoded) {
infrared_test_compare_message_results(message_decoded, &messages[message_counter]);
++message_counter;
}
free(timings);
free(messages);
mu_assert(message_counter == messages_count, "decoded less than expected");
}
MU_TEST(infrared_test_decoder_samsung32) {
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
}
MU_TEST(infrared_test_decoder_mixed) {
infrared_test_run_decoder(InfraredProtocolRC5, 2);
infrared_test_run_decoder(InfraredProtocolSIRC, 1);
infrared_test_run_decoder(InfraredProtocolNECext, 1);
infrared_test_run_decoder(InfraredProtocolRC6, 2);
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
infrared_test_run_decoder(InfraredProtocolRC6, 1);
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
infrared_test_run_decoder(InfraredProtocolRC5, 1);
infrared_test_run_decoder(InfraredProtocolSIRC, 2);
infrared_test_run_decoder(InfraredProtocolNECext, 1);
infrared_test_run_decoder(InfraredProtocolSIRC, 4);
infrared_test_run_decoder(InfraredProtocolNEC, 2);
infrared_test_run_decoder(InfraredProtocolRC6, 1);
infrared_test_run_decoder(InfraredProtocolNECext, 1);
infrared_test_run_decoder(InfraredProtocolSIRC, 5);
infrared_test_run_decoder(InfraredProtocolNEC, 3);
infrared_test_run_decoder(InfraredProtocolRC5, 5);
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
infrared_test_run_decoder(InfraredProtocolSIRC, 3);
}
MU_TEST(infrared_test_decoder_nec) {
infrared_test_run_decoder(InfraredProtocolNEC, 1);
infrared_test_run_decoder(InfraredProtocolNEC, 2);
infrared_test_run_decoder(InfraredProtocolNEC, 3);
}
MU_TEST(infrared_test_decoder_unexpected_end_in_sequence) {
infrared_test_run_decoder(InfraredProtocolNEC, 1);
infrared_test_run_decoder(InfraredProtocolNEC, 1);
infrared_test_run_decoder(InfraredProtocolNEC, 2);
infrared_test_run_decoder(InfraredProtocolNEC, 2);
}
MU_TEST(infrared_test_decoder_necext1) {
infrared_test_run_decoder(InfraredProtocolNECext, 1);
infrared_test_run_decoder(InfraredProtocolNECext, 1);
}
MU_TEST(infrared_test_decoder_long_packets_with_nec_start) {
infrared_test_run_decoder(InfraredProtocolNEC42ext, 1);
infrared_test_run_decoder(InfraredProtocolNEC42ext, 2);
}
MU_TEST(infrared_test_encoder_sirc) {
infrared_test_run_encoder(InfraredProtocolSIRC, 1);
infrared_test_run_encoder(InfraredProtocolSIRC, 2);
}
MU_TEST(infrared_test_decoder_sirc) {
infrared_test_run_decoder(InfraredProtocolSIRC, 3);
infrared_test_run_decoder(InfraredProtocolSIRC, 1);
infrared_test_run_decoder(InfraredProtocolSIRC, 2);
infrared_test_run_decoder(InfraredProtocolSIRC, 4);
infrared_test_run_decoder(InfraredProtocolSIRC, 5);
}
MU_TEST(infrared_test_decoder_rc5) {
infrared_test_run_decoder(InfraredProtocolRC5X, 1);
infrared_test_run_decoder(InfraredProtocolRC5, 1);
infrared_test_run_decoder(InfraredProtocolRC5, 2);
infrared_test_run_decoder(InfraredProtocolRC5, 3);
infrared_test_run_decoder(InfraredProtocolRC5, 4);
infrared_test_run_decoder(InfraredProtocolRC5, 5);
infrared_test_run_decoder(InfraredProtocolRC5, 6);
infrared_test_run_decoder(InfraredProtocolRC5, 7);
}
MU_TEST(infrared_test_encoder_rc5x) {
infrared_test_run_encoder(InfraredProtocolRC5X, 1);
}
MU_TEST(infrared_test_encoder_rc5) {
infrared_test_run_encoder(InfraredProtocolRC5, 1);
}
MU_TEST(infrared_test_decoder_rc6) {
infrared_test_run_decoder(InfraredProtocolRC6, 1);
}
MU_TEST(infrared_test_encoder_rc6) {
infrared_test_run_encoder(InfraredProtocolRC6, 1);
}
MU_TEST(infrared_test_encoder_decoder_all) {
infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1);
infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1);
infrared_test_run_encoder_decoder(InfraredProtocolNEC42, 1);
infrared_test_run_encoder_decoder(InfraredProtocolNEC42ext, 1);
infrared_test_run_encoder_decoder(InfraredProtocolSamsung32, 1);
infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1);
infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1);
infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1);
}
MU_TEST_SUITE(infrared_test) {
MU_SUITE_CONFIGURE(&infrared_test_alloc, &infrared_test_free);
MU_RUN_TEST(infrared_test_encoder_sirc);
MU_RUN_TEST(infrared_test_decoder_sirc);
MU_RUN_TEST(infrared_test_encoder_rc5x);
MU_RUN_TEST(infrared_test_encoder_rc5);
MU_RUN_TEST(infrared_test_decoder_rc5);
MU_RUN_TEST(infrared_test_decoder_rc6);
MU_RUN_TEST(infrared_test_encoder_rc6);
MU_RUN_TEST(infrared_test_decoder_unexpected_end_in_sequence);
MU_RUN_TEST(infrared_test_decoder_long_packets_with_nec_start);
MU_RUN_TEST(infrared_test_decoder_nec);
MU_RUN_TEST(infrared_test_decoder_samsung32);
MU_RUN_TEST(infrared_test_decoder_necext1);
MU_RUN_TEST(infrared_test_decoder_mixed);
MU_RUN_TEST(infrared_test_encoder_decoder_all);
}
int run_minunit_test_infrared() {
MU_RUN_SUITE(infrared_test);
return MU_EXIT_CODE;
}

View File

@@ -0,0 +1,473 @@
#include <furi.h>
#include "../minunit.h"
#include <lfrfid/tools/bit_lib.h>
MU_TEST(test_bit_lib_increment_index) {
uint32_t index = 0;
// test increment
for(uint32_t i = 0; i < 31; ++i) {
bit_lib_increment_index(index, 32);
mu_assert_int_eq(i + 1, index);
}
// test wrap around
for(uint32_t i = 0; i < 512; ++i) {
bit_lib_increment_index(index, 32);
mu_assert_int_less_than(32, index);
}
}
MU_TEST(test_bit_lib_is_set) {
uint32_t value = 0x0000FFFF;
for(uint32_t i = 0; i < 16; ++i) {
mu_check(bit_lib_bit_is_set(value, i));
mu_check(!bit_lib_bit_is_not_set(value, i));
}
for(uint32_t i = 16; i < 32; ++i) {
mu_check(!bit_lib_bit_is_set(value, i));
mu_check(bit_lib_bit_is_not_set(value, i));
}
}
MU_TEST(test_bit_lib_push) {
#define TEST_BIT_LIB_PUSH_DATA_SIZE 4
uint8_t data[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0};
uint8_t expected_data_1[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x0F, 0xFF};
uint8_t expected_data_2[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0xFF, 0xF0, 0x00};
uint8_t expected_data_3[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0x00, 0x00, 0xFF};
uint8_t expected_data_4[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF};
uint8_t expected_data_5[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x00, 0x00};
uint8_t expected_data_6[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xCC, 0xCC, 0xCC, 0xCC};
for(uint32_t i = 0; i < 12; ++i) {
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
}
mu_assert_mem_eq(expected_data_1, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
for(uint32_t i = 0; i < 12; ++i) {
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
}
mu_assert_mem_eq(expected_data_2, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
for(uint32_t i = 0; i < 4; ++i) {
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
}
for(uint32_t i = 0; i < 8; ++i) {
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
}
mu_assert_mem_eq(expected_data_3, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) {
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
}
mu_assert_mem_eq(expected_data_4, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) {
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
}
mu_assert_mem_eq(expected_data_5, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 2; ++i) {
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
}
mu_assert_mem_eq(expected_data_6, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
}
MU_TEST(test_bit_lib_set_bit) {
uint8_t value[2] = {0x00, 0xFF};
bit_lib_set_bit(value, 15, false);
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFE}), 2);
bit_lib_set_bit(value, 14, false);
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFC}), 2);
bit_lib_set_bit(value, 13, false);
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF8}), 2);
bit_lib_set_bit(value, 12, false);
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF0}), 2);
bit_lib_set_bit(value, 11, false);
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xE0}), 2);
bit_lib_set_bit(value, 10, false);
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xC0}), 2);
bit_lib_set_bit(value, 9, false);
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x80}), 2);
bit_lib_set_bit(value, 8, false);
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x00}), 2);
bit_lib_set_bit(value, 7, true);
mu_assert_mem_eq(value, ((uint8_t[]){0x01, 0x00}), 2);
bit_lib_set_bit(value, 6, true);
mu_assert_mem_eq(value, ((uint8_t[]){0x03, 0x00}), 2);
bit_lib_set_bit(value, 5, true);
mu_assert_mem_eq(value, ((uint8_t[]){0x07, 0x00}), 2);
bit_lib_set_bit(value, 4, true);
mu_assert_mem_eq(value, ((uint8_t[]){0x0F, 0x00}), 2);
bit_lib_set_bit(value, 3, true);
mu_assert_mem_eq(value, ((uint8_t[]){0x1F, 0x00}), 2);
bit_lib_set_bit(value, 2, true);
mu_assert_mem_eq(value, ((uint8_t[]){0x3F, 0x00}), 2);
bit_lib_set_bit(value, 1, true);
mu_assert_mem_eq(value, ((uint8_t[]){0x7F, 0x00}), 2);
bit_lib_set_bit(value, 0, true);
mu_assert_mem_eq(value, ((uint8_t[]){0xFF, 0x00}), 2);
}
MU_TEST(test_bit_lib_set_bits) {
uint8_t value[2] = {0b00000000, 0b11111111};
// set 4 bits to 0b0100 from 12 index
bit_lib_set_bits(value, 12, 0b0100, 4);
// [0100]
mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11110100}), 2);
// set 2 bits to 0b11 from 11 index
bit_lib_set_bits(value, 11, 0b11, 2);
// [11]
mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11111100}), 2);
// set 3 bits to 0b111 from 0 index
bit_lib_set_bits(value, 0, 0b111, 3);
// [111]
mu_assert_mem_eq(value, ((uint8_t[]){0b11100000, 0b11111100}), 2);
// set 8 bits to 0b11111000 from 3 index
bit_lib_set_bits(value, 3, 0b11111000, 8);
// [11111 000]
mu_assert_mem_eq(value, ((uint8_t[]){0b11111111, 0b00011100}), 2);
}
MU_TEST(test_bit_lib_get_bit) {
uint8_t value[2] = {0b00000000, 0b11111111};
for(uint32_t i = 0; i < 8; ++i) {
mu_check(bit_lib_get_bit(value, i) == false);
}
for(uint32_t i = 8; i < 16; ++i) {
mu_check(bit_lib_get_bit(value, i) == true);
}
}
MU_TEST(test_bit_lib_get_bits) {
uint8_t value[2] = {0b00000000, 0b11111111};
mu_assert_int_eq(0b00000000, bit_lib_get_bits(value, 0, 8));
mu_assert_int_eq(0b00000001, bit_lib_get_bits(value, 1, 8));
mu_assert_int_eq(0b00000011, bit_lib_get_bits(value, 2, 8));
mu_assert_int_eq(0b00000111, bit_lib_get_bits(value, 3, 8));
mu_assert_int_eq(0b00001111, bit_lib_get_bits(value, 4, 8));
mu_assert_int_eq(0b00011111, bit_lib_get_bits(value, 5, 8));
mu_assert_int_eq(0b00111111, bit_lib_get_bits(value, 6, 8));
mu_assert_int_eq(0b01111111, bit_lib_get_bits(value, 7, 8));
mu_assert_int_eq(0b11111111, bit_lib_get_bits(value, 8, 8));
}
MU_TEST(test_bit_lib_get_bits_16) {
uint8_t value[2] = {0b00001001, 0b10110001};
mu_assert_int_eq(0b0, bit_lib_get_bits_16(value, 0, 1));
mu_assert_int_eq(0b00, bit_lib_get_bits_16(value, 0, 2));
mu_assert_int_eq(0b000, bit_lib_get_bits_16(value, 0, 3));
mu_assert_int_eq(0b0000, bit_lib_get_bits_16(value, 0, 4));
mu_assert_int_eq(0b00001, bit_lib_get_bits_16(value, 0, 5));
mu_assert_int_eq(0b000010, bit_lib_get_bits_16(value, 0, 6));
mu_assert_int_eq(0b0000100, bit_lib_get_bits_16(value, 0, 7));
mu_assert_int_eq(0b00001001, bit_lib_get_bits_16(value, 0, 8));
mu_assert_int_eq(0b000010011, bit_lib_get_bits_16(value, 0, 9));
mu_assert_int_eq(0b0000100110, bit_lib_get_bits_16(value, 0, 10));
mu_assert_int_eq(0b00001001101, bit_lib_get_bits_16(value, 0, 11));
mu_assert_int_eq(0b000010011011, bit_lib_get_bits_16(value, 0, 12));
mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_16(value, 0, 13));
mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_16(value, 0, 14));
mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_16(value, 0, 15));
mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_16(value, 0, 16));
}
MU_TEST(test_bit_lib_get_bits_32) {
uint8_t value[4] = {0b00001001, 0b10110001, 0b10001100, 0b01100010};
mu_assert_int_eq(0b0, bit_lib_get_bits_32(value, 0, 1));
mu_assert_int_eq(0b00, bit_lib_get_bits_32(value, 0, 2));
mu_assert_int_eq(0b000, bit_lib_get_bits_32(value, 0, 3));
mu_assert_int_eq(0b0000, bit_lib_get_bits_32(value, 0, 4));
mu_assert_int_eq(0b00001, bit_lib_get_bits_32(value, 0, 5));
mu_assert_int_eq(0b000010, bit_lib_get_bits_32(value, 0, 6));
mu_assert_int_eq(0b0000100, bit_lib_get_bits_32(value, 0, 7));
mu_assert_int_eq(0b00001001, bit_lib_get_bits_32(value, 0, 8));
mu_assert_int_eq(0b000010011, bit_lib_get_bits_32(value, 0, 9));
mu_assert_int_eq(0b0000100110, bit_lib_get_bits_32(value, 0, 10));
mu_assert_int_eq(0b00001001101, bit_lib_get_bits_32(value, 0, 11));
mu_assert_int_eq(0b000010011011, bit_lib_get_bits_32(value, 0, 12));
mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_32(value, 0, 13));
mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_32(value, 0, 14));
mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_32(value, 0, 15));
mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_32(value, 0, 16));
mu_assert_int_eq(0b00001001101100011, bit_lib_get_bits_32(value, 0, 17));
mu_assert_int_eq(0b000010011011000110, bit_lib_get_bits_32(value, 0, 18));
mu_assert_int_eq(0b0000100110110001100, bit_lib_get_bits_32(value, 0, 19));
mu_assert_int_eq(0b00001001101100011000, bit_lib_get_bits_32(value, 0, 20));
mu_assert_int_eq(0b000010011011000110001, bit_lib_get_bits_32(value, 0, 21));
mu_assert_int_eq(0b0000100110110001100011, bit_lib_get_bits_32(value, 0, 22));
mu_assert_int_eq(0b00001001101100011000110, bit_lib_get_bits_32(value, 0, 23));
mu_assert_int_eq(0b000010011011000110001100, bit_lib_get_bits_32(value, 0, 24));
mu_assert_int_eq(0b0000100110110001100011000, bit_lib_get_bits_32(value, 0, 25));
mu_assert_int_eq(0b00001001101100011000110001, bit_lib_get_bits_32(value, 0, 26));
mu_assert_int_eq(0b000010011011000110001100011, bit_lib_get_bits_32(value, 0, 27));
mu_assert_int_eq(0b0000100110110001100011000110, bit_lib_get_bits_32(value, 0, 28));
mu_assert_int_eq(0b00001001101100011000110001100, bit_lib_get_bits_32(value, 0, 29));
mu_assert_int_eq(0b000010011011000110001100011000, bit_lib_get_bits_32(value, 0, 30));
mu_assert_int_eq(0b0000100110110001100011000110001, bit_lib_get_bits_32(value, 0, 31));
mu_assert_int_eq(0b00001001101100011000110001100010, bit_lib_get_bits_32(value, 0, 32));
}
MU_TEST(test_bit_lib_test_parity_u32) {
// test even parity
mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityEven), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityEven), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityEven), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityEven), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityEven), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityEven), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityEven), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityEven), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityEven), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityEven), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityEven), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityEven), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityEven), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityEven), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityEven), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityEven), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityEven), 1);
// test odd parity
mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityOdd), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityOdd), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityOdd), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityOdd), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityOdd), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityOdd), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityOdd), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityOdd), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityOdd), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityOdd), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityOdd), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityOdd), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityOdd), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityOdd), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityOdd), 0);
mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityOdd), 1);
mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityOdd), 0);
}
MU_TEST(test_bit_lib_test_parity) {
// next data contains valid parity for 1-3 nibble and invalid for 4 nibble
uint8_t data_always_0_parity[2] = {0b11101110, 0b11101111};
uint8_t data_always_1_parity[2] = {0b00010001, 0b00010000};
uint8_t data_always_odd_parity[2] = {0b00000011, 0b11110111};
uint8_t data_always_even_parity[2] = {0b00010111, 0b10110011};
// test alawys 0 parity
mu_check(bit_lib_test_parity(data_always_0_parity, 0, 12, BitLibParityAlways0, 4));
mu_check(bit_lib_test_parity(data_always_0_parity, 4, 8, BitLibParityAlways0, 4));
mu_check(bit_lib_test_parity(data_always_0_parity, 8, 4, BitLibParityAlways0, 4));
mu_check(bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways0, 4));
mu_check(!bit_lib_test_parity(data_always_0_parity, 0, 16, BitLibParityAlways0, 4));
mu_check(!bit_lib_test_parity(data_always_0_parity, 4, 12, BitLibParityAlways0, 4));
mu_check(!bit_lib_test_parity(data_always_0_parity, 8, 8, BitLibParityAlways0, 4));
mu_check(!bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways0, 4));
// test alawys 1 parity
mu_check(bit_lib_test_parity(data_always_1_parity, 0, 12, BitLibParityAlways1, 4));
mu_check(bit_lib_test_parity(data_always_1_parity, 4, 8, BitLibParityAlways1, 4));
mu_check(bit_lib_test_parity(data_always_1_parity, 8, 4, BitLibParityAlways1, 4));
mu_check(bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways1, 4));
mu_check(!bit_lib_test_parity(data_always_1_parity, 0, 16, BitLibParityAlways1, 4));
mu_check(!bit_lib_test_parity(data_always_1_parity, 4, 12, BitLibParityAlways1, 4));
mu_check(!bit_lib_test_parity(data_always_1_parity, 8, 8, BitLibParityAlways1, 4));
mu_check(!bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways1, 4));
// test odd parity
mu_check(bit_lib_test_parity(data_always_odd_parity, 0, 12, BitLibParityOdd, 4));
mu_check(bit_lib_test_parity(data_always_odd_parity, 4, 8, BitLibParityOdd, 4));
mu_check(bit_lib_test_parity(data_always_odd_parity, 8, 4, BitLibParityOdd, 4));
mu_check(bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityOdd, 4));
mu_check(!bit_lib_test_parity(data_always_odd_parity, 0, 16, BitLibParityOdd, 4));
mu_check(!bit_lib_test_parity(data_always_odd_parity, 4, 12, BitLibParityOdd, 4));
mu_check(!bit_lib_test_parity(data_always_odd_parity, 8, 8, BitLibParityOdd, 4));
mu_check(!bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityOdd, 4));
// test even parity
mu_check(bit_lib_test_parity(data_always_even_parity, 0, 12, BitLibParityEven, 4));
mu_check(bit_lib_test_parity(data_always_even_parity, 4, 8, BitLibParityEven, 4));
mu_check(bit_lib_test_parity(data_always_even_parity, 8, 4, BitLibParityEven, 4));
mu_check(bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityEven, 4));
mu_check(!bit_lib_test_parity(data_always_even_parity, 0, 16, BitLibParityEven, 4));
mu_check(!bit_lib_test_parity(data_always_even_parity, 4, 12, BitLibParityEven, 4));
mu_check(!bit_lib_test_parity(data_always_even_parity, 8, 8, BitLibParityEven, 4));
mu_check(!bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityEven, 4));
}
MU_TEST(test_bit_lib_remove_bit_every_nth) {
// TODO: more tests
uint8_t data_i[1] = {0b00001111};
uint8_t data_o[1] = {0b00011111};
size_t length;
length = bit_lib_remove_bit_every_nth(data_i, 0, 8, 3);
mu_assert_int_eq(6, length);
mu_assert_mem_eq(data_o, data_i, 1);
}
MU_TEST(test_bit_lib_reverse_bits) {
uint8_t data_1_i[2] = {0b11001010, 0b00011111};
uint8_t data_1_o[2] = {0b11111000, 0b01010011};
// reverse bits [0..15]
bit_lib_reverse_bits(data_1_i, 0, 16);
mu_assert_mem_eq(data_1_o, data_1_i, 2);
uint8_t data_2_i[2] = {0b11001010, 0b00011111};
uint8_t data_2_o[2] = {0b11001000, 0b01011111};
// reverse bits [4..11]
bit_lib_reverse_bits(data_2_i, 4, 8);
mu_assert_mem_eq(data_2_o, data_2_i, 2);
}
MU_TEST(test_bit_lib_copy_bits) {
uint8_t data_1_i[2] = {0b11001010, 0b00011111};
uint8_t data_1_o[2] = {0};
// data_1_o[0..15] = data_1_i[0..15]
bit_lib_copy_bits(data_1_o, 0, 16, data_1_i, 0);
mu_assert_mem_eq(data_1_i, data_1_o, 2);
memset(data_1_o, 0, 2);
// data_1_o[4..11] = data_1_i[0..7]
bit_lib_copy_bits(data_1_o, 4, 8, data_1_i, 0);
mu_assert_mem_eq(((uint8_t[]){0b00001100, 0b10100000}), data_1_o, 2);
}
MU_TEST(test_bit_lib_get_bit_count) {
mu_assert_int_eq(0, bit_lib_get_bit_count(0));
mu_assert_int_eq(1, bit_lib_get_bit_count(0b1));
mu_assert_int_eq(1, bit_lib_get_bit_count(0b10));
mu_assert_int_eq(2, bit_lib_get_bit_count(0b11));
mu_assert_int_eq(4, bit_lib_get_bit_count(0b11000011));
mu_assert_int_eq(6, bit_lib_get_bit_count(0b11000011000011));
mu_assert_int_eq(8, bit_lib_get_bit_count(0b11111111));
mu_assert_int_eq(16, bit_lib_get_bit_count(0b11111110000000000000000111111111));
mu_assert_int_eq(32, bit_lib_get_bit_count(0b11111111111111111111111111111111));
}
MU_TEST(test_bit_lib_reverse_16_fast) {
mu_assert_int_eq(0b0000000000000000, bit_lib_reverse_16_fast(0b0000000000000000));
mu_assert_int_eq(0b1000000000000000, bit_lib_reverse_16_fast(0b0000000000000001));
mu_assert_int_eq(0b1100000000000000, bit_lib_reverse_16_fast(0b0000000000000011));
mu_assert_int_eq(0b0000100000001001, bit_lib_reverse_16_fast(0b1001000000010000));
}
MU_TEST(test_bit_lib_crc16) {
uint8_t data[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};
uint8_t data_size = 9;
// Algorithm
// Check Poly Init RefIn RefOut XorOut
// CRC-16/CCITT-FALSE
// 0x29B1 0x1021 0xFFFF false false 0x0000
mu_assert_int_eq(0x29B1, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0x0000));
// CRC-16/ARC
// 0xBB3D 0x8005 0x0000 true true 0x0000
mu_assert_int_eq(0xBB3D, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0x0000));
// CRC-16/AUG-CCITT
// 0xE5CC 0x1021 0x1D0F false false 0x0000
mu_assert_int_eq(0xE5CC, bit_lib_crc16(data, data_size, 0x1021, 0x1D0F, false, false, 0x0000));
// CRC-16/BUYPASS
// 0xFEE8 0x8005 0x0000 false false 0x0000
mu_assert_int_eq(0xFEE8, bit_lib_crc16(data, data_size, 0x8005, 0x0000, false, false, 0x0000));
// CRC-16/CDMA2000
// 0x4C06 0xC867 0xFFFF false false 0x0000
mu_assert_int_eq(0x4C06, bit_lib_crc16(data, data_size, 0xC867, 0xFFFF, false, false, 0x0000));
// CRC-16/DDS-110
// 0x9ECF 0x8005 0x800D false false 0x0000
mu_assert_int_eq(0x9ECF, bit_lib_crc16(data, data_size, 0x8005, 0x800D, false, false, 0x0000));
// CRC-16/DECT-R
// 0x007E 0x0589 0x0000 false false 0x0001
mu_assert_int_eq(0x007E, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0001));
// CRC-16/DECT-X
// 0x007F 0x0589 0x0000 false false 0x0000
mu_assert_int_eq(0x007F, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0000));
// CRC-16/DNP
// 0xEA82 0x3D65 0x0000 true true 0xFFFF
mu_assert_int_eq(0xEA82, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, true, true, 0xFFFF));
// CRC-16/EN-13757
// 0xC2B7 0x3D65 0x0000 false false 0xFFFF
mu_assert_int_eq(0xC2B7, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, false, false, 0xFFFF));
// CRC-16/GENIBUS
// 0xD64E 0x1021 0xFFFF false false 0xFFFF
mu_assert_int_eq(0xD64E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0xFFFF));
// CRC-16/MAXIM
// 0x44C2 0x8005 0x0000 true true 0xFFFF
mu_assert_int_eq(0x44C2, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0xFFFF));
// CRC-16/MCRF4XX
// 0x6F91 0x1021 0xFFFF true true 0x0000
mu_assert_int_eq(0x6F91, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0x0000));
// CRC-16/RIELLO
// 0x63D0 0x1021 0xB2AA true true 0x0000
mu_assert_int_eq(0x63D0, bit_lib_crc16(data, data_size, 0x1021, 0xB2AA, true, true, 0x0000));
// CRC-16/T10-DIF
// 0xD0DB 0x8BB7 0x0000 false false 0x0000
mu_assert_int_eq(0xD0DB, bit_lib_crc16(data, data_size, 0x8BB7, 0x0000, false, false, 0x0000));
// CRC-16/TELEDISK
// 0x0FB3 0xA097 0x0000 false false 0x0000
mu_assert_int_eq(0x0FB3, bit_lib_crc16(data, data_size, 0xA097, 0x0000, false, false, 0x0000));
// CRC-16/TMS37157
// 0x26B1 0x1021 0x89EC true true 0x0000
mu_assert_int_eq(0x26B1, bit_lib_crc16(data, data_size, 0x1021, 0x89EC, true, true, 0x0000));
// CRC-16/USB
// 0xB4C8 0x8005 0xFFFF true true 0xFFFF
mu_assert_int_eq(0xB4C8, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0xFFFF));
// CRC-A
// 0xBF05 0x1021 0xC6C6 true true 0x0000
mu_assert_int_eq(0xBF05, bit_lib_crc16(data, data_size, 0x1021, 0xC6C6, true, true, 0x0000));
// CRC-16/KERMIT
// 0x2189 0x1021 0x0000 true true 0x0000
mu_assert_int_eq(0x2189, bit_lib_crc16(data, data_size, 0x1021, 0x0000, true, true, 0x0000));
// CRC-16/MODBUS
// 0x4B37 0x8005 0xFFFF true true 0x0000
mu_assert_int_eq(0x4B37, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0x0000));
// CRC-16/X-25
// 0x906E 0x1021 0xFFFF true true 0xFFFF
mu_assert_int_eq(0x906E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0xFFFF));
// CRC-16/XMODEM
// 0x31C3 0x1021 0x0000 false false 0x0000
mu_assert_int_eq(0x31C3, bit_lib_crc16(data, data_size, 0x1021, 0x0000, false, false, 0x0000));
}
MU_TEST_SUITE(test_bit_lib) {
MU_RUN_TEST(test_bit_lib_increment_index);
MU_RUN_TEST(test_bit_lib_is_set);
MU_RUN_TEST(test_bit_lib_push);
MU_RUN_TEST(test_bit_lib_set_bit);
MU_RUN_TEST(test_bit_lib_set_bits);
MU_RUN_TEST(test_bit_lib_get_bit);
MU_RUN_TEST(test_bit_lib_get_bits);
MU_RUN_TEST(test_bit_lib_get_bits_16);
MU_RUN_TEST(test_bit_lib_get_bits_32);
MU_RUN_TEST(test_bit_lib_test_parity_u32);
MU_RUN_TEST(test_bit_lib_test_parity);
MU_RUN_TEST(test_bit_lib_remove_bit_every_nth);
MU_RUN_TEST(test_bit_lib_copy_bits);
MU_RUN_TEST(test_bit_lib_reverse_bits);
MU_RUN_TEST(test_bit_lib_get_bit_count);
MU_RUN_TEST(test_bit_lib_reverse_16_fast);
MU_RUN_TEST(test_bit_lib_crc16);
}
int run_minunit_test_bit_lib() {
MU_RUN_SUITE(test_bit_lib);
return MU_EXIT_CODE;
}

View File

@@ -0,0 +1,464 @@
#include <furi.h>
#include "../minunit.h"
#include <toolbox/protocols/protocol_dict.h>
#include <lfrfid/protocols/lfrfid_protocols.h>
#include <toolbox/pulse_protocols/pulse_glue.h>
#define LF_RFID_READ_TIMING_MULTIPLIER 8
#define EM_TEST_DATA \
{ 0x58, 0x00, 0x85, 0x64, 0x02 }
#define EM_TEST_DATA_SIZE 5
#define EM_TEST_EMULATION_TIMINGS_COUNT (64 * 2)
const int8_t em_test_timings[EM_TEST_EMULATION_TIMINGS_COUNT] = {
32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, -32,
32, 32, -32, -32, 32, 32, -32, -32, 32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32,
-32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32,
32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32, -32, 32, 32, -32, -32, 32, 32, -32,
-32, 32, -32, 32, 32, -32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32, -32, 32, -32,
32, 32, -32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, 32, -32,
-32, 32, 32, -32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32,
};
#define HID10301_TEST_DATA \
{ 0x8D, 0x48, 0xA8 }
#define HID10301_TEST_DATA_SIZE 3
#define HID10301_TEST_EMULATION_TIMINGS_COUNT (541 * 2)
const int8_t hid10301_test_timings[HID10301_TEST_EMULATION_TIMINGS_COUNT] = {
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
};
#define IOPROX_XSF_TEST_DATA \
{ 0x65, 0x01, 0x05, 0x39 }
#define IOPROX_XSF_TEST_DATA_SIZE 4
#define IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT (468 * 2)
const int8_t ioprox_xsf_test_timings[IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] = {
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4,
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
};
#define INDALA26_EMULATION_TIMINGS_COUNT (1024 * 2)
#define INDALA26_TEST_DATA \
{ 0x3B, 0x73, 0x64, 0xA8 }
#define INDALA26_TEST_DATA_SIZE 4
const int8_t indala26_test_timings[INDALA26_EMULATION_TIMINGS_COUNT] = {
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
-1, 1, -1, 1, -1, 1, -1, 1,
};
MU_TEST(test_lfrfid_protocol_em_read_simple) {
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100));
mu_assert_string_eq("EM4100", protocol_dict_get_name(dict, LFRFIDProtocolEM4100));
mu_assert_string_eq("EM-Micro", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100));
const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA;
protocol_dict_decoders_start(dict);
ProtocolId protocol = PROTOCOL_NO;
PulseGlue* pulse_glue = pulse_glue_alloc();
for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT * 10; i++) {
bool pulse_pop = pulse_glue_push(
pulse_glue,
em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT] >= 0,
abs(em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT]) *
LF_RFID_READ_TIMING_MULTIPLIER);
if(pulse_pop) {
uint32_t length, period;
pulse_glue_pop(pulse_glue, &length, &period);
protocol = protocol_dict_decoders_feed(dict, true, period);
if(protocol != PROTOCOL_NO) break;
protocol = protocol_dict_decoders_feed(dict, false, length - period);
if(protocol != PROTOCOL_NO) break;
}
}
pulse_glue_free(pulse_glue);
mu_assert_int_eq(LFRFIDProtocolEM4100, protocol);
uint8_t received_data[EM_TEST_DATA_SIZE] = {0};
protocol_dict_get_data(dict, protocol, received_data, EM_TEST_DATA_SIZE);
mu_assert_mem_eq(data, received_data, EM_TEST_DATA_SIZE);
protocol_dict_free(dict);
}
MU_TEST(test_lfrfid_protocol_em_emulate_simple) {
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100));
mu_assert_string_eq("EM4100", protocol_dict_get_name(dict, LFRFIDProtocolEM4100));
mu_assert_string_eq("EM-Micro", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100));
const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA;
protocol_dict_set_data(dict, LFRFIDProtocolEM4100, data, EM_TEST_DATA_SIZE);
mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolEM4100));
for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT; i++) {
LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolEM4100);
if(level_duration_get_level(level_duration)) {
mu_assert_int_eq(em_test_timings[i], level_duration_get_duration(level_duration));
} else {
mu_assert_int_eq(em_test_timings[i], -level_duration_get_duration(level_duration));
}
}
protocol_dict_free(dict);
}
MU_TEST(test_lfrfid_protocol_h10301_read_simple) {
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
mu_assert_int_eq(
HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301));
mu_assert_string_eq("H10301", protocol_dict_get_name(dict, LFRFIDProtocolH10301));
mu_assert_string_eq("HID", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301));
const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA;
protocol_dict_decoders_start(dict);
ProtocolId protocol = PROTOCOL_NO;
PulseGlue* pulse_glue = pulse_glue_alloc();
for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT * 10; i++) {
bool pulse_pop = pulse_glue_push(
pulse_glue,
hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT] >= 0,
abs(hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT]) *
LF_RFID_READ_TIMING_MULTIPLIER);
if(pulse_pop) {
uint32_t length, period;
pulse_glue_pop(pulse_glue, &length, &period);
protocol = protocol_dict_decoders_feed(dict, true, period);
if(protocol != PROTOCOL_NO) break;
protocol = protocol_dict_decoders_feed(dict, false, length - period);
if(protocol != PROTOCOL_NO) break;
}
}
pulse_glue_free(pulse_glue);
mu_assert_int_eq(LFRFIDProtocolH10301, protocol);
uint8_t received_data[HID10301_TEST_DATA_SIZE] = {0};
protocol_dict_get_data(dict, protocol, received_data, HID10301_TEST_DATA_SIZE);
mu_assert_mem_eq(data, received_data, HID10301_TEST_DATA_SIZE);
protocol_dict_free(dict);
}
MU_TEST(test_lfrfid_protocol_h10301_emulate_simple) {
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
mu_assert_int_eq(
HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301));
mu_assert_string_eq("H10301", protocol_dict_get_name(dict, LFRFIDProtocolH10301));
mu_assert_string_eq("HID", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301));
const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA;
protocol_dict_set_data(dict, LFRFIDProtocolH10301, data, HID10301_TEST_DATA_SIZE);
mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolH10301));
for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT; i++) {
LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolH10301);
if(level_duration_get_level(level_duration)) {
mu_assert_int_eq(
hid10301_test_timings[i], level_duration_get_duration(level_duration));
} else {
mu_assert_int_eq(
hid10301_test_timings[i], -level_duration_get_duration(level_duration));
}
}
protocol_dict_free(dict);
}
MU_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple) {
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
mu_assert_int_eq(
IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF));
mu_assert_string_eq("IoProxXSF", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF));
mu_assert_string_eq("Kantech", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF));
const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA;
protocol_dict_decoders_start(dict);
ProtocolId protocol = PROTOCOL_NO;
PulseGlue* pulse_glue = pulse_glue_alloc();
for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT * 10; i++) {
bool pulse_pop = pulse_glue_push(
pulse_glue,
ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] >= 0,
abs(ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT]) *
LF_RFID_READ_TIMING_MULTIPLIER);
if(pulse_pop) {
uint32_t length, period;
pulse_glue_pop(pulse_glue, &length, &period);
protocol = protocol_dict_decoders_feed(dict, true, period);
if(protocol != PROTOCOL_NO) break;
protocol = protocol_dict_decoders_feed(dict, false, length - period);
if(protocol != PROTOCOL_NO) break;
}
}
pulse_glue_free(pulse_glue);
mu_assert_int_eq(LFRFIDProtocolIOProxXSF, protocol);
uint8_t received_data[IOPROX_XSF_TEST_DATA_SIZE] = {0};
protocol_dict_get_data(dict, protocol, received_data, IOPROX_XSF_TEST_DATA_SIZE);
mu_assert_mem_eq(data, received_data, IOPROX_XSF_TEST_DATA_SIZE);
protocol_dict_free(dict);
}
MU_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple) {
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
mu_assert_int_eq(
IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF));
mu_assert_string_eq("IoProxXSF", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF));
mu_assert_string_eq("Kantech", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF));
const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA;
protocol_dict_set_data(dict, LFRFIDProtocolIOProxXSF, data, IOPROX_XSF_TEST_DATA_SIZE);
mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIOProxXSF));
for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT; i++) {
LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIOProxXSF);
if(level_duration_get_level(level_duration)) {
mu_assert_int_eq(
ioprox_xsf_test_timings[i], level_duration_get_duration(level_duration));
} else {
mu_assert_int_eq(
ioprox_xsf_test_timings[i], -level_duration_get_duration(level_duration));
}
}
protocol_dict_free(dict);
}
MU_TEST(test_lfrfid_protocol_inadala26_emulate_simple) {
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
mu_assert_int_eq(
INDALA26_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIndala26));
mu_assert_string_eq("Indala26", protocol_dict_get_name(dict, LFRFIDProtocolIndala26));
mu_assert_string_eq("Motorola", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIndala26));
const uint8_t data[INDALA26_TEST_DATA_SIZE] = INDALA26_TEST_DATA;
protocol_dict_set_data(dict, LFRFIDProtocolIndala26, data, INDALA26_TEST_DATA_SIZE);
mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIndala26));
for(size_t i = 0; i < INDALA26_EMULATION_TIMINGS_COUNT; i++) {
LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIndala26);
if(level_duration_get_level(level_duration)) {
mu_assert_int_eq(
indala26_test_timings[i], level_duration_get_duration(level_duration));
} else {
mu_assert_int_eq(
indala26_test_timings[i], -level_duration_get_duration(level_duration));
}
}
protocol_dict_free(dict);
}
MU_TEST_SUITE(test_lfrfid_protocols_suite) {
MU_RUN_TEST(test_lfrfid_protocol_em_read_simple);
MU_RUN_TEST(test_lfrfid_protocol_em_emulate_simple);
MU_RUN_TEST(test_lfrfid_protocol_h10301_read_simple);
MU_RUN_TEST(test_lfrfid_protocol_h10301_emulate_simple);
MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple);
MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple);
MU_RUN_TEST(test_lfrfid_protocol_inadala26_emulate_simple);
}
int run_minunit_test_lfrfid_protocols() {
MU_RUN_SUITE(test_lfrfid_protocols_suite);
return MU_EXIT_CODE;
}

View File

@@ -0,0 +1,656 @@
/*
* Copyright (c) 2012 David Siñuela Pastor, siu.4coders@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef MINUNIT_MINUNIT_H
#define MINUNIT_MINUNIT_H
#ifdef __cplusplus
extern "C" {
#endif
#if defined(_WIN32)
#include <Windows.h>
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#define __func__ __FUNCTION__
#endif
#elif defined(__unix__) || defined(__unix) || defined(unix) || \
(defined(__APPLE__) && defined(__MACH__))
/* Change POSIX C SOURCE version for pure c99 compilers */
#if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200112L
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
#include <unistd.h> /* POSIX flags */
#include <time.h> /* clock_gettime(), time() */
#include <sys/time.h> /* gethrtime(), gettimeofday() */
#include <sys/resource.h>
#include <sys/times.h>
#include <string.h>
#if defined(__MACH__) && defined(__APPLE__)
#include <mach/mach.h>
#include <mach/mach_time.h>
#endif
#if __GNUC__ >= 5 && !defined(__STDC_VERSION__)
#define __func__ __extension__ __FUNCTION__
#endif
#else
// #error "Unable to define timers for an unknown OS."
#endif
#include <stdio.h>
#include <math.h>
/* Maximum length of last message */
#define MINUNIT_MESSAGE_LEN 1024
/* Accuracy with which floats are compared */
#define MINUNIT_EPSILON 1E-12
#include "minunit_vars_ex.h"
/* Test setup and teardown function pointers */
__attribute__((unused)) static void (*minunit_setup)(void) = NULL;
__attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
void minunit_print_progress(void);
void minunit_print_fail(const char* error);
/* Definitions */
#define MU_TEST(method_name) static void method_name(void)
#define MU_TEST_1(method_name, arg_1) static void method_name(arg_1)
#define MU_TEST_SUITE(suite_name) static void suite_name(void)
#define MU__SAFE_BLOCK(block) \
do { \
block \
} while(0)
/* Run test suite and unset setup and teardown functions */
#define MU_RUN_SUITE(suite_name) \
MU__SAFE_BLOCK(suite_name(); minunit_setup = NULL; minunit_teardown = NULL;)
/* Configure setup and teardown functions */
#define MU_SUITE_CONFIGURE(setup_fun, teardown_fun) \
MU__SAFE_BLOCK(minunit_setup = setup_fun; minunit_teardown = teardown_fun;)
/* Test runner */
#define MU_RUN_TEST(test) \
MU__SAFE_BLOCK( \
if(minunit_real_timer == 0 && minunit_proc_timer == 0) { \
minunit_real_timer = mu_timer_real(); \
minunit_proc_timer = mu_timer_cpu(); \
} if(minunit_setup) (*minunit_setup)(); \
minunit_status = 0; \
printf(#test "()\r\n"); \
test(); \
minunit_run++; \
if(minunit_status) { \
minunit_fail++; \
minunit_print_fail(minunit_last_message); \
minunit_status = 0; \
} fflush(stdout); \
if(minunit_teardown)(*minunit_teardown)();)
#define MU_RUN_TEST_1(test, arg_1) \
MU__SAFE_BLOCK( \
if(minunit_real_timer == 0 && minunit_proc_timer == 0) { \
minunit_real_timer = mu_timer_real(); \
minunit_proc_timer = mu_timer_cpu(); \
} if(minunit_setup) (*minunit_setup)(); \
minunit_status = 0; \
printf(#test "(" #arg_1 ")\r\n"); \
test(arg_1); \
minunit_run++; \
if(minunit_status) { \
minunit_fail++; \
minunit_print_fail(minunit_last_message); \
minunit_status = 0; \
} fflush(stdout); \
if(minunit_teardown)(*minunit_teardown)();)
/* Report */
#define MU_REPORT() \
MU__SAFE_BLOCK(double minunit_end_real_timer; double minunit_end_proc_timer; printf( \
"\n\n%d tests, %d assertions, %d failures\n", \
minunit_run, \
minunit_assert, \
minunit_fail); \
minunit_end_real_timer = mu_timer_real(); \
minunit_end_proc_timer = mu_timer_cpu(); \
printf( \
"\nFinished in %.8f seconds (real) %.8f seconds (proc)\n\n", \
minunit_end_real_timer - minunit_real_timer, \
minunit_end_proc_timer - minunit_proc_timer);)
#define MU_EXIT_CODE minunit_fail
/* Assertions */
#define mu_check(test) \
MU__SAFE_BLOCK( \
minunit_assert++; if(!(test)) { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: %s", \
__func__, \
__FILE__, \
__LINE__, \
#test); \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_fail(message) \
MU__SAFE_BLOCK(minunit_assert++; snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: %s", \
__func__, \
__FILE__, \
__LINE__, \
message); \
minunit_status = 1; \
return;)
#define mu_assert(test, message) \
MU__SAFE_BLOCK( \
minunit_assert++; if(!(test)) { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: %s", \
__func__, \
__FILE__, \
__LINE__, \
message); \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_assert_int_eq(expected, result) \
MU__SAFE_BLOCK( \
int minunit_tmp_e; int minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \
minunit_tmp_r = (result); \
if(minunit_tmp_e != minunit_tmp_r) { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: %d expected but was %d", \
__func__, \
__FILE__, \
__LINE__, \
minunit_tmp_e, \
minunit_tmp_r); \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_assert_int_not_eq(expected, result) \
MU__SAFE_BLOCK( \
int minunit_tmp_e; int minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \
minunit_tmp_r = (result); \
if(minunit_tmp_e == minunit_tmp_r) { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: expected different results but both were %d", \
__func__, \
__FILE__, \
__LINE__, \
minunit_tmp_e); \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_assert_int_greater_than(val, result) \
MU__SAFE_BLOCK( \
int minunit_tmp_e; int minunit_tmp_r; minunit_assert++; minunit_tmp_e = (val); \
minunit_tmp_r = (result); \
if(val >= minunit_tmp_r) { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: %d <= %d", \
__func__, \
__FILE__, \
__LINE__, \
minunit_tmp_r, \
minunit_tmp_e); \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_assert_int_less_than(val, result) \
MU__SAFE_BLOCK( \
int minunit_tmp_e; int minunit_tmp_r; minunit_assert++; minunit_tmp_e = (val); \
minunit_tmp_r = (result); \
if(val <= minunit_tmp_r) { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: %d >= %d", \
__func__, \
__FILE__, \
__LINE__, \
minunit_tmp_r, \
minunit_tmp_e); \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_assert_int_between(expected_lower, expected_upper, result) \
MU__SAFE_BLOCK( \
int minunit_tmp_e; int minunit_tmp_m; int minunit_tmp_r; minunit_assert++; \
minunit_tmp_e = (expected_lower); \
minunit_tmp_m = (expected_upper); \
minunit_tmp_r = (result); \
if(result < minunit_tmp_e || result > minunit_tmp_m) { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: %d was not between (inclusive) %d and %d", \
__func__, \
__FILE__, \
__LINE__, \
minunit_tmp_e, \
minunit_tmp_r, \
minunit_tmp_m); \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_assert_int_in(expected, array_length, result) \
MU__SAFE_BLOCK( \
int minunit_tmp_r; minunit_assert++; minunit_tmp_r = (result); int t = 0; int i; \
for(i = 0; i < array_length; i++) { \
if(expected[i] == minunit_tmp_r) t = 1; \
} if(t == 0) { \
char tmp[500] = {0}; \
tmp[0] = '['; \
for(i = 0; i < array_length; i++) { \
sprintf(tmp + strlen(tmp), "%d, ", expected[i]); \
} \
int len = strlen(tmp); \
tmp[len - 2] = ']'; \
tmp[len - 1] = '\0'; \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: expected to be one of %s but was %d", \
__func__, \
__FILE__, \
__LINE__, \
tmp, \
minunit_tmp_r); \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_assert_double_eq(expected, result) \
MU__SAFE_BLOCK( \
double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \
minunit_tmp_r = (result); \
if(fabs(minunit_tmp_e - minunit_tmp_r) > MINUNIT_EPSILON) { \
int minunit_significant_figures = 1 - log10(MINUNIT_EPSILON); \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: %.*g expected but was %.*g", \
__func__, \
__FILE__, \
__LINE__, \
minunit_significant_figures, \
minunit_tmp_e, \
minunit_significant_figures, \
minunit_tmp_r); \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_assert_double_greater_than(val, result) \
MU__SAFE_BLOCK( \
double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (val); \
minunit_tmp_r = (result); \
if(val >= minunit_tmp_r) { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: %f <= %f", \
__func__, \
__FILE__, \
__LINE__, \
minunit_tmp_r, \
minunit_tmp_e); \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_assert_double_less_than(val, result) \
MU__SAFE_BLOCK( \
double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (val); \
minunit_tmp_r = (result); \
if(val <= minunit_tmp_r) { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: %f >= %f", \
__func__, \
__FILE__, \
__LINE__, \
minunit_tmp_r, \
minunit_tmp_e); \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_assert_double_between(expected_lower, expected_upper, result) \
MU__SAFE_BLOCK( \
double minunit_tmp_e; double minunit_tmp_m; double minunit_tmp_r; minunit_assert++; \
minunit_tmp_e = (expected_lower); \
minunit_tmp_m = (expected_upper); \
minunit_tmp_r = (result); \
if(result < minunit_tmp_e || result > minunit_tmp_m) { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: %f was not between (inclusive) %f and %f", \
__func__, \
__FILE__, \
__LINE__, \
minunit_tmp_e, \
minunit_tmp_r, \
minunit_tmp_m); \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_assert_string_eq(expected, result) \
MU__SAFE_BLOCK( \
const char* minunit_tmp_e = expected; const char* minunit_tmp_r = result; \
minunit_assert++; \
if(!minunit_tmp_e) { minunit_tmp_e = "<null pointer>"; } if(!minunit_tmp_r) { \
minunit_tmp_r = "<null pointer>"; \
} if(strcmp(minunit_tmp_e, minunit_tmp_r)) { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: '%s' expected but was '%s'", \
__func__, \
__FILE__, \
__LINE__, \
minunit_tmp_e, \
minunit_tmp_r); \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_assert_mem_eq(expected, result, size) \
MU__SAFE_BLOCK( \
const void* minunit_tmp_e = expected; const void* minunit_tmp_r = result; \
minunit_assert++; \
if(memcmp(minunit_tmp_e, minunit_tmp_r, size)) { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: mem not equal\r\n\tEXP RES", \
__func__, \
__FILE__, \
__LINE__); \
for(size_t __index = 0; __index < size; __index++) { \
if(strlen(minunit_last_message) > MINUNIT_MESSAGE_LEN - 20) break; \
uint8_t __e = ((uint8_t*)minunit_tmp_e)[__index]; \
uint8_t __r = ((uint8_t*)minunit_tmp_r)[__index]; \
snprintf( \
minunit_last_message + strlen(minunit_last_message), \
MINUNIT_MESSAGE_LEN - strlen(minunit_last_message), \
"\r\n\t%02X %s %02X", \
__e, \
((__e == __r) ? ".." : "!="), \
__r); \
} \
minunit_status = 1; \
return; \
} else { minunit_print_progress(); })
#define mu_assert_null(result) \
MU__SAFE_BLOCK( \
minunit_assert++; if(result == NULL) { minunit_print_progress(); } else { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: Expected result was not NULL", \
__func__, \
__FILE__, \
__LINE__); \
minunit_status = 1; \
return; \
})
#define mu_assert_not_null(result) \
MU__SAFE_BLOCK( \
minunit_assert++; if(result != NULL) { minunit_print_progress(); } else { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: Expected result was not NULL", \
__func__, \
__FILE__, \
__LINE__); \
minunit_status = 1; \
return; \
})
#define mu_assert_pointers_eq(pointer1, pointer2) \
MU__SAFE_BLOCK( \
minunit_assert++; if(pointer1 == pointer2) { minunit_print_progress(); } else { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: Expected the pointers to point to the same memory location", \
__func__, \
__FILE__, \
__LINE__); \
minunit_status = 1; \
return; \
})
#define mu_assert_pointers_not_eq(pointer1, pointer2) \
MU__SAFE_BLOCK( \
minunit_assert++; if(pointer1 != pointer2) { minunit_print_progress(); } else { \
snprintf( \
minunit_last_message, \
MINUNIT_MESSAGE_LEN, \
"%s failed:\r\n\t%s:%d: Expected the pointers to point to the same memory location", \
__func__, \
__FILE__, \
__LINE__); \
minunit_status = 1; \
return; \
})
/*
* The following two functions were written by David Robert Nadeau
* from http://NadeauSoftware.com/ and distributed under the
* Creative Commons Attribution 3.0 Unported License
*/
/**
* Returns the real time, in seconds, or -1.0 if an error occurred.
*
* Time is measured since an arbitrary and OS-dependent start time.
* The returned real time is only useful for computing an elapsed time
* between two calls to this function.
*/
__attribute__((unused)) static double mu_timer_real(void) {
#if defined(_WIN32)
/* Windows 2000 and later. ---------------------------------- */
LARGE_INTEGER Time;
LARGE_INTEGER Frequency;
QueryPerformanceFrequency(&Frequency);
QueryPerformanceCounter(&Time);
Time.QuadPart *= 1000000;
Time.QuadPart /= Frequency.QuadPart;
return (double)Time.QuadPart / 1000000.0;
#elif(defined(__hpux) || defined(hpux)) || \
((defined(__sun__) || defined(__sun) || defined(sun)) && \
(defined(__SVR4) || defined(__svr4__)))
/* HP-UX, Solaris. ------------------------------------------ */
return (double)gethrtime() / 1000000000.0;
#elif defined(__MACH__) && defined(__APPLE__)
/* OSX. ----------------------------------------------------- */
static double timeConvert = 0.0;
if(timeConvert == 0.0) {
mach_timebase_info_data_t timeBase;
(void)mach_timebase_info(&timeBase);
timeConvert = (double)timeBase.numer / (double)timeBase.denom / 1000000000.0;
}
return (double)mach_absolute_time() * timeConvert;
#elif defined(_POSIX_VERSION)
/* POSIX. --------------------------------------------------- */
struct timeval tm;
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
{
struct timespec ts;
#if defined(CLOCK_MONOTONIC_PRECISE)
/* BSD. --------------------------------------------- */
const clockid_t id = CLOCK_MONOTONIC_PRECISE;
#elif defined(CLOCK_MONOTONIC_RAW)
/* Linux. ------------------------------------------- */
const clockid_t id = CLOCK_MONOTONIC_RAW;
#elif defined(CLOCK_HIGHRES)
/* Solaris. ----------------------------------------- */
const clockid_t id = CLOCK_HIGHRES;
#elif defined(CLOCK_MONOTONIC)
/* AIX, BSD, Linux, POSIX, Solaris. ----------------- */
const clockid_t id = CLOCK_MONOTONIC;
#elif defined(CLOCK_REALTIME)
/* AIX, BSD, HP-UX, Linux, POSIX. ------------------- */
const clockid_t id = CLOCK_REALTIME;
#else
const clockid_t id = (clockid_t)-1; /* Unknown. */
#endif /* CLOCK_* */
if(id != (clockid_t)-1 && clock_gettime(id, &ts) != -1)
return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0;
/* Fall thru. */
}
#endif /* _POSIX_TIMERS */
/* AIX, BSD, Cygwin, HP-UX, Linux, OSX, POSIX, Solaris. ----- */
gettimeofday(&tm, NULL);
return (double)tm.tv_sec + (double)tm.tv_usec / 1000000.0;
#else
return -1.0; /* Failed. */
#endif
}
/**
* Returns the amount of CPU time used by the current process,
* in seconds, or -1.0 if an error occurred.
*/
__attribute__((unused)) static double mu_timer_cpu(void) {
#if defined(_WIN32)
/* Windows -------------------------------------------------- */
FILETIME createTime;
FILETIME exitTime;
FILETIME kernelTime;
FILETIME userTime;
/* This approach has a resolution of 1/64 second. Unfortunately, Windows' API does not offer better */
if(GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime) != 0) {
ULARGE_INTEGER userSystemTime;
memcpy(&userSystemTime, &userTime, sizeof(ULARGE_INTEGER));
return (double)userSystemTime.QuadPart / 10000000.0;
}
#elif defined(__unix__) || defined(__unix) || defined(unix) || \
(defined(__APPLE__) && defined(__MACH__))
/* AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris --------- */
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
/* Prefer high-res POSIX timers, when available. */
{
clockid_t id;
struct timespec ts;
#if _POSIX_CPUTIME > 0
/* Clock ids vary by OS. Query the id, if possible. */
if(clock_getcpuclockid(0, &id) == -1)
#endif
#if defined(CLOCK_PROCESS_CPUTIME_ID)
/* Use known clock id for AIX, Linux, or Solaris. */
id = CLOCK_PROCESS_CPUTIME_ID;
#elif defined(CLOCK_VIRTUAL)
/* Use known clock id for BSD or HP-UX. */
id = CLOCK_VIRTUAL;
#else
id = (clockid_t)-1;
#endif
if(id != (clockid_t)-1 && clock_gettime(id, &ts) != -1)
return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0;
}
#endif
#if defined(RUSAGE_SELF)
{
struct rusage rusage;
if(getrusage(RUSAGE_SELF, &rusage) != -1)
return (double)rusage.ru_utime.tv_sec + (double)rusage.ru_utime.tv_usec / 1000000.0;
}
#endif
#if defined(_SC_CLK_TCK)
{
const double ticks = (double)sysconf(_SC_CLK_TCK);
struct tms tms;
if(times(&tms) != (clock_t)-1) return (double)tms.tms_utime / ticks;
}
#endif
#if defined(CLOCKS_PER_SEC)
{
clock_t cl = clock();
if(cl != (clock_t)-1) return (double)cl / (double)CLOCKS_PER_SEC;
}
#endif
#endif
return -1; /* Failed. */
}
#ifdef __cplusplus
}
#endif
#endif /* MINUNIT_MINUNIT_H */

View File

@@ -0,0 +1,15 @@
#pragma once
#include "minunit.h"
/* Misc. counters */
int minunit_run = 0;
int minunit_assert = 0;
int minunit_fail = 0;
int minunit_status = 0;
/* Timers */
double minunit_real_timer = 0;
double minunit_proc_timer = 0;
/* Last message */
char minunit_last_message[MINUNIT_MESSAGE_LEN];

View File

@@ -0,0 +1,15 @@
#pragma once
#include "minunit.h"
/* Misc. counters */
extern int minunit_run;
extern int minunit_assert;
extern int minunit_fail;
extern int minunit_status;
/* Timers */
extern double minunit_real_timer;
extern double minunit_proc_timer;
/* Last message */
extern char minunit_last_message[MINUNIT_MESSAGE_LEN];

View File

@@ -0,0 +1,184 @@
#include <furi.h>
#include <furi_hal.h>
#include <storage/storage.h>
#include <lib/flipper_format/flipper_format.h>
#include <lib/nfc/protocols/nfca.h>
#include <lib/digital_signal/digital_signal.h>
#include <lib/flipper_format/flipper_format_i.h>
#include <lib/toolbox/stream/file_stream.h>
#include "../minunit.h"
#define TAG "NfcTest"
#define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/")
#define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc"
#define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc"
static const char* nfc_test_file_type = "Flipper NFC test";
static const uint32_t nfc_test_file_version = 1;
#define NFC_TEST_DATA_MAX_LEN 18
#define NFC_TETS_TIMINGS_MAX_LEN 1350
typedef struct {
Storage* storage;
NfcaSignal* signal;
uint32_t test_data_len;
uint8_t test_data[NFC_TEST_DATA_MAX_LEN];
uint32_t test_timings_len;
uint32_t test_timings[NFC_TETS_TIMINGS_MAX_LEN];
} NfcTest;
static NfcTest* nfc_test = NULL;
static void nfc_test_alloc() {
nfc_test = malloc(sizeof(NfcTest));
nfc_test->signal = nfca_signal_alloc();
nfc_test->storage = furi_record_open(RECORD_STORAGE);
}
static void nfc_test_free() {
furi_assert(nfc_test);
furi_record_close(RECORD_STORAGE);
nfca_signal_free(nfc_test->signal);
free(nfc_test);
nfc_test = NULL;
}
static bool nfc_test_read_signal_from_file(const char* file_name) {
bool success = false;
FlipperFormat* file = flipper_format_file_alloc(nfc_test->storage);
string_t file_type;
string_init(file_type);
uint32_t file_version = 0;
do {
if(!flipper_format_file_open_existing(file, file_name)) break;
if(!flipper_format_read_header(file, file_type, &file_version)) break;
if(string_cmp_str(file_type, nfc_test_file_type) || file_version != nfc_test_file_version)
break;
if(!flipper_format_read_uint32(file, "Data length", &nfc_test->test_data_len, 1)) break;
if(nfc_test->test_data_len > NFC_TEST_DATA_MAX_LEN) break;
if(!flipper_format_read_hex(
file, "Plain data", nfc_test->test_data, nfc_test->test_data_len))
break;
if(!flipper_format_read_uint32(file, "Timings length", &nfc_test->test_timings_len, 1))
break;
if(nfc_test->test_timings_len > NFC_TETS_TIMINGS_MAX_LEN) break;
if(!flipper_format_read_uint32(
file, "Timings", nfc_test->test_timings, nfc_test->test_timings_len))
break;
success = true;
} while(false);
string_clear(file_type);
flipper_format_free(file);
return success;
}
static bool nfc_test_digital_signal_test_encode(
const char* file_name,
uint32_t encode_max_time,
uint32_t timing_tolerance,
uint32_t timings_sum_tolerance) {
furi_assert(nfc_test);
bool success = false;
uint32_t time = 0;
uint32_t dut_timings_sum = 0;
uint32_t ref_timings_sum = 0;
uint8_t parity[10] = {};
do {
// Read test data
if(!nfc_test_read_signal_from_file(file_name)) break;
// Encode signal
FURI_CRITICAL_ENTER();
time = DWT->CYCCNT;
nfca_signal_encode(
nfc_test->signal, nfc_test->test_data, nfc_test->test_data_len * 8, parity);
digital_signal_prepare_arr(nfc_test->signal->tx_signal);
time = (DWT->CYCCNT - time) / furi_hal_cortex_instructions_per_microsecond();
FURI_CRITICAL_EXIT();
// Check timings
if(time > encode_max_time) {
FURI_LOG_E(
TAG, "Encoding time: %d us while accepted value: %d us", time, encode_max_time);
break;
}
// Check data
if(nfc_test->signal->tx_signal->edge_cnt != nfc_test->test_timings_len) {
FURI_LOG_E(TAG, "Not equal timings buffers length");
break;
}
uint32_t timings_diff = 0;
uint32_t* ref = nfc_test->test_timings;
uint32_t* dut = nfc_test->signal->tx_signal->reload_reg_buff;
bool timing_check_success = true;
for(size_t i = 0; i < nfc_test->test_timings_len; i++) {
timings_diff = dut[i] > ref[i] ? dut[i] - ref[i] : ref[i] - dut[i];
dut_timings_sum += dut[i];
ref_timings_sum += ref[i];
if(timings_diff > timing_tolerance) {
FURI_LOG_E(
TAG, "Too big differece in %d timings. Ref: %d, DUT: %d", i, ref[i], dut[i]);
timing_check_success = false;
break;
}
}
if(!timing_check_success) break;
uint32_t sum_diff = dut_timings_sum > ref_timings_sum ? dut_timings_sum - ref_timings_sum :
ref_timings_sum - dut_timings_sum;
if(sum_diff > timings_sum_tolerance) {
FURI_LOG_E(
TAG,
"Too big difference in timings sum. Ref: %d, DUT: %d",
ref_timings_sum,
dut_timings_sum);
break;
}
FURI_LOG_I(TAG, "Encoding time: %d us. Acceptable time: %d us", time, encode_max_time);
FURI_LOG_I(
TAG,
"Timings sum difference: %d [1/64MHZ]. Acceptable difference: %d [1/64MHz]",
sum_diff,
timings_sum_tolerance);
success = true;
} while(false);
return success;
}
MU_TEST(nfc_digital_signal_test) {
mu_assert(
nfc_test_digital_signal_test_encode(
NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_SHORT_FILE, 500, 1, 37),
"NFC short digital signal test failed\r\n");
mu_assert(
nfc_test_digital_signal_test_encode(
NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_LONG_FILE, 2000, 1, 37),
"NFC long digital signal test failed\r\n");
}
MU_TEST_SUITE(nfc) {
nfc_test_alloc();
MU_RUN_TEST(nfc_digital_signal_test);
nfc_test_free();
}
int run_minunit_test_nfc() {
MU_RUN_SUITE(nfc);
return MU_EXIT_CODE;
}

View File

@@ -0,0 +1,222 @@
#include <furi.h>
#include "../minunit.h"
#include <toolbox/protocols/protocol_dict.h>
typedef enum {
TestDictProtocol0,
TestDictProtocol1,
TestDictProtocolMax,
} TestDictProtocols;
/*********************** PROTOCOL 0 START ***********************/
typedef struct {
uint32_t data;
size_t encoder_counter;
} Protocol0Data;
static const uint32_t protocol_0_decoder_result = 0xDEADBEEF;
static void* protocol_0_alloc() {
void* data = malloc(sizeof(Protocol0Data));
return data;
}
static void protocol_0_free(Protocol0Data* data) {
free(data);
}
static uint8_t* protocol_0_get_data(Protocol0Data* data) {
return (uint8_t*)&data->data;
}
static void protocol_0_decoder_start(Protocol0Data* data) {
data->data = 0;
}
static bool protocol_0_decoder_feed(Protocol0Data* data, bool level, uint32_t duration) {
if(level && duration == 666) {
data->data = protocol_0_decoder_result;
return true;
} else {
return false;
}
}
static bool protocol_0_encoder_start(Protocol0Data* data) {
data->encoder_counter = 0;
return true;
}
static LevelDuration protocol_0_encoder_yield(Protocol0Data* data) {
data->encoder_counter++;
return level_duration_make(data->encoder_counter % 2, data->data);
}
/*********************** PROTOCOL 1 START ***********************/
typedef struct {
uint64_t data;
size_t encoder_counter;
} Protocol1Data;
static const uint64_t protocol_1_decoder_result = 0x1234567890ABCDEF;
static void* protocol_1_alloc() {
void* data = malloc(sizeof(Protocol1Data));
return data;
}
static void protocol_1_free(Protocol1Data* data) {
free(data);
}
static uint8_t* protocol_1_get_data(Protocol1Data* data) {
return (uint8_t*)&data->data;
}
static void protocol_1_decoder_start(Protocol1Data* data) {
data->data = 0;
}
static bool protocol_1_decoder_feed(Protocol1Data* data, bool level, uint32_t duration) {
if(level && duration == 543) {
data->data = 0x1234567890ABCDEF;
return true;
} else {
return false;
}
}
static bool protocol_1_encoder_start(Protocol1Data* data) {
data->encoder_counter = 0;
return true;
}
static LevelDuration protocol_1_encoder_yield(Protocol1Data* data) {
data->encoder_counter++;
return level_duration_make(!(data->encoder_counter % 2), 100);
}
/*********************** PROTOCOLS DESCRIPTION ***********************/
static const ProtocolBase protocol_0 = {
.name = "Protocol 0",
.manufacturer = "Manufacturer 0",
.data_size = 4,
.alloc = (ProtocolAlloc)protocol_0_alloc,
.free = (ProtocolFree)protocol_0_free,
.get_data = (ProtocolGetData)protocol_0_get_data,
.decoder =
{
.start = (ProtocolDecoderStart)protocol_0_decoder_start,
.feed = (ProtocolDecoderFeed)protocol_0_decoder_feed,
},
.encoder =
{
.start = (ProtocolEncoderStart)protocol_0_encoder_start,
.yield = (ProtocolEncoderYield)protocol_0_encoder_yield,
},
};
static const ProtocolBase protocol_1 = {
.name = "Protocol 1",
.manufacturer = "Manufacturer 1",
.data_size = 8,
.alloc = (ProtocolAlloc)protocol_1_alloc,
.free = (ProtocolFree)protocol_1_free,
.get_data = (ProtocolGetData)protocol_1_get_data,
.decoder =
{
.start = (ProtocolDecoderStart)protocol_1_decoder_start,
.feed = (ProtocolDecoderFeed)protocol_1_decoder_feed,
},
.encoder =
{
.start = (ProtocolEncoderStart)protocol_1_encoder_start,
.yield = (ProtocolEncoderYield)protocol_1_encoder_yield,
},
};
static const ProtocolBase* test_protocols_base[] = {
[TestDictProtocol0] = &protocol_0,
[TestDictProtocol1] = &protocol_1,
};
MU_TEST(test_protocol_dict) {
ProtocolDict* dict = protocol_dict_alloc(test_protocols_base, TestDictProtocolMax);
size_t max_data_size = protocol_dict_get_max_data_size(dict);
mu_assert_int_eq(8, max_data_size);
uint8_t* data = malloc(max_data_size);
protocol_dict_decoders_start(dict);
ProtocolId protocol_id = PROTOCOL_NO;
for(size_t i = 0; i < 100; i++) {
protocol_id = protocol_dict_decoders_feed(dict, i % 2, 100);
mu_assert_int_eq(PROTOCOL_NO, protocol_id);
}
// trigger protocol 1
protocol_id = protocol_dict_decoders_feed(dict, true, 543);
mu_assert_int_eq(TestDictProtocol1, protocol_id);
mu_assert_string_eq("Protocol 1", protocol_dict_get_name(dict, protocol_id));
mu_assert_string_eq("Manufacturer 1", protocol_dict_get_manufacturer(dict, protocol_id));
size_t data_size = protocol_dict_get_data_size(dict, protocol_id);
mu_assert_int_eq(8, data_size);
protocol_dict_get_data(dict, protocol_id, data, data_size);
mu_assert_mem_eq(&protocol_1_decoder_result, data, data_size);
// trigger protocol 0
protocol_id = protocol_dict_decoders_feed(dict, true, 666);
mu_assert_int_eq(TestDictProtocol0, protocol_id);
mu_assert_string_eq("Protocol 0", protocol_dict_get_name(dict, protocol_id));
mu_assert_string_eq("Manufacturer 0", protocol_dict_get_manufacturer(dict, protocol_id));
data_size = protocol_dict_get_data_size(dict, protocol_id);
mu_assert_int_eq(4, data_size);
protocol_dict_get_data(dict, protocol_id, data, data_size);
mu_assert_mem_eq(&protocol_0_decoder_result, data, data_size);
protocol_dict_decoders_start(dict);
protocol_id = TestDictProtocol0;
const uint8_t protocol_0_test_data[4] = {100, 0, 0, 0};
protocol_dict_set_data(dict, protocol_id, protocol_0_test_data, 4);
mu_check(protocol_dict_encoder_start(dict, protocol_id));
LevelDuration level;
level = protocol_dict_encoder_yield(dict, protocol_id);
mu_assert_int_eq(true, level_duration_get_level(level));
mu_assert_int_eq(100, level_duration_get_duration(level));
level = protocol_dict_encoder_yield(dict, protocol_id);
mu_assert_int_eq(false, level_duration_get_level(level));
mu_assert_int_eq(100, level_duration_get_duration(level));
level = protocol_dict_encoder_yield(dict, protocol_id);
mu_assert_int_eq(true, level_duration_get_level(level));
mu_assert_int_eq(100, level_duration_get_duration(level));
mu_check(protocol_dict_encoder_start(dict, protocol_id));
level = protocol_dict_encoder_yield(dict, protocol_id);
mu_assert_int_eq(true, level_duration_get_level(level));
mu_assert_int_eq(100, level_duration_get_duration(level));
protocol_dict_free(dict);
free(data);
}
MU_TEST_SUITE(test_protocol_dict_suite) {
MU_RUN_TEST(test_protocol_dict);
}
int run_minunit_test_protocol_dict() {
MU_RUN_SUITE(test_protocol_dict_suite);
return MU_EXIT_CODE;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,272 @@
#include "../minunit.h"
#include <furi.h>
#include <m-dict.h>
#include <toolbox/dir_walk.h>
static const char* const storage_test_dirwalk_paths[] = {
"1",
"11",
"111",
"1/2",
"1/22",
"1/222",
"11/2",
"111/2",
"111/22",
"111/22/33",
};
static const char* const storage_test_dirwalk_files[] = {
"file1.test",
"file2.test",
"file3.ext_test",
"1/file1.test",
"111/22/33/file1.test",
"111/22/33/file2.test",
"111/22/33/file3.ext_test",
"111/22/33/file4.ext_test",
};
typedef struct {
const char* const path;
bool is_dir;
} StorageTestPathDesc;
const StorageTestPathDesc storage_test_dirwalk_full[] = {
{.path = "1", .is_dir = true},
{.path = "11", .is_dir = true},
{.path = "111", .is_dir = true},
{.path = "1/2", .is_dir = true},
{.path = "1/22", .is_dir = true},
{.path = "1/222", .is_dir = true},
{.path = "11/2", .is_dir = true},
{.path = "111/2", .is_dir = true},
{.path = "111/22", .is_dir = true},
{.path = "111/22/33", .is_dir = true},
{.path = "file1.test", .is_dir = false},
{.path = "file2.test", .is_dir = false},
{.path = "file3.ext_test", .is_dir = false},
{.path = "1/file1.test", .is_dir = false},
{.path = "111/22/33/file1.test", .is_dir = false},
{.path = "111/22/33/file2.test", .is_dir = false},
{.path = "111/22/33/file3.ext_test", .is_dir = false},
{.path = "111/22/33/file4.ext_test", .is_dir = false},
};
const StorageTestPathDesc storage_test_dirwalk_no_recursive[] = {
{.path = "1", .is_dir = true},
{.path = "11", .is_dir = true},
{.path = "111", .is_dir = true},
{.path = "file1.test", .is_dir = false},
{.path = "file2.test", .is_dir = false},
{.path = "file3.ext_test", .is_dir = false},
};
const StorageTestPathDesc storage_test_dirwalk_filter[] = {
{.path = "file1.test", .is_dir = false},
{.path = "file2.test", .is_dir = false},
{.path = "1/file1.test", .is_dir = false},
{.path = "111/22/33/file1.test", .is_dir = false},
{.path = "111/22/33/file2.test", .is_dir = false},
};
typedef struct {
bool is_dir;
bool visited;
} StorageTestPath;
DICT_DEF2(StorageTestPathDict, string_t, STRING_OPLIST, StorageTestPath, M_POD_OPLIST)
static StorageTestPathDict_t*
storage_test_paths_alloc(const StorageTestPathDesc paths[], size_t paths_count) {
StorageTestPathDict_t* data = malloc(sizeof(StorageTestPathDict_t));
StorageTestPathDict_init(*data);
for(size_t i = 0; i < paths_count; i++) {
string_t key;
string_init_set(key, paths[i].path);
StorageTestPath value = {
.is_dir = paths[i].is_dir,
.visited = false,
};
StorageTestPathDict_set_at(*data, key, value);
string_clear(key);
}
return data;
}
static void storage_test_paths_free(StorageTestPathDict_t* data) {
StorageTestPathDict_clear(*data);
free(data);
}
static bool storage_test_paths_mark(StorageTestPathDict_t* data, string_t path, bool is_dir) {
bool found = false;
StorageTestPath* record = StorageTestPathDict_get(*data, path);
if(record) {
if(is_dir == record->is_dir) {
if(record->visited == false) {
record->visited = true;
found = true;
}
}
}
return found;
}
static bool storage_test_paths_check(StorageTestPathDict_t* data) {
bool error = false;
StorageTestPathDict_it_t it;
for(StorageTestPathDict_it(it, *data); !StorageTestPathDict_end_p(it);
StorageTestPathDict_next(it)) {
const StorageTestPathDict_itref_t* itref = StorageTestPathDict_cref(it);
if(itref->value.visited == false) {
error = true;
break;
}
}
return error;
}
static bool write_file_13DA(Storage* storage, const char* path) {
File* file = storage_file_alloc(storage);
bool result = false;
if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
result = storage_file_write(file, "13DA", 4) == 4;
}
storage_file_close(file);
storage_file_free(file);
return result;
}
static void storage_dirs_create(Storage* storage, const char* base) {
string_t path;
string_init(path);
storage_common_mkdir(storage, base);
for(size_t i = 0; i < COUNT_OF(storage_test_dirwalk_paths); i++) {
string_printf(path, "%s/%s", base, storage_test_dirwalk_paths[i]);
storage_common_mkdir(storage, string_get_cstr(path));
}
for(size_t i = 0; i < COUNT_OF(storage_test_dirwalk_files); i++) {
string_printf(path, "%s/%s", base, storage_test_dirwalk_files[i]);
write_file_13DA(storage, string_get_cstr(path));
}
string_clear(path);
}
MU_TEST_1(test_dirwalk_full, Storage* storage) {
string_t path;
string_init(path);
FileInfo fileinfo;
StorageTestPathDict_t* paths =
storage_test_paths_alloc(storage_test_dirwalk_full, COUNT_OF(storage_test_dirwalk_full));
DirWalk* dir_walk = dir_walk_alloc(storage);
mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk")));
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
string_right(path, strlen(EXT_PATH("dirwalk/")));
mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
}
dir_walk_free(dir_walk);
string_clear(path);
mu_check(storage_test_paths_check(paths) == false);
storage_test_paths_free(paths);
}
MU_TEST_1(test_dirwalk_no_recursive, Storage* storage) {
string_t path;
string_init(path);
FileInfo fileinfo;
StorageTestPathDict_t* paths = storage_test_paths_alloc(
storage_test_dirwalk_no_recursive, COUNT_OF(storage_test_dirwalk_no_recursive));
DirWalk* dir_walk = dir_walk_alloc(storage);
dir_walk_set_recursive(dir_walk, false);
mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk")));
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
string_right(path, strlen(EXT_PATH("dirwalk/")));
mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
}
dir_walk_free(dir_walk);
string_clear(path);
mu_check(storage_test_paths_check(paths) == false);
storage_test_paths_free(paths);
}
static bool test_dirwalk_filter_no_folder_ext(const char* name, FileInfo* fileinfo, void* ctx) {
UNUSED(ctx);
// only files
if(!(fileinfo->flags & FSF_DIRECTORY)) {
// with ".test" in name
if(strstr(name, ".test") != NULL) {
return true;
}
}
return false;
}
MU_TEST_1(test_dirwalk_filter, Storage* storage) {
string_t path;
string_init(path);
FileInfo fileinfo;
StorageTestPathDict_t* paths = storage_test_paths_alloc(
storage_test_dirwalk_filter, COUNT_OF(storage_test_dirwalk_filter));
DirWalk* dir_walk = dir_walk_alloc(storage);
dir_walk_set_filter_cb(dir_walk, test_dirwalk_filter_no_folder_ext, NULL);
mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk")));
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
string_right(path, strlen(EXT_PATH("dirwalk/")));
mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
}
dir_walk_free(dir_walk);
string_clear(path);
mu_check(storage_test_paths_check(paths) == false);
storage_test_paths_free(paths);
}
MU_TEST_SUITE(test_dirwalk_suite) {
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_dirs_create(storage, EXT_PATH("dirwalk"));
MU_RUN_TEST_1(test_dirwalk_full, storage);
MU_RUN_TEST_1(test_dirwalk_no_recursive, storage);
MU_RUN_TEST_1(test_dirwalk_filter, storage);
storage_simply_remove_recursive(storage, EXT_PATH("dirwalk"));
furi_record_close(RECORD_STORAGE);
}
int run_minunit_test_dirwalk() {
MU_RUN_SUITE(test_dirwalk_suite);
return MU_EXIT_CODE;
}

View File

@@ -0,0 +1,317 @@
#include "../minunit.h"
#include <furi.h>
#include <storage/storage.h>
#define STORAGE_LOCKED_FILE EXT_PATH("locked_file.test")
#define STORAGE_LOCKED_DIR STORAGE_INT_PATH_PREFIX
static void storage_file_open_lock_setup() {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
storage_simply_remove(storage, STORAGE_LOCKED_FILE);
mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_WRITE, FSOM_CREATE_NEW));
mu_check(storage_file_write(file, "0123", 4) == 4);
mu_check(storage_file_close(file));
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
}
static void storage_file_open_lock_teardown() {
Storage* storage = furi_record_open(RECORD_STORAGE);
mu_check(storage_simply_remove(storage, STORAGE_LOCKED_FILE));
furi_record_close(RECORD_STORAGE);
}
static int32_t storage_file_locker(void* ctx) {
Storage* storage = furi_record_open(RECORD_STORAGE);
FuriSemaphore* semaphore = ctx;
File* file = storage_file_alloc(storage);
furi_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING));
furi_semaphore_release(semaphore);
furi_delay_ms(1000);
furi_check(storage_file_close(file));
furi_record_close(RECORD_STORAGE);
storage_file_free(file);
return 0;
}
MU_TEST(storage_file_open_lock) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool result = false;
FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0);
File* file = storage_file_alloc(storage);
// file_locker thread start
FuriThread* locker_thread = furi_thread_alloc();
furi_thread_set_name(locker_thread, "StorageFileLocker");
furi_thread_set_stack_size(locker_thread, 2048);
furi_thread_set_context(locker_thread, semaphore);
furi_thread_set_callback(locker_thread, storage_file_locker);
furi_thread_start(locker_thread);
// wait for file lock
furi_semaphore_acquire(semaphore, FuriWaitForever);
furi_semaphore_free(semaphore);
result = storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING);
storage_file_close(file);
// file_locker thread stop
mu_check(furi_thread_join(locker_thread) == FuriStatusOk);
furi_thread_free(locker_thread);
// clean data
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
mu_assert(result, "cannot open locked file");
}
MU_TEST(storage_file_open_close) {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file;
file = storage_file_alloc(storage);
mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING));
storage_file_close(file);
storage_file_free(file);
for(size_t i = 0; i < 10; i++) {
file = storage_file_alloc(storage);
mu_check(
storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING));
storage_file_free(file);
}
furi_record_close(RECORD_STORAGE);
}
MU_TEST_SUITE(storage_file) {
storage_file_open_lock_setup();
MU_RUN_TEST(storage_file_open_close);
MU_RUN_TEST(storage_file_open_lock);
storage_file_open_lock_teardown();
}
MU_TEST(storage_dir_open_close) {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file;
file = storage_file_alloc(storage);
mu_check(storage_dir_open(file, STORAGE_LOCKED_DIR));
storage_dir_close(file);
storage_file_free(file);
for(size_t i = 0; i < 10; i++) {
file = storage_file_alloc(storage);
mu_check(storage_dir_open(file, STORAGE_LOCKED_DIR));
storage_file_free(file);
}
furi_record_close(RECORD_STORAGE);
}
static int32_t storage_dir_locker(void* ctx) {
Storage* storage = furi_record_open(RECORD_STORAGE);
FuriSemaphore* semaphore = ctx;
File* file = storage_file_alloc(storage);
furi_check(storage_dir_open(file, STORAGE_LOCKED_DIR));
furi_semaphore_release(semaphore);
furi_delay_ms(1000);
furi_check(storage_dir_close(file));
furi_record_close(RECORD_STORAGE);
storage_file_free(file);
return 0;
}
MU_TEST(storage_dir_open_lock) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool result = false;
FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0);
File* file = storage_file_alloc(storage);
// file_locker thread start
FuriThread* locker_thread = furi_thread_alloc();
furi_thread_set_name(locker_thread, "StorageDirLocker");
furi_thread_set_stack_size(locker_thread, 2048);
furi_thread_set_context(locker_thread, semaphore);
furi_thread_set_callback(locker_thread, storage_dir_locker);
furi_thread_start(locker_thread);
// wait for dir lock
furi_semaphore_acquire(semaphore, FuriWaitForever);
furi_semaphore_free(semaphore);
result = storage_dir_open(file, STORAGE_LOCKED_DIR);
storage_dir_close(file);
// file_locker thread stop
mu_check(furi_thread_join(locker_thread) == FuriStatusOk);
furi_thread_free(locker_thread);
// clean data
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
mu_assert(result, "cannot open locked dir");
}
MU_TEST_SUITE(storage_dir) {
MU_RUN_TEST(storage_dir_open_close);
MU_RUN_TEST(storage_dir_open_lock);
}
static const char* const storage_copy_test_paths[] = {
"1",
"11",
"111",
"1/2",
"1/22",
"1/222",
"11/1",
"111/2",
"111/22",
"111/22/33",
};
static const char* const storage_copy_test_files[] = {
"file.test",
"1/file.test",
"111/22/33/file.test",
};
static bool write_file_13DA(Storage* storage, const char* path) {
File* file = storage_file_alloc(storage);
bool result = false;
if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
result = storage_file_write(file, "13DA", 4) == 4;
}
storage_file_close(file);
storage_file_free(file);
return result;
}
static bool check_file_13DA(Storage* storage, const char* path) {
File* file = storage_file_alloc(storage);
bool result = false;
if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
char data[10] = {0};
result = storage_file_read(file, data, 4) == 4;
if(result) {
result = memcmp(data, "13DA", 4) == 0;
}
}
storage_file_close(file);
storage_file_free(file);
return result;
}
static void storage_dir_create(Storage* storage, const char* base) {
string_t path;
string_init(path);
storage_common_mkdir(storage, base);
for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) {
string_printf(path, "%s/%s", base, storage_copy_test_paths[i]);
storage_common_mkdir(storage, string_get_cstr(path));
}
for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) {
string_printf(path, "%s/%s", base, storage_copy_test_files[i]);
write_file_13DA(storage, string_get_cstr(path));
}
string_clear(path);
}
static void storage_dir_remove(Storage* storage, const char* base) {
storage_simply_remove_recursive(storage, base);
}
static bool storage_dir_rename_check(Storage* storage, const char* base) {
bool result = false;
string_t path;
string_init(path);
result = (storage_common_stat(storage, base, NULL) == FSE_OK);
if(result) {
for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) {
string_printf(path, "%s/%s", base, storage_copy_test_paths[i]);
result = (storage_common_stat(storage, string_get_cstr(path), NULL) == FSE_OK);
if(!result) {
break;
}
}
}
if(result) {
for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) {
string_printf(path, "%s/%s", base, storage_copy_test_files[i]);
result = check_file_13DA(storage, string_get_cstr(path));
if(!result) {
break;
}
}
}
string_clear(path);
return result;
}
MU_TEST(storage_file_rename) {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
mu_check(write_file_13DA(storage, EXT_PATH("file.old")));
mu_check(check_file_13DA(storage, EXT_PATH("file.old")));
mu_assert_int_eq(
FSE_OK, storage_common_rename(storage, EXT_PATH("file.old"), EXT_PATH("file.new")));
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("file.old"), NULL));
mu_assert_int_eq(FSE_OK, storage_common_stat(storage, EXT_PATH("file.new"), NULL));
mu_check(check_file_13DA(storage, EXT_PATH("file.new")));
mu_assert_int_eq(FSE_OK, storage_common_remove(storage, EXT_PATH("file.new")));
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
}
MU_TEST(storage_dir_rename) {
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_dir_create(storage, EXT_PATH("dir.old"));
mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.old")));
mu_assert_int_eq(
FSE_OK, storage_common_rename(storage, EXT_PATH("dir.old"), EXT_PATH("dir.new")));
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.old"), NULL));
mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.new")));
storage_dir_remove(storage, EXT_PATH("dir.new"));
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.new"), NULL));
furi_record_close(RECORD_STORAGE);
}
MU_TEST_SUITE(storage_rename) {
MU_RUN_TEST(storage_file_rename);
MU_RUN_TEST(storage_dir_rename);
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_dir_remove(storage, EXT_PATH("dir.old"));
storage_dir_remove(storage, EXT_PATH("dir.new"));
furi_record_close(RECORD_STORAGE);
}
int run_minunit_test_storage() {
MU_RUN_SUITE(storage_file);
MU_RUN_SUITE(storage_dir);
MU_RUN_SUITE(storage_rename);
return MU_EXIT_CODE;
}

View File

@@ -0,0 +1,508 @@
#include <furi.h>
#include <toolbox/stream/stream.h>
#include <toolbox/stream/string_stream.h>
#include <toolbox/stream/file_stream.h>
#include <toolbox/stream/buffered_file_stream.h>
#include <storage/storage.h>
#include "../minunit.h"
static const char* stream_test_data = "I write differently from what I speak, "
"I speak differently from what I think, "
"I think differently from the way I ought to think, "
"and so it all proceeds into deepest darkness.";
static const char* stream_test_left_data = "There are two cardinal human sins ";
static const char* stream_test_right_data =
"from which all others derive: impatience and indolence.";
MU_TEST_1(stream_composite_subtest, Stream* stream) {
const size_t data_size = 128;
uint8_t data[data_size];
string_t string_lee;
string_init_set(string_lee, "lee");
// test that stream is empty
// "" -> ""
mu_check(stream_size(stream) == 0);
mu_check(stream_eof(stream));
mu_check(stream_tell(stream) == 0);
mu_check(stream_read(stream, data, data_size) == 0);
mu_check(stream_eof(stream));
mu_check(stream_tell(stream) == 0);
// write char
// "" -> "2"
mu_check(stream_write_char(stream, '2') == 1);
mu_check(stream_size(stream) == 1);
mu_check(stream_tell(stream) == 1);
mu_check(stream_eof(stream));
// test rewind and eof
stream_rewind(stream);
mu_check(stream_size(stream) == 1);
mu_check(stream_tell(stream) == 0);
mu_check(!stream_eof(stream));
// add another char with replacement
// "2" -> "1"
mu_check(stream_write_char(stream, '1') == 1);
mu_check(stream_size(stream) == 1);
mu_check(stream_tell(stream) == 1);
mu_check(stream_eof(stream));
// write string
// "1" -> "1337_69"
mu_check(stream_write_cstring(stream, "337_69") == 6);
mu_check(stream_size(stream) == 7);
mu_check(stream_tell(stream) == 7);
mu_check(stream_eof(stream));
// read data
memset(data, 0, data_size);
stream_rewind(stream);
mu_check(stream_read(stream, data, data_size) == 7);
mu_check(strcmp((char*)data, "1337_69") == 0);
// test misc seeks
mu_check(stream_seek(stream, 2, StreamOffsetFromStart));
mu_check(stream_tell(stream) == 2);
mu_check(!stream_seek(stream, 9000, StreamOffsetFromStart));
mu_check(stream_tell(stream) == 7);
mu_check(stream_eof(stream));
mu_check(stream_seek(stream, -3, StreamOffsetFromEnd));
mu_check(stream_tell(stream) == 4);
// write string with replacemet
// "1337_69" -> "1337lee"
mu_check(stream_write_string(stream, string_lee) == 3);
mu_check(stream_size(stream) == 7);
mu_check(stream_tell(stream) == 7);
mu_check(stream_eof(stream));
// append char
// "1337lee" -> "1337leet"
mu_check(stream_write(stream, (uint8_t*)"t", 1) == 1);
mu_check(stream_size(stream) == 8);
mu_check(stream_tell(stream) == 8);
mu_check(stream_eof(stream));
// read data
memset(data, 0, data_size);
stream_rewind(stream);
mu_check(stream_read(stream, data, data_size) == 8);
mu_check(strcmp((char*)data, "1337leet") == 0);
mu_check(stream_tell(stream) == 8);
mu_check(stream_eof(stream));
// negative seek from current position -> clamp to 0
mu_check(!stream_seek(stream, -9000, StreamOffsetFromCurrent));
mu_check(stream_tell(stream) == 0);
// negative seek from start position -> clamp to 0
stream_rewind(stream);
mu_check(!stream_seek(stream, -3, StreamOffsetFromStart));
mu_check(stream_tell(stream) == 0);
// zero seek from current position -> clamp to stream size
mu_check(stream_seek(stream, 0, StreamOffsetFromEnd));
mu_check(stream_tell(stream) == 8);
// negative seek from end position -> clamp to 0
mu_check(!stream_seek(stream, -9000, StreamOffsetFromEnd));
mu_check(stream_tell(stream) == 0);
// clean stream
stream_clean(stream);
mu_check(stream_size(stream) == 0);
mu_check(stream_eof(stream));
mu_check(stream_tell(stream) == 0);
// write format
// "" -> "dio666"
mu_check(stream_write_format(stream, "%s%d", "dio", 666) == 6);
mu_check(stream_size(stream) == 6);
mu_check(stream_eof(stream));
mu_check(stream_tell(stream) == 6);
// read data
memset(data, 0, data_size);
stream_rewind(stream);
mu_check(stream_read(stream, data, data_size) == 6);
mu_check(strcmp((char*)data, "dio666") == 0);
// clean and write cstring
// "dio666" -> "" -> "1234567890"
stream_clean(stream);
mu_check(stream_write_cstring(stream, "1234567890") == 10);
// delete 4 bytes from 1 pos
// "1xxxx67890" -> "167890"
mu_check(stream_seek(stream, 1, StreamOffsetFromStart));
mu_check(stream_delete(stream, 4));
mu_assert_int_eq(6, stream_size(stream));
// read data
memset(data, 0, data_size);
stream_rewind(stream);
mu_assert_int_eq(6, stream_read(stream, data, data_size));
mu_check(strcmp((char*)data, "167890") == 0);
// write cstring
// "167890" -> "167890It Was Me, Dio!"
mu_check(stream_write_cstring(stream, "It Was Me, Dio!") == 15);
// delete 1337 bytes from 1 pos
// and check that we can delete only 20 bytes
// "1xxxxxxxxxxxxxxxxxxxx" -> "1"
mu_check(stream_seek(stream, 1, StreamOffsetFromStart));
mu_check(stream_delete(stream, 1337));
mu_assert_int_eq(1, stream_size(stream));
// read data
memset(data, 0, data_size);
stream_rewind(stream);
mu_check(stream_read(stream, data, data_size) == 1);
mu_check(strcmp((char*)data, "1") == 0);
// write cstring from 0 pos, replacing 1 byte
// "1" -> "Oh? You're roaching me?"
mu_check(stream_rewind(stream));
mu_assert_int_eq(23, stream_write_cstring(stream, "Oh? You're roaching me?"));
// insert 11 bytes to 0 pos
// "Oh? You're roaching me?" -> "Za Warudo! Oh? You're roaching me?"
mu_check(stream_rewind(stream));
mu_check(stream_insert(stream, (uint8_t*)"Za Warudo! ", 11));
mu_assert_int_eq(34, stream_size(stream));
// read data
memset(data, 0, data_size);
stream_rewind(stream);
mu_assert_int_eq(34, stream_read(stream, data, data_size));
mu_assert_string_eq("Za Warudo! Oh? You're roaching me?", (char*)data);
// insert cstring to 22 pos
// "Za Warudo! Oh? You're roaching me?" -> "Za Warudo! Oh? You're approaching me?"
mu_check(stream_seek(stream, 22, StreamOffsetFromStart));
mu_check(stream_insert_cstring(stream, "app"));
mu_assert_int_eq(37, stream_size(stream));
// read data
memset(data, 0, data_size);
stream_rewind(stream);
mu_assert_int_eq(37, stream_read(stream, data, data_size));
mu_assert_string_eq("Za Warudo! Oh? You're approaching me?", (char*)data);
// insert cstring to the end of the stream
// "Za Warudo! Oh? You're approaching me?" -> "Za Warudo! Oh? You're approaching me? It was me, Dio!"
mu_check(stream_seek(stream, 0, StreamOffsetFromEnd));
mu_check(stream_insert_cstring(stream, " It was me, Dio!"));
mu_assert_int_eq(53, stream_size(stream));
// read data
memset(data, 0, data_size);
stream_rewind(stream);
mu_assert_int_eq(53, stream_read(stream, data, data_size));
mu_assert_string_eq("Za Warudo! Oh? You're approaching me? It was me, Dio!", (char*)data);
// delete 168430090 bytes from stream
// and test that we can delete only 53
mu_check(stream_rewind(stream));
mu_check(stream_delete(stream, 0x0A0A0A0A));
mu_assert_int_eq(0, stream_size(stream));
mu_check(stream_eof(stream));
mu_assert_int_eq(0, stream_tell(stream));
// clean stream
stream_clean(stream);
mu_assert_int_eq(0, stream_size(stream));
mu_check(stream_eof(stream));
mu_assert_int_eq(0, stream_tell(stream));
// insert formated string at the end of stream
// "" -> "dio666"
mu_check(stream_insert_format(stream, "%s%d", "dio", 666));
mu_assert_int_eq(6, stream_size(stream));
mu_check(stream_eof(stream));
mu_assert_int_eq(6, stream_tell(stream));
// insert formated string at the end of stream
// "dio666" -> "dio666zlo555"
mu_check(stream_insert_format(stream, "%s%d", "zlo", 555));
mu_assert_int_eq(12, stream_size(stream));
mu_check(stream_eof(stream));
mu_assert_int_eq(12, stream_tell(stream));
// insert formated string at the 6 pos
// "dio666" -> "dio666baba13zlo555"
mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
mu_check(stream_insert_format(stream, "%s%d", "baba", 13));
mu_assert_int_eq(18, stream_size(stream));
mu_assert_int_eq(12, stream_tell(stream));
// read data
memset(data, 0, data_size);
stream_rewind(stream);
mu_assert_int_eq(18, stream_read(stream, data, data_size));
mu_assert_string_eq("dio666baba13zlo555", (char*)data);
// delete 6 chars from pos 6 and insert 1 chars
// "dio666baba13zlo555" -> "dio666xzlo555"
mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
mu_check(stream_delete_and_insert_char(stream, 6, 'x'));
mu_assert_int_eq(13, stream_size(stream));
mu_assert_int_eq(7, stream_tell(stream));
// read data
memset(data, 0, data_size);
stream_rewind(stream);
mu_check(stream_read(stream, data, data_size) == 13);
mu_assert_string_eq("dio666xzlo555", (char*)data);
// delete 9000 chars from pos 6 and insert 3 chars from string
// "dio666xzlo555" -> "dio666777"
mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
mu_check(stream_delete_and_insert_cstring(stream, 9000, "777"));
mu_assert_int_eq(9, stream_size(stream));
mu_assert_int_eq(9, stream_tell(stream));
mu_check(stream_eof(stream));
string_clear(string_lee);
}
MU_TEST(stream_composite_test) {
// test string stream
Stream* stream;
stream = string_stream_alloc();
MU_RUN_TEST_1(stream_composite_subtest, stream);
stream_free(stream);
// test file stream
Storage* storage = furi_record_open(RECORD_STORAGE);
stream = file_stream_alloc(storage);
mu_check(
file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
MU_RUN_TEST_1(stream_composite_subtest, stream);
stream_free(stream);
// test buffered file stream
stream = buffered_file_stream_alloc(storage);
mu_check(buffered_file_stream_open(
stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
MU_RUN_TEST_1(stream_composite_subtest, stream);
stream_free(stream);
furi_record_close(RECORD_STORAGE);
}
MU_TEST_1(stream_write_subtest, Stream* stream) {
mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data));
}
MU_TEST_1(stream_read_subtest, Stream* stream) {
uint8_t data[256] = {0};
mu_check(stream_rewind(stream));
mu_assert_int_eq(strlen(stream_test_data), stream_read(stream, data, 256));
mu_assert_string_eq(stream_test_data, (const char*)data);
}
MU_TEST(stream_write_read_save_load_test) {
Stream* stream_orig = string_stream_alloc();
Stream* stream_copy = string_stream_alloc();
Storage* storage = furi_record_open(RECORD_STORAGE);
// write, read
MU_RUN_TEST_1(stream_write_subtest, stream_orig);
MU_RUN_TEST_1(stream_read_subtest, stream_orig);
// copy, read
mu_assert_int_eq(strlen(stream_test_data), stream_copy_full(stream_orig, stream_copy));
MU_RUN_TEST_1(stream_read_subtest, stream_orig);
// save to file
mu_check(stream_seek(stream_orig, 0, StreamOffsetFromStart));
mu_assert_int_eq(
strlen(stream_test_data),
stream_save_to_file(stream_orig, storage, EXT_PATH("filestream.str"), FSOM_CREATE_ALWAYS));
stream_free(stream_copy);
stream_free(stream_orig);
// load from file, read
Stream* stream_new = string_stream_alloc();
mu_assert_int_eq(
strlen(stream_test_data),
stream_load_from_file(stream_new, storage, EXT_PATH("filestream.str")));
MU_RUN_TEST_1(stream_read_subtest, stream_new);
stream_free(stream_new);
furi_record_close(RECORD_STORAGE);
}
MU_TEST_1(stream_split_subtest, Stream* stream) {
stream_clean(stream);
stream_write_cstring(stream, stream_test_left_data);
stream_write_cstring(stream, stream_test_right_data);
Stream* stream_left = string_stream_alloc();
Stream* stream_right = string_stream_alloc();
mu_check(stream_seek(stream, strlen(stream_test_left_data), StreamOffsetFromStart));
mu_check(stream_split(stream, stream_left, stream_right));
uint8_t data[256] = {0};
mu_check(stream_rewind(stream_left));
mu_assert_int_eq(strlen(stream_test_left_data), stream_read(stream_left, data, 256));
mu_assert_string_eq(stream_test_left_data, (const char*)data);
mu_check(stream_rewind(stream_right));
mu_assert_int_eq(strlen(stream_test_right_data), stream_read(stream_right, data, 256));
mu_assert_string_eq(stream_test_right_data, (const char*)data);
stream_free(stream_right);
stream_free(stream_left);
}
MU_TEST(stream_split_test) {
// test string stream
Stream* stream;
stream = string_stream_alloc();
MU_RUN_TEST_1(stream_split_subtest, stream);
stream_free(stream);
// test file stream
Storage* storage = furi_record_open(RECORD_STORAGE);
stream = file_stream_alloc(storage);
mu_check(
file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
MU_RUN_TEST_1(stream_split_subtest, stream);
stream_free(stream);
// test buffered stream
stream = buffered_file_stream_alloc(storage);
mu_check(buffered_file_stream_open(
stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
MU_RUN_TEST_1(stream_split_subtest, stream);
stream_free(stream);
furi_record_close(RECORD_STORAGE);
}
MU_TEST(stream_buffered_write_after_read_test) {
const char* prefix = "I write ";
const char* substr = "Hello there";
const size_t substr_len = strlen(substr);
const size_t prefix_len = strlen(prefix);
const size_t buf_size = substr_len + 1;
char buf[buf_size];
memset(buf, 0, buf_size);
Storage* storage = furi_record_open(RECORD_STORAGE);
Stream* stream = buffered_file_stream_alloc(storage);
mu_check(buffered_file_stream_open(
stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data));
mu_check(stream_rewind(stream));
mu_assert_int_eq(prefix_len, stream_read(stream, (uint8_t*)buf, prefix_len));
mu_assert_string_eq(prefix, buf);
mu_assert_int_eq(substr_len, stream_write(stream, (uint8_t*)substr, substr_len));
mu_check(stream_seek(stream, prefix_len, StreamOffsetFromStart));
mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
mu_assert_string_eq(substr, buf);
stream_free(stream);
furi_record_close(RECORD_STORAGE);
}
MU_TEST(stream_buffered_large_file_test) {
string_t input_data;
string_t output_data;
string_init(input_data);
string_init(output_data);
Storage* storage = furi_record_open(RECORD_STORAGE);
// generate test data consisting of several identical lines
const size_t data_size = 4096;
const size_t line_size = strlen(stream_test_data);
const size_t rep_count = data_size / line_size + 1;
for(size_t i = 0; i < rep_count; ++i) {
string_cat_printf(input_data, "%s\n", stream_test_data);
}
// write test data to file
Stream* stream = buffered_file_stream_alloc(storage);
mu_check(buffered_file_stream_open(
stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
mu_assert_int_eq(0, stream_size(stream));
mu_assert_int_eq(string_size(input_data), stream_write_string(stream, input_data));
mu_assert_int_eq(string_size(input_data), stream_size(stream));
const size_t substr_start = 8;
const size_t substr_len = 11;
mu_check(stream_seek(stream, substr_start, StreamOffsetFromStart));
mu_assert_int_eq(substr_start, stream_tell(stream));
// copy one substring from test data
char test_substr[substr_len + 1];
memset(test_substr, 0, substr_len + 1);
memcpy(test_substr, stream_test_data + substr_start, substr_len);
char buf[substr_len + 1];
memset(buf, 0, substr_len + 1);
// read substring
mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
mu_assert_string_eq(test_substr, buf);
memset(buf, 0, substr_len + 1);
// forward seek to cause a cache miss
mu_check(stream_seek(
stream, (line_size + 1) * (rep_count - 1) - substr_len, StreamOffsetFromCurrent));
// read same substring from a different line
mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
mu_assert_string_eq(test_substr, buf);
memset(buf, 0, substr_len + 1);
// backward seek to cause a cache miss
mu_check(stream_seek(
stream, -((line_size + 1) * (rep_count - 1) + substr_len), StreamOffsetFromCurrent));
mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
mu_assert_string_eq(test_substr, buf);
// read the whole file
mu_check(stream_rewind(stream));
string_t tmp;
string_init(tmp);
while(stream_read_line(stream, tmp)) {
string_cat(output_data, tmp);
}
string_clear(tmp);
// check against generated data
mu_assert_int_eq(string_size(input_data), string_size(output_data));
mu_check(string_equal_p(input_data, output_data));
mu_check(stream_eof(stream));
stream_free(stream);
furi_record_close(RECORD_STORAGE);
string_clear(input_data);
string_clear(output_data);
}
MU_TEST_SUITE(stream_suite) {
MU_RUN_TEST(stream_write_read_save_load_test);
MU_RUN_TEST(stream_composite_test);
MU_RUN_TEST(stream_split_test);
MU_RUN_TEST(stream_buffered_write_after_read_test);
MU_RUN_TEST(stream_buffered_large_file_test);
}
int run_minunit_test_stream() {
MU_RUN_SUITE(stream_suite);
return MU_EXIT_CODE;
}

View File

@@ -0,0 +1,627 @@
#include <furi.h>
#include <furi_hal.h>
#include "../minunit.h"
#include <lib/subghz/receiver.h>
#include <lib/subghz/transmitter.h>
#include <lib/subghz/subghz_keystore.h>
#include <lib/subghz/subghz_file_encoder_worker.h>
#include <lib/subghz/protocols/registry.h>
#include <flipper_format/flipper_format_i.h>
#define TAG "SubGhz TEST"
#define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes")
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
#define TEST_RANDOM_COUNT_PARSE 232
#define TEST_TIMEOUT 10000
static SubGhzEnvironment* environment_handler;
static SubGhzReceiver* receiver_handler;
//static SubGhzTransmitter* transmitter_handler;
static SubGhzFileEncoderWorker* file_worker_encoder_handler;
static uint16_t subghz_test_decoder_count = 0;
static void subghz_test_rx_callback(
SubGhzReceiver* receiver,
SubGhzProtocolDecoderBase* decoder_base,
void* context) {
UNUSED(receiver);
UNUSED(context);
string_t text;
string_init(text);
subghz_protocol_decoder_base_get_string(decoder_base, text);
subghz_receiver_reset(receiver_handler);
FURI_LOG_T(TAG, "\r\n%s", string_get_cstr(text));
string_clear(text);
subghz_test_decoder_count++;
}
static void subghz_test_init(void) {
environment_handler = subghz_environment_alloc();
subghz_environment_set_came_atomo_rainbow_table_file_name(
environment_handler, CAME_ATOMO_DIR_NAME);
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment_handler, NICE_FLOR_S_DIR_NAME);
receiver_handler = subghz_receiver_alloc_init(environment_handler);
subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable);
subghz_receiver_set_rx_callback(receiver_handler, subghz_test_rx_callback, NULL);
}
static void subghz_test_deinit(void) {
subghz_receiver_free(receiver_handler);
subghz_environment_free(environment_handler);
}
static bool subghz_decoder_test(const char* path, const char* name_decoder) {
subghz_test_decoder_count = 0;
uint32_t test_start = furi_get_tick();
SubGhzProtocolDecoderBase* decoder =
subghz_receiver_search_decoder_base_by_name(receiver_handler, name_decoder);
if(decoder) {
file_worker_encoder_handler = subghz_file_encoder_worker_alloc();
if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) {
// the worker needs a file in order to open and read part of the file
furi_delay_ms(100);
LevelDuration level_duration;
while(furi_get_tick() - test_start < TEST_TIMEOUT) {
level_duration =
subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler);
if(!level_duration_is_reset(level_duration)) {
bool level = level_duration_get_level(level_duration);
uint32_t duration = level_duration_get_duration(level_duration);
// Yield, to load data inside the worker
furi_thread_yield();
decoder->protocol->decoder->feed(decoder, level, duration);
} else {
break;
}
}
furi_delay_ms(10);
}
if(subghz_file_encoder_worker_is_running(file_worker_encoder_handler)) {
subghz_file_encoder_worker_stop(file_worker_encoder_handler);
}
subghz_file_encoder_worker_free(file_worker_encoder_handler);
}
FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
if(furi_get_tick() - test_start > TEST_TIMEOUT) {
printf("\033[0;31mTest decoder %s ERROR TimeOut\033[0m\r\n", name_decoder);
return false;
} else {
return subghz_test_decoder_count ? true : false;
}
}
static bool subghz_decode_random_test(const char* path) {
subghz_test_decoder_count = 0;
subghz_receiver_reset(receiver_handler);
uint32_t test_start = furi_get_tick();
file_worker_encoder_handler = subghz_file_encoder_worker_alloc();
if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) {
// the worker needs a file in order to open and read part of the file
furi_delay_ms(100);
LevelDuration level_duration;
while(furi_get_tick() - test_start < TEST_TIMEOUT * 10) {
level_duration =
subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler);
if(!level_duration_is_reset(level_duration)) {
bool level = level_duration_get_level(level_duration);
uint32_t duration = level_duration_get_duration(level_duration);
// Yield, to load data inside the worker
furi_thread_yield();
subghz_receiver_decode(receiver_handler, level, duration);
} else {
break;
}
}
furi_delay_ms(10);
if(subghz_file_encoder_worker_is_running(file_worker_encoder_handler)) {
subghz_file_encoder_worker_stop(file_worker_encoder_handler);
}
subghz_file_encoder_worker_free(file_worker_encoder_handler);
}
FURI_LOG_D(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
if(furi_get_tick() - test_start > TEST_TIMEOUT * 10) {
printf("\033[0;31mRandom test ERROR TimeOut\033[0m\r\n");
return false;
} else if(subghz_test_decoder_count == TEST_RANDOM_COUNT_PARSE) {
return true;
} else {
return false;
}
}
static bool subghz_encoder_test(const char* path) {
subghz_test_decoder_count = 0;
uint32_t test_start = furi_get_tick();
string_t temp_str;
string_init(temp_str);
bool file_load = false;
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
do {
if(!flipper_format_file_open_existing(fff_data_file, path)) {
FURI_LOG_E(TAG, "Error open file %s", path);
break;
}
if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
FURI_LOG_E(TAG, "Missing Preset");
break;
}
if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
file_load = true;
} while(false);
if(file_load) {
SubGhzTransmitter* transmitter =
subghz_transmitter_alloc_init(environment_handler, string_get_cstr(temp_str));
subghz_transmitter_deserialize(transmitter, fff_data_file);
SubGhzProtocolDecoderBase* decoder = subghz_receiver_search_decoder_base_by_name(
receiver_handler, string_get_cstr(temp_str));
if(decoder) {
LevelDuration level_duration;
while(furi_get_tick() - test_start < TEST_TIMEOUT) {
level_duration = subghz_transmitter_yield(transmitter);
if(!level_duration_is_reset(level_duration)) {
bool level = level_duration_get_level(level_duration);
uint32_t duration = level_duration_get_duration(level_duration);
decoder->protocol->decoder->feed(decoder, level, duration);
} else {
break;
}
}
furi_delay_ms(10);
}
subghz_transmitter_free(transmitter);
}
flipper_format_free(fff_data_file);
FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
if(furi_get_tick() - test_start > TEST_TIMEOUT) {
printf("\033[0;31mTest encoder %s ERROR TimeOut\033[0m\r\n", string_get_cstr(temp_str));
subghz_test_decoder_count = 0;
}
string_clear(temp_str);
return subghz_test_decoder_count ? true : false;
}
MU_TEST(subghz_keystore_test) {
mu_assert(
subghz_environment_load_keystore(environment_handler, KEYSTORE_DIR_NAME),
"Test keystore error");
}
//test decoders
MU_TEST(subghz_decoder_came_atomo_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/came_atomo_raw.sub"), SUBGHZ_PROTOCOL_CAME_ATOMO_NAME),
"Test decoder " SUBGHZ_PROTOCOL_CAME_ATOMO_NAME " error\r\n");
}
MU_TEST(subghz_decoder_came_test) {
mu_assert(
subghz_decoder_test(EXT_PATH("unit_tests/subghz/came_raw.sub"), SUBGHZ_PROTOCOL_CAME_NAME),
"Test decoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n");
}
MU_TEST(subghz_decoder_came_twee_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/came_twee_raw.sub"), SUBGHZ_PROTOCOL_CAME_TWEE_NAME),
"Test decoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n");
}
MU_TEST(subghz_decoder_faac_slh_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/faac_slh_raw.sub"), SUBGHZ_PROTOCOL_FAAC_SLH_NAME),
"Test decoder " SUBGHZ_PROTOCOL_FAAC_SLH_NAME " error\r\n");
}
MU_TEST(subghz_decoder_gate_tx_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/gate_tx_raw.sub"), SUBGHZ_PROTOCOL_GATE_TX_NAME),
"Test decoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n");
}
MU_TEST(subghz_decoder_hormann_hsm_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/hormann_hsm_raw.sub"), SUBGHZ_PROTOCOL_HORMANN_HSM_NAME),
"Test decoder " SUBGHZ_PROTOCOL_HORMANN_HSM_NAME " error\r\n");
}
MU_TEST(subghz_decoder_ido_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/ido_117_111_raw.sub"), SUBGHZ_PROTOCOL_IDO_NAME),
"Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n");
}
MU_TEST(subghz_decoder_keelog_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/doorhan_raw.sub"), SUBGHZ_PROTOCOL_KEELOQ_NAME),
"Test decoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n");
}
MU_TEST(subghz_decoder_kia_seed_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/kia_seed_raw.sub"), SUBGHZ_PROTOCOL_KIA_NAME),
"Test decoder " SUBGHZ_PROTOCOL_KIA_NAME " error\r\n");
}
MU_TEST(subghz_decoder_nero_radio_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/nero_radio_raw.sub"), SUBGHZ_PROTOCOL_NERO_RADIO_NAME),
"Test decoder " SUBGHZ_PROTOCOL_NERO_RADIO_NAME " error\r\n");
}
MU_TEST(subghz_decoder_nero_sketch_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/nero_sketch_raw.sub"), SUBGHZ_PROTOCOL_NERO_SKETCH_NAME),
"Test decoder " SUBGHZ_PROTOCOL_NERO_SKETCH_NAME " error\r\n");
}
MU_TEST(subghz_decoder_nice_flo_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/nice_flo_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLO_NAME),
"Test decoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n");
}
MU_TEST(subghz_decoder_nice_flor_s_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/nice_flor_s_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME),
"Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n");
}
MU_TEST(subghz_decoder_princeton_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/Princeton_raw.sub"), SUBGHZ_PROTOCOL_PRINCETON_NAME),
"Test decoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n");
}
MU_TEST(subghz_decoder_scher_khan_magic_code_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/scher_khan_magic_code.sub"),
SUBGHZ_PROTOCOL_SCHER_KHAN_NAME),
"Test decoder " SUBGHZ_PROTOCOL_SCHER_KHAN_NAME " error\r\n");
}
MU_TEST(subghz_decoder_somfy_keytis_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/Somfy_keytis_raw.sub"), SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME),
"Test decoder " SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME " error\r\n");
}
MU_TEST(subghz_decoder_somfy_telis_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/somfy_telis_raw.sub"), SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME),
"Test decoder " SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME " error\r\n");
}
MU_TEST(subghz_decoder_star_line_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/cenmax_raw.sub"), SUBGHZ_PROTOCOL_STAR_LINE_NAME),
"Test decoder " SUBGHZ_PROTOCOL_STAR_LINE_NAME " error\r\n");
}
MU_TEST(subghz_decoder_linear_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/linear_raw.sub"), SUBGHZ_PROTOCOL_LINEAR_NAME),
"Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
}
MU_TEST(subghz_decoder_megacode_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/megacode_raw.sub"), SUBGHZ_PROTOCOL_MEGACODE_NAME),
"Test decoder " SUBGHZ_PROTOCOL_MEGACODE_NAME " error\r\n");
}
MU_TEST(subghz_decoder_secplus_v1_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/security_pls_1_0_raw.sub"),
SUBGHZ_PROTOCOL_SECPLUS_V1_NAME),
"Test decoder " SUBGHZ_PROTOCOL_SECPLUS_V1_NAME " error\r\n");
}
MU_TEST(subghz_decoder_secplus_v2_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/security_pls_2_0_raw.sub"),
SUBGHZ_PROTOCOL_SECPLUS_V2_NAME),
"Test decoder " SUBGHZ_PROTOCOL_SECPLUS_V2_NAME " error\r\n");
}
MU_TEST(subghz_decoder_holtek_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/holtek_raw.sub"), SUBGHZ_PROTOCOL_HOLTEK_NAME),
"Test decoder " SUBGHZ_PROTOCOL_HOLTEK_NAME " error\r\n");
}
MU_TEST(subghz_decoder_power_smart_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/power_smart_raw.sub"), SUBGHZ_PROTOCOL_POWER_SMART_NAME),
"Test decoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n");
}
MU_TEST(subghz_decoder_marantec_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/marantec_raw.sub"), SUBGHZ_PROTOCOL_MARANTEC_NAME),
"Test decoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n");
}
MU_TEST(subghz_decoder_bett_test) {
mu_assert(
subghz_decoder_test(EXT_PATH("unit_tests/subghz/bett_raw.sub"), SUBGHZ_PROTOCOL_BETT_NAME),
"Test decoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n");
}
MU_TEST(subghz_decoder_doitrand_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/doitrand_raw.sub"), SUBGHZ_PROTOCOL_DOITRAND_NAME),
"Test decoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n");
}
MU_TEST(subghz_decoder_phoenix_v2_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/phoenix_v2_raw.sub"), SUBGHZ_PROTOCOL_PHOENIX_V2_NAME),
"Test decoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n");
}
MU_TEST(subghz_decoder_honeywell_wdb_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/honeywell_wdb_raw.sub"),
SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME),
"Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
}
MU_TEST(subghz_decoder_magellen_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/magellen_raw.sub"), SUBGHZ_PROTOCOL_MAGELLEN_NAME),
"Test decoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
}
MU_TEST(subghz_decoder_intertechno_v3_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/intertechno_v3_raw.sub"),
SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME),
"Test decoder " SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME " error\r\n");
}
MU_TEST(subghz_decoder_clemsa_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/clemsa_raw.sub"), SUBGHZ_PROTOCOL_CLEMSA_NAME),
"Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
}
//test encoders
MU_TEST(subghz_encoder_princeton_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/princeton.sub")),
"Test encoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n");
}
MU_TEST(subghz_encoder_came_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/came.sub")),
"Test encoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n");
}
MU_TEST(subghz_encoder_came_twee_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/came_twee.sub")),
"Test encoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n");
}
MU_TEST(subghz_encoder_gate_tx_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/gate_tx.sub")),
"Test encoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n");
}
MU_TEST(subghz_encoder_nice_flo_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/nice_flo.sub")),
"Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n");
}
MU_TEST(subghz_encoder_keelog_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/doorhan.sub")),
"Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n");
}
MU_TEST(subghz_encoder_linear_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/linear.sub")),
"Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
}
MU_TEST(subghz_encoder_megacode_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")),
"Test encoder " SUBGHZ_PROTOCOL_MEGACODE_NAME " error\r\n");
}
MU_TEST(subghz_encoder_holtek_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/holtek.sub")),
"Test encoder " SUBGHZ_PROTOCOL_HOLTEK_NAME " error\r\n");
}
MU_TEST(subghz_encoder_secplus_v1_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/security_pls_1_0.sub")),
"Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V1_NAME " error\r\n");
}
MU_TEST(subghz_encoder_secplus_v2_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/security_pls_2_0.sub")),
"Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V2_NAME " error\r\n");
}
MU_TEST(subghz_encoder_power_smart_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/power_smart.sub")),
"Test encoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n");
}
MU_TEST(subghz_encoder_marantec_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/marantec.sub")),
"Test encoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n");
}
MU_TEST(subghz_encoder_bett_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/bett.sub")),
"Test encoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n");
}
MU_TEST(subghz_encoder_doitrand_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/doitrand.sub")),
"Test encoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n");
}
MU_TEST(subghz_encoder_phoenix_v2_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/phoenix_v2.sub")),
"Test encoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n");
}
MU_TEST(subghz_encoder_honeywell_wdb_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/honeywell_wdb.sub")),
"Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
}
MU_TEST(subghz_encoder_magellen_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellen.sub")),
"Test encoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
}
MU_TEST(subghz_encoder_intertechno_v3_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/intertechno_v3.sub")),
"Test encoder " SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME " error\r\n");
}
MU_TEST(subghz_encoder_clemsa_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/clemsa.sub")),
"Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
}
MU_TEST(subghz_random_test) {
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
}
MU_TEST_SUITE(subghz) {
subghz_test_init();
MU_RUN_TEST(subghz_keystore_test);
MU_RUN_TEST(subghz_decoder_came_atomo_test);
MU_RUN_TEST(subghz_decoder_came_test);
MU_RUN_TEST(subghz_decoder_came_twee_test);
MU_RUN_TEST(subghz_decoder_faac_slh_test);
MU_RUN_TEST(subghz_decoder_gate_tx_test);
MU_RUN_TEST(subghz_decoder_hormann_hsm_test);
MU_RUN_TEST(subghz_decoder_ido_test);
MU_RUN_TEST(subghz_decoder_keelog_test);
MU_RUN_TEST(subghz_decoder_kia_seed_test);
MU_RUN_TEST(subghz_decoder_nero_radio_test);
MU_RUN_TEST(subghz_decoder_nero_sketch_test);
MU_RUN_TEST(subghz_decoder_nice_flo_test);
MU_RUN_TEST(subghz_decoder_nice_flor_s_test);
MU_RUN_TEST(subghz_decoder_princeton_test);
MU_RUN_TEST(subghz_decoder_scher_khan_magic_code_test);
MU_RUN_TEST(subghz_decoder_somfy_keytis_test);
MU_RUN_TEST(subghz_decoder_somfy_telis_test);
MU_RUN_TEST(subghz_decoder_star_line_test);
MU_RUN_TEST(subghz_decoder_linear_test);
MU_RUN_TEST(subghz_decoder_megacode_test);
MU_RUN_TEST(subghz_decoder_secplus_v1_test);
MU_RUN_TEST(subghz_decoder_secplus_v2_test);
MU_RUN_TEST(subghz_decoder_holtek_test);
MU_RUN_TEST(subghz_decoder_power_smart_test);
MU_RUN_TEST(subghz_decoder_marantec_test);
MU_RUN_TEST(subghz_decoder_bett_test);
MU_RUN_TEST(subghz_decoder_doitrand_test);
MU_RUN_TEST(subghz_decoder_phoenix_v2_test);
MU_RUN_TEST(subghz_decoder_honeywell_wdb_test);
MU_RUN_TEST(subghz_decoder_magellen_test);
MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
MU_RUN_TEST(subghz_decoder_clemsa_test);
MU_RUN_TEST(subghz_encoder_princeton_test);
MU_RUN_TEST(subghz_encoder_came_test);
MU_RUN_TEST(subghz_encoder_came_twee_test);
MU_RUN_TEST(subghz_encoder_gate_tx_test);
MU_RUN_TEST(subghz_encoder_nice_flo_test);
MU_RUN_TEST(subghz_encoder_keelog_test);
MU_RUN_TEST(subghz_encoder_linear_test);
MU_RUN_TEST(subghz_encoder_megacode_test);
MU_RUN_TEST(subghz_encoder_holtek_test);
MU_RUN_TEST(subghz_encoder_secplus_v1_test);
MU_RUN_TEST(subghz_encoder_secplus_v2_test);
MU_RUN_TEST(subghz_encoder_power_smart_test);
MU_RUN_TEST(subghz_encoder_marantec_test);
MU_RUN_TEST(subghz_encoder_bett_test);
MU_RUN_TEST(subghz_encoder_doitrand_test);
MU_RUN_TEST(subghz_encoder_phoenix_v2_test);
MU_RUN_TEST(subghz_encoder_honeywell_wdb_test);
MU_RUN_TEST(subghz_encoder_magellen_test);
MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
MU_RUN_TEST(subghz_encoder_clemsa_test);
MU_RUN_TEST(subghz_random_test);
subghz_test_deinit();
}
int run_minunit_test_subghz() {
MU_RUN_SUITE(subghz);
return MU_EXIT_CODE;
}

View File

@@ -0,0 +1,137 @@
#include "m-string.h"
#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();
int run_minunit_test_infrared();
int run_minunit_test_rpc();
int run_minunit_test_flipper_format();
int run_minunit_test_flipper_format_string();
int run_minunit_test_stream();
int run_minunit_test_storage();
int run_minunit_test_subghz();
int run_minunit_test_dirwalk();
int run_minunit_test_protocol_dict();
int run_minunit_test_lfrfid_protocols();
int run_minunit_test_nfc();
int run_minunit_test_bit_lib();
typedef int (*UnitTestEntry)();
typedef struct {
const char* name;
const UnitTestEntry entry;
} UnitTest;
const UnitTest unit_tests[] = {
{.name = "furi", .entry = run_minunit_test_furi},
{.name = "storage", .entry = run_minunit_test_storage},
{.name = "stream", .entry = run_minunit_test_stream},
{.name = "dirwalk", .entry = run_minunit_test_dirwalk},
{.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 = "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},
};
void minunit_print_progress() {
static const char progress[] = {'\\', '|', '/', '-'};
static uint8_t progress_counter = 0;
static TickType_t last_tick = 0;
TickType_t current_tick = xTaskGetTickCount();
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 unit_tests_cli(Cli* cli, string_t args, void* context) {
UNUSED(cli);
UNUSED(args);
UNUSED(context);
uint32_t failed_tests = 0;
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: 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(string_size(args)) {
if(string_cmp_str(args, unit_tests[i].name) == 0) {
failed_tests += unit_tests[i].entry();
} else {
printf("Skipping %s\r\n", unit_tests[i].name);
}
} else {
failed_tests += unit_tests[i].entry();
}
}
printf("\r\nFailed tests: %lu\r\n", failed_tests);
// 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(failed_tests == 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() {
#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
}

View File

@@ -0,0 +1,88 @@
#include <furi.h>
#include <furi_hal.h>
#include "../minunit.h"
#include <toolbox/varint.h>
#include <toolbox/profiler.h>
MU_TEST(test_varint_basic_u) {
mu_assert_int_eq(1, varint_uint32_length(0));
mu_assert_int_eq(5, varint_uint32_length(UINT32_MAX));
uint8_t data[8] = {};
uint32_t out_value;
mu_assert_int_eq(1, varint_uint32_pack(0, data));
mu_assert_int_eq(1, varint_uint32_unpack(&out_value, data, 8));
mu_assert_int_eq(0, out_value);
mu_assert_int_eq(5, varint_uint32_pack(UINT32_MAX, data));
mu_assert_int_eq(5, varint_uint32_unpack(&out_value, data, 8));
mu_assert_int_eq(UINT32_MAX, out_value);
}
MU_TEST(test_varint_basic_i) {
mu_assert_int_eq(5, varint_int32_length(INT32_MIN / 2));
mu_assert_int_eq(1, varint_int32_length(0));
mu_assert_int_eq(5, varint_int32_length(INT32_MAX / 2));
mu_assert_int_eq(2, varint_int32_length(127));
mu_assert_int_eq(2, varint_int32_length(-127));
uint8_t data[8] = {};
int32_t out_value;
mu_assert_int_eq(1, varint_int32_pack(0, data));
mu_assert_int_eq(1, varint_int32_unpack(&out_value, data, 8));
mu_assert_int_eq(0, out_value);
mu_assert_int_eq(2, varint_int32_pack(127, data));
mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8));
mu_assert_int_eq(127, out_value);
mu_assert_int_eq(2, varint_int32_pack(-127, data));
mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8));
mu_assert_int_eq(-127, out_value);
mu_assert_int_eq(5, varint_int32_pack(INT32_MAX, data));
mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8));
mu_assert_int_eq(INT32_MAX, out_value);
mu_assert_int_eq(5, varint_int32_pack(INT32_MIN / 2 + 1, data));
mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8));
mu_assert_int_eq(INT32_MIN / 2 + 1, out_value);
}
MU_TEST(test_varint_rand_u) {
uint8_t data[8] = {};
uint32_t out_value;
for(size_t i = 0; i < 200000; i++) {
uint32_t rand_value = rand();
mu_assert_int_eq(
varint_uint32_pack(rand_value, data), varint_uint32_unpack(&out_value, data, 8));
mu_assert_int_eq(rand_value, out_value);
}
}
MU_TEST(test_varint_rand_i) {
uint8_t data[8] = {};
int32_t out_value;
for(size_t i = 0; i < 200000; i++) {
int32_t rand_value = rand() + (INT32_MIN / 2 + 1);
mu_assert_int_eq(
varint_int32_pack(rand_value, data), varint_int32_unpack(&out_value, data, 8));
mu_assert_int_eq(rand_value, out_value);
}
}
MU_TEST_SUITE(test_varint_suite) {
MU_RUN_TEST(test_varint_basic_u);
MU_RUN_TEST(test_varint_basic_i);
MU_RUN_TEST(test_varint_rand_u);
MU_RUN_TEST(test_varint_rand_i);
}
int run_minunit_test_varint() {
MU_RUN_SUITE(test_varint_suite);
return MU_EXIT_CODE;
}