[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:
		
							parent
							
								
									f04d0eea96
								
							
						
					
					
						commit
						fac4391af7
					
				| @ -282,7 +282,7 @@ FS_Error storage_int_restore(Storage* api, const char* dstname); | |||||||
| /***************** Simplified Functions ******************/ | /***************** 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 storage pointer to the api | ||||||
|  * @param path  |  * @param path  | ||||||
|  * @return true on success or if file/dir is not exist |  * @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); | 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 storage pointer to the api | ||||||
|  * @param path |  * @param path | ||||||
|  * @return true on success or if file/dir is not exist |  * @return true on success or if file/dir is not exist | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ | |||||||
| #include <cli/cli.h> | #include <cli/cli.h> | ||||||
| #include <lib/toolbox/args.h> | #include <lib/toolbox/args.h> | ||||||
| #include <lib/toolbox/md5.h> | #include <lib/toolbox/md5.h> | ||||||
|  | #include <lib/toolbox/dir_walk.h> | ||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
| #include <storage/storage_sd_api.h> | #include <storage/storage_sd_api.h> | ||||||
| #include <power/power_service/power.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("\tinfo\t - get FS info\r\n"); | ||||||
|     printf("\tformat\t - format filesystem\r\n"); |     printf("\tformat\t - format filesystem\r\n"); | ||||||
|     printf("\tlist\t - list files and dirs\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("\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("\tread\t - read text from file and print file size and content to cli\r\n"); | ||||||
|     printf( |     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) { | static void storage_cli_read(Cli* cli, string_t path) { | ||||||
|     UNUSED(cli); |     UNUSED(cli); | ||||||
|     Storage* api = furi_record_open("storage"); |     Storage* api = furi_record_open("storage"); | ||||||
| @ -474,6 +514,11 @@ void storage_cli(Cli* cli, string_t args, void* context) { | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if(string_cmp_str(cmd, "tree") == 0) { | ||||||
|  |             storage_cli_tree(cli, path); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         if(string_cmp_str(cmd, "read") == 0) { |         if(string_cmp_str(cmd, "read") == 0) { | ||||||
|             storage_cli_read(cli, path); |             storage_cli_read(cli, path); | ||||||
|             break; |             break; | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ | |||||||
| #include "storage_i.h" | #include "storage_i.h" | ||||||
| #include "storage_message.h" | #include "storage_message.h" | ||||||
| #include <toolbox/stream/file_stream.h> | #include <toolbox/stream/file_stream.h> | ||||||
|  | #include <toolbox/dir_walk.h> | ||||||
| 
 | 
 | ||||||
| #define MAX_NAME_LENGTH 256 | #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 storage_common_rename(Storage* storage, const char* old_path, const char* new_path) { | ||||||
|     FS_Error error = storage_common_copy(storage, old_path, new_path); |     FS_Error error = storage_common_copy(storage, old_path, new_path); | ||||||
|     if(error == FSE_OK) { |     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; |     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 storage_common_copy(Storage* storage, const char* old_path, const char* new_path) { | ||||||
|     FS_Error error; |     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(error == FSE_OK) { | ||||||
|         if(fileinfo.flags & FSF_DIRECTORY) { |         if(fileinfo.flags & FSF_DIRECTORY) { | ||||||
|             error = storage_common_mkdir(storage, new_path); |             error = storage_copy_recursive(storage, old_path, new_path); | ||||||
|         } else { |         } else { | ||||||
|             Stream* stream_from = file_stream_alloc(storage); |             Stream* stream_from = file_stream_alloc(storage); | ||||||
|             Stream* stream_to = file_stream_alloc(storage); |             Stream* stream_to = file_stream_alloc(storage); | ||||||
|  | |||||||
							
								
								
									
										272
									
								
								applications/unit_tests/storage/dirwalk_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								applications/unit_tests/storage/dirwalk_test.c
									
									
									
									
									
										Normal 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; | ||||||
|  | } | ||||||
| @ -164,8 +164,153 @@ MU_TEST_SUITE(storage_dir) { | |||||||
|     MU_RUN_TEST(storage_dir_open_lock); |     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() { | int run_minunit_test_storage() { | ||||||
|     MU_RUN_SUITE(storage_file); |     MU_RUN_SUITE(storage_file); | ||||||
|     MU_RUN_SUITE(storage_dir); |     MU_RUN_SUITE(storage_dir); | ||||||
|  |     MU_RUN_SUITE(storage_rename); | ||||||
|     return MU_EXIT_CODE; |     return MU_EXIT_CODE; | ||||||
| } | } | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ static void subghz_test_rx_callback( | |||||||
|     string_init(text); |     string_init(text); | ||||||
|     subghz_protocol_decoder_base_get_string(decoder_base, text); |     subghz_protocol_decoder_base_get_string(decoder_base, text); | ||||||
|     subghz_receiver_reset(receiver_handler); |     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); |     string_clear(text); | ||||||
|     subghz_test_decoder_count++; |     subghz_test_decoder_count++; | ||||||
| } | } | ||||||
| @ -54,7 +54,7 @@ static void subghz_test_deinit(void) { | |||||||
|     subghz_environment_free(environment_handler); |     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; |     subghz_test_decoder_count = 0; | ||||||
|     uint32_t test_start = furi_hal_get_tick(); |     uint32_t test_start = furi_hal_get_tick(); | ||||||
| 
 | 
 | ||||||
| @ -64,18 +64,18 @@ static bool subghz_decode_test(const char* path, const char* name_decoder) { | |||||||
|     if(decoder) { |     if(decoder) { | ||||||
|         file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); |         file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); | ||||||
|         if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) { |         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
 |             // the worker needs a file in order to open and read part of the file
 | ||||||
|             osDelay(100); |             osDelay(100); | ||||||
| 
 | 
 | ||||||
|             LevelDuration level_duration; |             LevelDuration level_duration; | ||||||
|             while(furi_hal_get_tick() - test_start < TEST_TIMEOUT) { |             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 = |                 level_duration = | ||||||
|                     subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler); |                     subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler); | ||||||
|                 if(!level_duration_is_reset(level_duration)) { |                 if(!level_duration_is_reset(level_duration)) { | ||||||
|                     bool level = level_duration_get_level(level_duration); |                     bool level = level_duration_get_level(level_duration); | ||||||
|                     uint32_t duration = level_duration_get_duration(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); |                     decoder->protocol->decoder->feed(decoder, level, duration); | ||||||
|                 } else { |                 } else { | ||||||
|                     break; |                     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); |         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) { |     if(furi_hal_get_tick() - test_start > TEST_TIMEOUT) { | ||||||
|         printf("\033[0;31mTest decoder %s ERROR TimeOut\033[0m\r\n", name_decoder); |         printf("\033[0;31mTest decoder %s ERROR TimeOut\033[0m\r\n", name_decoder); | ||||||
|         return false; |         return false; | ||||||
| @ -97,24 +97,25 @@ 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_test_decoder_count = 0; | ||||||
|     subghz_receiver_reset(receiver_handler); |     subghz_receiver_reset(receiver_handler); | ||||||
|     uint32_t test_start = furi_hal_get_tick(); |     uint32_t test_start = furi_hal_get_tick(); | ||||||
| 
 | 
 | ||||||
|     file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); |     file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); | ||||||
|     if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) { |     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
 |         // the worker needs a file in order to open and read part of the file
 | ||||||
|         osDelay(100); |         osDelay(100); | ||||||
| 
 | 
 | ||||||
|         LevelDuration level_duration; |         LevelDuration level_duration; | ||||||
|         while(furi_hal_get_tick() - test_start < TEST_TIMEOUT * 10) { |         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 = |             level_duration = | ||||||
|                 subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler); |                 subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler); | ||||||
|             if(!level_duration_is_reset(level_duration)) { |             if(!level_duration_is_reset(level_duration)) { | ||||||
|                 bool level = level_duration_get_level(level_duration); |                 bool level = level_duration_get_level(level_duration); | ||||||
|                 uint32_t duration = level_duration_get_duration(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); |                 subghz_receiver_decode(receiver_handler, level, duration); | ||||||
|             } else { |             } else { | ||||||
|                 break; |                 break; | ||||||
| @ -126,7 +127,7 @@ static bool subghz_decode_ramdom_test(const char* path) { | |||||||
|         } |         } | ||||||
|         subghz_file_encoder_worker_free(file_worker_encoder_handler); |         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) { |     if(furi_hal_get_tick() - test_start > TEST_TIMEOUT * 10) { | ||||||
|         printf("\033[0;31mRandom test ERROR TimeOut\033[0m\r\n"); |         printf("\033[0;31mRandom test ERROR TimeOut\033[0m\r\n"); | ||||||
|         return false; |         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; |     subghz_test_decoder_count = 0; | ||||||
|     uint32_t test_start = furi_hal_get_tick(); |     uint32_t test_start = furi_hal_get_tick(); | ||||||
|     string_t temp_str; |     string_t temp_str; | ||||||
| @ -189,7 +190,7 @@ static bool subghz_ecode_test(const char* path) { | |||||||
|         subghz_transmitter_free(transmitter); |         subghz_transmitter_free(transmitter); | ||||||
|     } |     } | ||||||
|     flipper_format_free(fff_data_file); |     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) { |     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)); |         printf("\033[0;31mTest encoder %s ERROR TimeOut\033[0m\r\n", string_get_cstr(temp_str)); | ||||||
|         subghz_test_decoder_count = 0; |         subghz_test_decoder_count = 0; | ||||||
| @ -207,167 +208,166 @@ MU_TEST(subghz_keystore_test) { | |||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_came_atomo_test) { | MU_TEST(subghz_decoder_came_atomo_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decode_test( |         subghz_decoder_test( | ||||||
|             "/ext/unit_tests/subghz/came_atomo_raw.sub", SUBGHZ_PROTOCOL_CAME_ATOMO_NAME), |             "/ext/unit_tests/subghz/came_atomo_raw.sub", SUBGHZ_PROTOCOL_CAME_ATOMO_NAME), | ||||||
|         "Test decoder " SUBGHZ_PROTOCOL_CAME_ATOMO_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_CAME_ATOMO_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_came_test) { | MU_TEST(subghz_decoder_came_test) { | ||||||
|     mu_assert( |     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"); |         "Test decoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_came_twee_test) { | MU_TEST(subghz_decoder_came_twee_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decode_test( |         subghz_decoder_test( | ||||||
|             "/ext/unit_tests/subghz/came_twee_raw.sub", SUBGHZ_PROTOCOL_CAME_TWEE_NAME), |             "/ext/unit_tests/subghz/came_twee_raw.sub", SUBGHZ_PROTOCOL_CAME_TWEE_NAME), | ||||||
|         "Test decoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_faac_slh_test) { | MU_TEST(subghz_decoder_faac_slh_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decode_test( |         subghz_decoder_test( | ||||||
|             "/ext/unit_tests/subghz/faac_slh_raw.sub", SUBGHZ_PROTOCOL_FAAC_SLH_NAME), |             "/ext/unit_tests/subghz/faac_slh_raw.sub", SUBGHZ_PROTOCOL_FAAC_SLH_NAME), | ||||||
|         "Test decoder " SUBGHZ_PROTOCOL_FAAC_SLH_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_FAAC_SLH_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_gate_tx_test) { | MU_TEST(subghz_decoder_gate_tx_test) { | ||||||
|     mu_assert( |     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"); |         "Test decoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_hormann_hsm_test) { | MU_TEST(subghz_decoder_hormann_hsm_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decode_test( |         subghz_decoder_test( | ||||||
|             "/ext/unit_tests/subghz/hormann_hsm_raw.sub", SUBGHZ_PROTOCOL_HORMANN_HSM_NAME), |             "/ext/unit_tests/subghz/hormann_hsm_raw.sub", SUBGHZ_PROTOCOL_HORMANN_HSM_NAME), | ||||||
|         "Test decoder " SUBGHZ_PROTOCOL_HORMANN_HSM_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_HORMANN_HSM_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_ido_test) { | MU_TEST(subghz_decoder_ido_test) { | ||||||
|     mu_assert( |     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"); |         "Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_keelog_test) { | MU_TEST(subghz_decoder_keelog_test) { | ||||||
|     mu_assert( |     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"); |         "Test decoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_kia_seed_test) { | MU_TEST(subghz_decoder_kia_seed_test) { | ||||||
|     mu_assert( |     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"); |         "Test decoder " SUBGHZ_PROTOCOL_KIA_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_nero_radio_test) { | MU_TEST(subghz_decoder_nero_radio_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decode_test( |         subghz_decoder_test( | ||||||
|             "/ext/unit_tests/subghz/nero_radio_raw.sub", SUBGHZ_PROTOCOL_NERO_RADIO_NAME), |             "/ext/unit_tests/subghz/nero_radio_raw.sub", SUBGHZ_PROTOCOL_NERO_RADIO_NAME), | ||||||
|         "Test decoder " SUBGHZ_PROTOCOL_NERO_RADIO_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_NERO_RADIO_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_nero_sketch_test) { | MU_TEST(subghz_decoder_nero_sketch_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decode_test( |         subghz_decoder_test( | ||||||
|             "/ext/unit_tests/subghz/nero_sketch_raw.sub", SUBGHZ_PROTOCOL_NERO_SKETCH_NAME), |             "/ext/unit_tests/subghz/nero_sketch_raw.sub", SUBGHZ_PROTOCOL_NERO_SKETCH_NAME), | ||||||
|         "Test decoder " SUBGHZ_PROTOCOL_NERO_SKETCH_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_NERO_SKETCH_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_nice_flo_test) { | MU_TEST(subghz_decoder_nice_flo_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decode_test( |         subghz_decoder_test( | ||||||
|             "/ext/unit_tests/subghz/nice_flo_raw.sub", SUBGHZ_PROTOCOL_NICE_FLO_NAME), |             "/ext/unit_tests/subghz/nice_flo_raw.sub", SUBGHZ_PROTOCOL_NICE_FLO_NAME), | ||||||
|         "Test decoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_nice_flor_s_test) { | MU_TEST(subghz_decoder_nice_flor_s_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decode_test( |         subghz_decoder_test( | ||||||
|             "/ext/unit_tests/subghz/nice_flor_s_raw.sub", SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME), |             "/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"); |         "Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_princeton_test) { | MU_TEST(subghz_decoder_princeton_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decode_test( |         subghz_decoder_test( | ||||||
|             "/ext/unit_tests/subghz/Princeton_raw.sub", SUBGHZ_PROTOCOL_PRINCETON_NAME), |             "/ext/unit_tests/subghz/Princeton_raw.sub", SUBGHZ_PROTOCOL_PRINCETON_NAME), | ||||||
|         "Test decoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_scher_khan_magic_code_test) { | MU_TEST(subghz_decoder_scher_khan_magic_code_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decode_test( |         subghz_decoder_test( | ||||||
|             "/ext/unit_tests/subghz/scher_khan_magic_code.sub", SUBGHZ_PROTOCOL_SCHER_KHAN_NAME), |             "/ext/unit_tests/subghz/scher_khan_magic_code.sub", SUBGHZ_PROTOCOL_SCHER_KHAN_NAME), | ||||||
|         "Test decoder " SUBGHZ_PROTOCOL_SCHER_KHAN_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_SCHER_KHAN_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_somfy_keytis_test) { | MU_TEST(subghz_decoder_somfy_keytis_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decode_test( |         subghz_decoder_test( | ||||||
|             "/ext/unit_tests/subghz/Somfy_keytis_raw.sub", SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME), |             "/ext/unit_tests/subghz/Somfy_keytis_raw.sub", SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME), | ||||||
|         "Test decoder " SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_somfy_telis_test) { | MU_TEST(subghz_decoder_somfy_telis_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decode_test( |         subghz_decoder_test( | ||||||
|             "/ext/unit_tests/subghz/somfy_telis_raw.sub", SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME), |             "/ext/unit_tests/subghz/somfy_telis_raw.sub", SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME), | ||||||
|         "Test decoder " SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_star_line_test) { | MU_TEST(subghz_decoder_star_line_test) { | ||||||
|     mu_assert( |     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"); |         "Test decoder " SUBGHZ_PROTOCOL_STAR_LINE_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_ecoder_princeton_test) { | MU_TEST(subghz_encoder_princeton_test) { | ||||||
|     mu_assert( |     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"); |         "Test encoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_ecoder_came_test) { | MU_TEST(subghz_encoder_came_test) { | ||||||
|     mu_assert( |     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"); |         "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( |     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"); |         "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( |     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"); |         "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( |     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"); |         "Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_ecoder_keelog_test) { | MU_TEST(subghz_encoder_keelog_test) { | ||||||
|     mu_assert( |     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"); |         "Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_random_test) { | 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_TEST_SUITE(subghz) { | ||||||
|     //MU_SUITE_CONFIGURE(&subghz_test_init, &subghz_test_deinit);
 |  | ||||||
| 
 |  | ||||||
|     subghz_test_init(); |     subghz_test_init(); | ||||||
|     MU_RUN_TEST(subghz_keystore_test); |     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_somfy_telis_test); | ||||||
|     MU_RUN_TEST(subghz_decoder_star_line_test); |     MU_RUN_TEST(subghz_decoder_star_line_test); | ||||||
| 
 | 
 | ||||||
|     MU_RUN_TEST(subghz_ecoder_princeton_test); |     MU_RUN_TEST(subghz_encoder_princeton_test); | ||||||
|     MU_RUN_TEST(subghz_ecoder_came_test); |     MU_RUN_TEST(subghz_encoder_came_test); | ||||||
|     MU_RUN_TEST(subghz_ecoder_came_twee_test); |     MU_RUN_TEST(subghz_encoder_came_twee_test); | ||||||
|     MU_RUN_TEST(subghz_ecoder_gate_tx_test); |     MU_RUN_TEST(subghz_encoder_gate_tx_test); | ||||||
|     MU_RUN_TEST(subghz_ecoder_nice_flo_test); |     MU_RUN_TEST(subghz_encoder_nice_flo_test); | ||||||
|     MU_RUN_TEST(subghz_ecoder_keelog_test); |     MU_RUN_TEST(subghz_encoder_keelog_test); | ||||||
| 
 | 
 | ||||||
|     MU_RUN_TEST(subghz_random_test); |     MU_RUN_TEST(subghz_random_test); | ||||||
|     subghz_test_deinit(); |     subghz_test_deinit(); | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ int run_minunit_test_flipper_format_string(); | |||||||
| int run_minunit_test_stream(); | int run_minunit_test_stream(); | ||||||
| int run_minunit_test_storage(); | int run_minunit_test_storage(); | ||||||
| int run_minunit_test_subghz(); | int run_minunit_test_subghz(); | ||||||
|  | int run_minunit_test_dirwalk(); | ||||||
| 
 | 
 | ||||||
| void minunit_print_progress(void) { | void minunit_print_progress(void) { | ||||||
|     static char progress[] = {'\\', '|', '/', '-'}; |     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_result |= run_minunit_test_storage(); |         test_result |= run_minunit_test_storage(); | ||||||
|         test_result |= run_minunit_test_stream(); |         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(); | ||||||
|         test_result |= run_minunit_test_flipper_format_string(); |         test_result |= run_minunit_test_flipper_format_string(); | ||||||
|         test_result |= run_minunit_test_infrared_decoder_encoder(); |         test_result |= run_minunit_test_infrared_decoder_encoder(); | ||||||
|  | |||||||
| @ -54,27 +54,24 @@ void subghz_file_encoder_worker_add_level_duration( | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool subghz_file_encoder_worker_data_parse( | bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) { | ||||||
|     SubGhzFileEncoderWorker* instance, |  | ||||||
|     const char* strStart, |  | ||||||
|     size_t len) { |  | ||||||
|     char* str1; |     char* str1; | ||||||
|     size_t ind_start = (size_t)strStart; //store the start address of the beginning of the line
 |  | ||||||
|     bool res = false; |     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) { |     if(str1 != NULL) { | ||||||
|         str1 = strchr( |         // Skip key
 | ||||||
|             str1, |         str1 = strchr(str1, ' '); | ||||||
|             ' '); //if found, shift the pointer by 1 element per line "RAW_Data: -1, 2, -2..."
 | 
 | ||||||
|         while( |         // Check that there is still an element in the line
 | ||||||
|             strchr(str1, ' ') != NULL && |         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
 |  | ||||||
|             str1 = strchr(str1, ' '); |             str1 = strchr(str1, ' '); | ||||||
|             str1 += 1; //if found, shift the pointer by next element per line
 | 
 | ||||||
|  |             // Skip space
 | ||||||
|  |             str1 += 1; | ||||||
|             subghz_file_encoder_worker_add_level_duration(instance, atoi(str1)); |             subghz_file_encoder_worker_add_level_duration(instance, atoi(str1)); | ||||||
|         } |         } | ||||||
|         res = true; |         res = true; | ||||||
| @ -143,9 +140,7 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { | |||||||
|             if(stream_read_line(stream, instance->str_data)) { |             if(stream_read_line(stream, instance->str_data)) { | ||||||
|                 string_strim(instance->str_data); |                 string_strim(instance->str_data); | ||||||
|                 if(!subghz_file_encoder_worker_data_parse( |                 if(!subghz_file_encoder_worker_data_parse( | ||||||
|                        instance, |                        instance, string_get_cstr(instance->str_data))) { | ||||||
|                        string_get_cstr(instance->str_data), |  | ||||||
|                        strlen(string_get_cstr(instance->str_data)))) { |  | ||||||
|                     //to stop DMA correctly
 |                     //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); | ||||||
|                     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
									
								
							
							
						
						
									
										152
									
								
								lib/toolbox/dir_walk.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										79
									
								
								lib/toolbox/dir_walk.h
									
									
									
									
									
										Normal 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 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 SG
						SG