[FL-2499] Folders rename fix (#1190)

* Toolbox: dir_walk concept (like os.walk)
* Storage CLI: tree command
* Storage: fix folders copying, stage 1
* UnitTest: proper delays in subghz tests
* Toolbox: dir_walk, recursive and filter options
* dir_walk: unit tests
* Merge: Fix unused param
* SubGhz: cleaned up data parsing routine
* SubGhz unit test: cleaned up logs, yield data load
* SubGhz unit test: naming

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
SG 2022-05-11 00:05:36 +10:00 committed by GitHub
parent f04d0eea96
commit fac4391af7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 822 additions and 74 deletions

View File

@ -282,7 +282,7 @@ FS_Error storage_int_restore(Storage* api, const char* dstname);
/***************** Simplified Functions ******************/
/**
* Removes a file/directory from the repository, the directory must be empty and the file/directory must not be open
* Removes a file/directory, the directory must be empty and the file/directory must not be open
* @param storage pointer to the api
* @param path
* @return true on success or if file/dir is not exist
@ -290,7 +290,7 @@ FS_Error storage_int_restore(Storage* api, const char* dstname);
bool storage_simply_remove(Storage* storage, const char* path);
/**
* Removes a file/directory from the repository, the directory can be not empty
* Recursively removes a file/directory, the directory can be not empty
* @param storage pointer to the api
* @param path
* @return true on success or if file/dir is not exist

View File

@ -4,6 +4,7 @@
#include <cli/cli.h>
#include <lib/toolbox/args.h>
#include <lib/toolbox/md5.h>
#include <lib/toolbox/dir_walk.h>
#include <storage/storage.h>
#include <storage/storage_sd_api.h>
#include <power/power_service/power.h>
@ -18,6 +19,7 @@ static void storage_cli_print_usage() {
printf("\tinfo\t - get FS info\r\n");
printf("\tformat\t - format filesystem\r\n");
printf("\tlist\t - list files and dirs\r\n");
printf("\ttree\t - list files and dirs, recursive\r\n");
printf("\tremove\t - delete the file or directory\r\n");
printf("\tread\t - read text from file and print file size and content to cli\r\n");
printf(
@ -138,6 +140,44 @@ static void storage_cli_list(Cli* cli, string_t path) {
}
}
static void storage_cli_tree(Cli* cli, string_t path) {
if(string_cmp_str(path, "/") == 0) {
string_set(path, "/int");
storage_cli_tree(cli, path);
string_set(path, "/ext");
storage_cli_tree(cli, path);
} else {
Storage* api = furi_record_open("storage");
DirWalk* dir_walk = dir_walk_alloc(api);
string_t name;
string_init(name);
if(dir_walk_open(dir_walk, string_get_cstr(path))) {
FileInfo fileinfo;
bool read_done = false;
while(dir_walk_read(dir_walk, name, &fileinfo) == DirWalkOK) {
read_done = true;
if(fileinfo.flags & FSF_DIRECTORY) {
printf("\t[D] %s\r\n", string_get_cstr(name));
} else {
printf("\t[F] %s %lub\r\n", string_get_cstr(name), (uint32_t)(fileinfo.size));
}
}
if(!read_done) {
printf("\tEmpty\r\n");
}
} else {
storage_cli_print_error(dir_walk_get_error(dir_walk));
}
string_clear(name);
dir_walk_free(dir_walk);
furi_record_close("storage");
}
}
static void storage_cli_read(Cli* cli, string_t path) {
UNUSED(cli);
Storage* api = furi_record_open("storage");
@ -474,6 +514,11 @@ void storage_cli(Cli* cli, string_t args, void* context) {
break;
}
if(string_cmp_str(cmd, "tree") == 0) {
storage_cli_tree(cli, path);
break;
}
if(string_cmp_str(cmd, "read") == 0) {
storage_cli_read(cli, path);
break;

View File

@ -4,6 +4,7 @@
#include "storage_i.h"
#include "storage_message.h"
#include <toolbox/stream/file_stream.h>
#include <toolbox/dir_walk.h>
#define MAX_NAME_LENGTH 256
@ -332,12 +333,69 @@ FS_Error storage_common_remove(Storage* storage, const char* path) {
FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path) {
FS_Error error = storage_common_copy(storage, old_path, new_path);
if(error == FSE_OK) {
error = storage_common_remove(storage, old_path);
if(storage_simply_remove_recursive(storage, old_path)) {
error = FSE_OK;
} else {
error = FSE_INTERNAL;
}
}
return error;
}
static FS_Error
storage_copy_recursive(Storage* storage, const char* old_path, const char* new_path) {
FS_Error error = storage_common_mkdir(storage, new_path);
DirWalk* dir_walk = dir_walk_alloc(storage);
string_t path;
string_t tmp_new_path;
string_t tmp_old_path;
FileInfo fileinfo;
string_init(path);
string_init(tmp_new_path);
string_init(tmp_old_path);
do {
if(error != FSE_OK) break;
if(!dir_walk_open(dir_walk, old_path)) {
error = dir_walk_get_error(dir_walk);
break;
}
while(1) {
DirWalkResult res = dir_walk_read(dir_walk, path, &fileinfo);
if(res == DirWalkError) {
error = dir_walk_get_error(dir_walk);
break;
} else if(res == DirWalkLast) {
break;
} else {
string_set(tmp_old_path, path);
string_right(path, strlen(old_path));
string_printf(tmp_new_path, "%s%s", new_path, string_get_cstr(path));
if(fileinfo.flags & FSF_DIRECTORY) {
error = storage_common_mkdir(storage, string_get_cstr(tmp_new_path));
} else {
error = storage_common_copy(
storage, string_get_cstr(tmp_old_path), string_get_cstr(tmp_new_path));
}
if(error != FSE_OK) break;
}
}
} while(false);
string_clear(tmp_new_path);
string_clear(tmp_old_path);
string_clear(path);
dir_walk_free(dir_walk);
return error;
}
FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path) {
FS_Error error;
@ -346,7 +404,7 @@ FS_Error storage_common_copy(Storage* storage, const char* old_path, const char*
if(error == FSE_OK) {
if(fileinfo.flags & FSF_DIRECTORY) {
error = storage_common_mkdir(storage, new_path);
error = storage_copy_recursive(storage, old_path, new_path);
} else {
Stream* stream_from = file_stream_alloc(storage);
Stream* stream_to = file_stream_alloc(storage);

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/dirwalk"));
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
string_right(path, strlen("/ext/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/dirwalk"));
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
string_right(path, strlen("/ext/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/dirwalk"));
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
string_right(path, strlen("/ext/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("storage");
storage_dirs_create(storage, "/ext/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/dirwalk");
furi_record_close("storage");
}
int run_minunit_test_dirwalk() {
MU_RUN_SUITE(test_dirwalk_suite);
return MU_EXIT_CODE;
}

View File

@ -164,8 +164,153 @@ MU_TEST_SUITE(storage_dir) {
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("storage");
File* file = storage_file_alloc(storage);
mu_check(write_file_13DA(storage, "/ext/file.old"));
mu_check(check_file_13DA(storage, "/ext/file.old"));
mu_assert_int_eq(FSE_OK, storage_common_rename(storage, "/ext/file.old", "/ext/file.new"));
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, "/ext/file.old", NULL));
mu_assert_int_eq(FSE_OK, storage_common_stat(storage, "/ext/file.new", NULL));
mu_check(check_file_13DA(storage, "/ext/file.new"));
mu_assert_int_eq(FSE_OK, storage_common_remove(storage, "/ext/file.new"));
storage_file_free(file);
furi_record_close("storage");
}
MU_TEST(storage_dir_rename) {
Storage* storage = furi_record_open("storage");
storage_dir_create(storage, "/ext/dir.old");
mu_check(storage_dir_rename_check(storage, "/ext/dir.old"));
mu_assert_int_eq(FSE_OK, storage_common_rename(storage, "/ext/dir.old", "/ext/dir.new"));
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, "/ext/dir.old", NULL));
mu_check(storage_dir_rename_check(storage, "/ext/dir.new"));
storage_dir_remove(storage, "/ext/dir.new");
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, "/ext/dir.new", NULL));
furi_record_close("storage");
}
MU_TEST_SUITE(storage_rename) {
MU_RUN_TEST(storage_file_rename);
MU_RUN_TEST(storage_dir_rename);
Storage* storage = furi_record_open("storage");
storage_dir_remove(storage, "/ext/dir.old");
storage_dir_remove(storage, "/ext/dir.new");
furi_record_close("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

@ -32,7 +32,7 @@ static void subghz_test_rx_callback(
string_init(text);
subghz_protocol_decoder_base_get_string(decoder_base, text);
subghz_receiver_reset(receiver_handler);
FURI_LOG_I(TAG, "\r\n%s", string_get_cstr(text));
FURI_LOG_T(TAG, "\r\n%s", string_get_cstr(text));
string_clear(text);
subghz_test_decoder_count++;
}
@ -54,7 +54,7 @@ static void subghz_test_deinit(void) {
subghz_environment_free(environment_handler);
}
static bool subghz_decode_test(const char* path, const char* name_decoder) {
static bool subghz_decoder_test(const char* path, const char* name_decoder) {
subghz_test_decoder_count = 0;
uint32_t test_start = furi_hal_get_tick();
@ -69,13 +69,13 @@ static bool subghz_decode_test(const char* path, const char* name_decoder) {
LevelDuration level_duration;
while(furi_hal_get_tick() - test_start < TEST_TIMEOUT) {
furi_hal_delay_us(
500); //you need to have time to read from the file from the SD card
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
osThreadYield();
decoder->protocol->decoder->feed(decoder, level, duration);
} else {
break;
@ -88,7 +88,7 @@ static bool subghz_decode_test(const char* path, const char* name_decoder) {
}
subghz_file_encoder_worker_free(file_worker_encoder_handler);
}
FURI_LOG_I(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
if(furi_hal_get_tick() - test_start > TEST_TIMEOUT) {
printf("\033[0;31mTest decoder %s ERROR TimeOut\033[0m\r\n", name_decoder);
return false;
@ -97,7 +97,7 @@ static bool subghz_decode_test(const char* path, const char* name_decoder) {
}
}
static bool subghz_decode_ramdom_test(const char* path) {
static bool subghz_decode_random_test(const char* path) {
subghz_test_decoder_count = 0;
subghz_receiver_reset(receiver_handler);
uint32_t test_start = furi_hal_get_tick();
@ -109,12 +109,13 @@ static bool subghz_decode_ramdom_test(const char* path) {
LevelDuration level_duration;
while(furi_hal_get_tick() - test_start < TEST_TIMEOUT * 10) {
furi_hal_delay_us(500); //you need to have time to read from the file from the SD card
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
osThreadYield();
subghz_receiver_decode(receiver_handler, level, duration);
} else {
break;
@ -126,7 +127,7 @@ static bool subghz_decode_ramdom_test(const char* path) {
}
subghz_file_encoder_worker_free(file_worker_encoder_handler);
}
FURI_LOG_I(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
if(furi_hal_get_tick() - test_start > TEST_TIMEOUT * 10) {
printf("\033[0;31mRandom test ERROR TimeOut\033[0m\r\n");
return false;
@ -137,7 +138,7 @@ static bool subghz_decode_ramdom_test(const char* path) {
}
}
static bool subghz_ecode_test(const char* path) {
static bool subghz_encoder_test(const char* path) {
subghz_test_decoder_count = 0;
uint32_t test_start = furi_hal_get_tick();
string_t temp_str;
@ -189,7 +190,7 @@ static bool subghz_ecode_test(const char* path) {
subghz_transmitter_free(transmitter);
}
flipper_format_free(fff_data_file);
FURI_LOG_I(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
if(furi_hal_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;
@ -207,167 +208,166 @@ MU_TEST(subghz_keystore_test) {
MU_TEST(subghz_decoder_came_atomo_test) {
mu_assert(
subghz_decode_test(
subghz_decoder_test(
"/ext/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_decode_test("/ext/unit_tests/subghz/came_raw.sub", SUBGHZ_PROTOCOL_CAME_NAME),
subghz_decoder_test("/ext/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_decode_test(
subghz_decoder_test(
"/ext/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_decode_test(
subghz_decoder_test(
"/ext/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_decode_test("/ext/unit_tests/subghz/gate_tx_raw.sub", SUBGHZ_PROTOCOL_GATE_TX_NAME),
subghz_decoder_test("/ext/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_decode_test(
subghz_decoder_test(
"/ext/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_decode_test("/ext/unit_tests/subghz/ido_117_111_raw.sub", SUBGHZ_PROTOCOL_IDO_NAME),
subghz_decoder_test("/ext/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_decode_test("/ext/unit_tests/subghz/doorhan_raw.sub", SUBGHZ_PROTOCOL_KEELOQ_NAME),
subghz_decoder_test("/ext/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_decode_test("/ext/unit_tests/subghz/kia_seed_raw.sub", SUBGHZ_PROTOCOL_KIA_NAME),
subghz_decoder_test("/ext/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_decode_test(
subghz_decoder_test(
"/ext/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_decode_test(
subghz_decoder_test(
"/ext/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_decode_test(
subghz_decoder_test(
"/ext/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_decode_test(
subghz_decoder_test(
"/ext/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_decode_test(
subghz_decoder_test(
"/ext/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_decode_test(
subghz_decoder_test(
"/ext/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_decode_test(
subghz_decoder_test(
"/ext/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_decode_test(
subghz_decoder_test(
"/ext/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_decode_test("/ext/unit_tests/subghz/cenmax_raw.sub", SUBGHZ_PROTOCOL_STAR_LINE_NAME),
subghz_decoder_test(
"/ext/unit_tests/subghz/cenmax_raw.sub", SUBGHZ_PROTOCOL_STAR_LINE_NAME),
"Test decoder " SUBGHZ_PROTOCOL_STAR_LINE_NAME " error\r\n");
}
MU_TEST(subghz_ecoder_princeton_test) {
MU_TEST(subghz_encoder_princeton_test) {
mu_assert(
subghz_ecode_test("/ext/unit_tests/subghz/princeton.sub"),
subghz_encoder_test("/ext/unit_tests/subghz/princeton.sub"),
"Test encoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n");
}
MU_TEST(subghz_ecoder_came_test) {
MU_TEST(subghz_encoder_came_test) {
mu_assert(
subghz_ecode_test("/ext/unit_tests/subghz/came.sub"),
subghz_encoder_test("/ext/unit_tests/subghz/came.sub"),
"Test encoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n");
}
MU_TEST(subghz_ecoder_came_twee_test) {
MU_TEST(subghz_encoder_came_twee_test) {
mu_assert(
subghz_ecode_test("/ext/unit_tests/subghz/came_twee.sub"),
subghz_encoder_test("/ext/unit_tests/subghz/came_twee.sub"),
"Test encoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n");
}
MU_TEST(subghz_ecoder_gate_tx_test) {
MU_TEST(subghz_encoder_gate_tx_test) {
mu_assert(
subghz_ecode_test("/ext/unit_tests/subghz/gate_tx.sub"),
subghz_encoder_test("/ext/unit_tests/subghz/gate_tx.sub"),
"Test encoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n");
}
MU_TEST(subghz_ecoder_nice_flo_test) {
MU_TEST(subghz_encoder_nice_flo_test) {
mu_assert(
subghz_ecode_test("/ext/unit_tests/subghz/nice_flo.sub"),
subghz_encoder_test("/ext/unit_tests/subghz/nice_flo.sub"),
"Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n");
}
MU_TEST(subghz_ecoder_keelog_test) {
MU_TEST(subghz_encoder_keelog_test) {
mu_assert(
subghz_ecode_test("/ext/unit_tests/subghz/doorhan.sub"),
subghz_encoder_test("/ext/unit_tests/subghz/doorhan.sub"),
"Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n");
}
MU_TEST(subghz_random_test) {
mu_assert(subghz_decode_ramdom_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
}
MU_TEST_SUITE(subghz) {
//MU_SUITE_CONFIGURE(&subghz_test_init, &subghz_test_deinit);
subghz_test_init();
MU_RUN_TEST(subghz_keystore_test);
@ -390,12 +390,12 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_somfy_telis_test);
MU_RUN_TEST(subghz_decoder_star_line_test);
MU_RUN_TEST(subghz_ecoder_princeton_test);
MU_RUN_TEST(subghz_ecoder_came_test);
MU_RUN_TEST(subghz_ecoder_came_twee_test);
MU_RUN_TEST(subghz_ecoder_gate_tx_test);
MU_RUN_TEST(subghz_ecoder_nice_flo_test);
MU_RUN_TEST(subghz_ecoder_keelog_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_random_test);
subghz_test_deinit();

View File

@ -18,6 +18,7 @@ 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();
void minunit_print_progress(void) {
static char progress[] = {'\\', '|', '/', '-'};
@ -60,6 +61,7 @@ void unit_tests_cli(Cli* cli, string_t args, void* context) {
test_result |= run_minunit();
test_result |= run_minunit_test_storage();
test_result |= run_minunit_test_stream();
test_result |= run_minunit_test_dirwalk();
test_result |= run_minunit_test_flipper_format();
test_result |= run_minunit_test_flipper_format_string();
test_result |= run_minunit_test_infrared_decoder_encoder();

View File

@ -54,27 +54,24 @@ void subghz_file_encoder_worker_add_level_duration(
}
}
bool subghz_file_encoder_worker_data_parse(
SubGhzFileEncoderWorker* instance,
const char* strStart,
size_t len) {
bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) {
char* str1;
size_t ind_start = (size_t)strStart; //store the start address of the beginning of the line
bool res = false;
// Line sample: "RAW_Data: -1, 2, -2..."
// Look for a key in the line
str1 = strstr(strStart, "RAW_Data: ");
str1 = strstr(
strStart, "RAW_Data: "); //looking for the beginning of the desired title in the line
if(str1 != NULL) {
str1 = strchr(
str1,
' '); //if found, shift the pointer by 1 element per line "RAW_Data: -1, 2, -2..."
while(
strchr(str1, ' ') != NULL &&
((size_t)str1 <
(len +
ind_start))) { //check that there is still an element in the line and that it has not gone beyond the line
// Skip key
str1 = strchr(str1, ' ');
str1 += 1; //if found, shift the pointer by next element per line
// Check that there is still an element in the line
while(strchr(str1, ' ') != NULL) {
str1 = strchr(str1, ' ');
// Skip space
str1 += 1;
subghz_file_encoder_worker_add_level_duration(instance, atoi(str1));
}
res = true;
@ -143,9 +140,7 @@ static int32_t subghz_file_encoder_worker_thread(void* context) {
if(stream_read_line(stream, instance->str_data)) {
string_strim(instance->str_data);
if(!subghz_file_encoder_worker_data_parse(
instance,
string_get_cstr(instance->str_data),
strlen(string_get_cstr(instance->str_data)))) {
instance, string_get_cstr(instance->str_data))) {
//to stop DMA correctly
subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);

152
lib/toolbox/dir_walk.c Normal file
View File

@ -0,0 +1,152 @@
#include "dir_walk.h"
#include <m-list.h>
LIST_DEF(DirIndexList, uint32_t);
struct DirWalk {
File* file;
string_t path;
DirIndexList_t index_list;
uint32_t current_index;
bool recursive;
DirWalkFilterCb filter_cb;
void* filter_context;
};
DirWalk* dir_walk_alloc(Storage* storage) {
DirWalk* dir_walk = malloc(sizeof(DirWalk));
string_init(dir_walk->path);
dir_walk->file = storage_file_alloc(storage);
DirIndexList_init(dir_walk->index_list);
dir_walk->recursive = true;
dir_walk->filter_cb = NULL;
return dir_walk;
}
void dir_walk_free(DirWalk* dir_walk) {
storage_file_free(dir_walk->file);
string_clear(dir_walk->path);
DirIndexList_clear(dir_walk->index_list);
free(dir_walk);
}
void dir_walk_set_recursive(DirWalk* dir_walk, bool recursive) {
dir_walk->recursive = recursive;
}
void dir_walk_set_filter_cb(DirWalk* dir_walk, DirWalkFilterCb cb, void* context) {
dir_walk->filter_cb = cb;
dir_walk->filter_context = context;
}
bool dir_walk_open(DirWalk* dir_walk, const char* path) {
string_set(dir_walk->path, path);
dir_walk->current_index = 0;
return storage_dir_open(dir_walk->file, path);
}
static bool dir_walk_filter(DirWalk* dir_walk, const char* name, FileInfo* fileinfo) {
if(dir_walk->filter_cb) {
return dir_walk->filter_cb(name, fileinfo, dir_walk->filter_context);
} else {
return true;
}
}
static DirWalkResult dir_walk_iter(DirWalk* dir_walk, string_t return_path, FileInfo* fileinfo) {
DirWalkResult result = DirWalkError;
char* name = malloc(256);
FileInfo info;
bool end = false;
while(!end) {
storage_dir_read(dir_walk->file, &info, name, 255);
if(storage_file_get_error(dir_walk->file) == FSE_OK) {
result = DirWalkOK;
dir_walk->current_index++;
if(dir_walk_filter(dir_walk, name, &info)) {
if(return_path != NULL) {
string_printf(return_path, "%s/%s", string_get_cstr(dir_walk->path), name);
}
if(fileinfo != NULL) {
memcpy(fileinfo, &info, sizeof(FileInfo));
}
end = true;
}
if((info.flags & FSF_DIRECTORY) && dir_walk->recursive) {
// step into
DirIndexList_push_back(dir_walk->index_list, dir_walk->current_index);
dir_walk->current_index = 0;
storage_dir_close(dir_walk->file);
string_cat_printf(dir_walk->path, "/%s", name);
storage_dir_open(dir_walk->file, string_get_cstr(dir_walk->path));
}
} else if(storage_file_get_error(dir_walk->file) == FSE_NOT_EXIST) {
if(DirIndexList_size(dir_walk->index_list) == 0) {
// last
result = DirWalkLast;
end = true;
} else {
// step out
uint32_t index;
DirIndexList_pop_back(&index, dir_walk->index_list);
dir_walk->current_index = 0;
storage_dir_close(dir_walk->file);
size_t last_char = string_search_rchar(dir_walk->path, '/');
if(last_char != STRING_FAILURE) {
string_left(dir_walk->path, last_char);
}
storage_dir_open(dir_walk->file, string_get_cstr(dir_walk->path));
// rewind
while(true) {
if(index == dir_walk->current_index) {
result = DirWalkOK;
break;
}
if(!storage_dir_read(dir_walk->file, &info, name, 255)) {
result = DirWalkError;
end = true;
break;
}
dir_walk->current_index++;
}
}
} else {
result = DirWalkError;
end = true;
}
}
free(name);
return result;
}
FS_Error dir_walk_get_error(DirWalk* dir_walk) {
return storage_file_get_error(dir_walk->file);
}
DirWalkResult dir_walk_read(DirWalk* dir_walk, string_t return_path, FileInfo* fileinfo) {
return dir_walk_iter(dir_walk, return_path, fileinfo);
}
void dir_walk_close(DirWalk* dir_walk) {
if(storage_file_is_open(dir_walk->file)) {
storage_dir_close(dir_walk->file);
}
DirIndexList_reset(dir_walk->index_list);
string_reset(dir_walk->path);
dir_walk->current_index = 0;
}

79
lib/toolbox/dir_walk.h Normal file
View File

@ -0,0 +1,79 @@
#pragma once
#include <storage/storage.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct DirWalk DirWalk;
typedef enum {
DirWalkOK, /**< OK */
DirWalkError, /**< Error */
DirWalkLast, /**< Last element */
} DirWalkResult;
typedef bool (*DirWalkFilterCb)(const char* name, FileInfo* fileinfo, void* ctx);
/**
* Allocate DirWalk
* @param storage
* @return DirWalk*
*/
DirWalk* dir_walk_alloc(Storage* storage);
/**
* Free DirWalk
* @param dir_walk
*/
void dir_walk_free(DirWalk* dir_walk);
/**
* Set recursive mode (true by default)
* @param dir_walk
* @param recursive
*/
void dir_walk_set_recursive(DirWalk* dir_walk, bool recursive);
/**
* Set filter callback (Should return true if the data is valid)
* @param dir_walk
* @param cb
* @param context
*/
void dir_walk_set_filter_cb(DirWalk* dir_walk, DirWalkFilterCb cb, void* context);
/**
* Open directory
* @param dir_walk
* @param path
* @return true
* @return false
*/
bool dir_walk_open(DirWalk* dir_walk, const char* path);
/**
* Get error id
* @param dir_walk
* @return FS_Error
*/
FS_Error dir_walk_get_error(DirWalk* dir_walk);
/**
* Read next element from directory
* @param dir_walk
* @param return_path
* @param fileinfo
* @return DirWalkResult
*/
DirWalkResult dir_walk_read(DirWalk* dir_walk, string_t return_path, FileInfo* fileinfo);
/**
* Close directory
* @param dir_walk
*/
void dir_walk_close(DirWalk* dir_walk);
#ifdef __cplusplus
}
#endif