From 41b6128a355badaa2a887d5bf6a428ea20b85219 Mon Sep 17 00:00:00 2001 From: Martin Fouilleul Date: Fri, 26 May 2023 11:40:00 +0200 Subject: [PATCH] [io] testing abstracted io --- .gitignore | 2 +- build.sh | 1 + src/platform/platform_io.h | 21 ++-- src/platform/platform_io_common.c | 29 +++-- src/platform/posix_io.c | 31 +++-- test/files/build.sh | 17 +++ test/files/data/directory/dummy.txt | 0 test/files/data/regular.txt | 1 + test/files/data/symlink | 1 + test/files/main.c | 181 ++++++++++++++++++++++++++++ 10 files changed, 246 insertions(+), 38 deletions(-) create mode 100755 test/files/build.sh create mode 100644 test/files/data/directory/dummy.txt create mode 100644 test/files/data/regular.txt create mode 120000 test/files/data/symlink create mode 100644 test/files/main.c diff --git a/.gitignore b/.gitignore index 7553783..1a924e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .DS_Store *.dSYM -bin/* +bin *.metallib *.pdb diff --git a/build.sh b/build.sh index 9c578a3..173da3f 100755 --- a/build.sh +++ b/build.sh @@ -76,6 +76,7 @@ if [ $target = 'lib' ] ; then install_name_tool -change "./libGLESv2.dylib" '@rpath/libGLESv2.dylib' $BINDIR/libmilepost.dylib # add executable path to rpath. Client executable can still add its own rpaths if needed, e.g. @executable_path/libs/ etc. + install_name_tool -id "@rpath/libmilepost.dylib" $BINDIR/libmilepost.dylib install_name_tool -add_rpath "@executable_path" $BINDIR/libmilepost.dylib else diff --git a/src/platform/platform_io.h b/src/platform/platform_io.h index 9fa4c84..db6c9af 100644 --- a/src/platform/platform_io.h +++ b/src/platform/platform_io.h @@ -25,8 +25,9 @@ enum { FILE_OPEN_TRUNCATE = 1<<3, FILE_OPEN_CREATE = 1<<4, - FILE_OPEN_NO_FOLLOW = 1<<5, - FILE_OPEN_RESTRICT = 1<<6, + FILE_OPEN_SYMLINK = 1<<5, + FILE_OPEN_NO_FOLLOW = 1<<6, + FILE_OPEN_RESTRICT = 1<<7, //... }; @@ -104,12 +105,12 @@ enum { typedef struct io_cmp { - io_req id; + io_req_id id; io_error error; union { - u64 result; + i64 result; u64 size; i64 offset; file_handle handle; @@ -130,13 +131,13 @@ file_handle file_open(str8 path, file_open_flags flags); file_handle file_open_relative(file_handle base, str8 path, file_open_flags flags); void file_close(file_handle file); -off_t file_pos(file_handle file); -off_t file_seek(file_handle file, long offset, file_whence whence); +i64 file_pos(file_handle file); +i64 file_seek(file_handle file, long offset, file_whence whence); -size_t file_write(file_handle file, size_t size, char* buffer); -size_t file_read(file_handle file, size_t size, char* buffer); +u64 file_write(file_handle file, u64 size, char* buffer); +u64 file_read(file_handle file, u64 size, char* buffer); -io_error io_last_error(file_handle handle); +io_error file_last_error(file_handle handle); //---------------------------------------------------------------- // File System wrapper API @@ -185,7 +186,7 @@ typedef struct file_status } file_status; file_status file_get_status(file_handle file); -size_t file_size(file_handle file); +u64 file_size(file_handle file); //TODO: Complete as needed... diff --git a/src/platform/platform_io_common.c b/src/platform/platform_io_common.c index 942036c..9bb3fd3 100644 --- a/src/platform/platform_io_common.c +++ b/src/platform/platform_io_common.c @@ -14,7 +14,7 @@ file_handle file_open(str8 path, file_open_flags flags) { - io_req req = {.op = IO_OP_OPEN, + io_req req = {.op = IO_OP_OPEN_AT, .size = path.len, .buffer = path.ptr, .openFlags = flags}; @@ -23,8 +23,7 @@ file_handle file_open(str8 path, file_open_flags flags) //WARN: we always return a handle that can be queried for errors. Handles must be closed // even if there was an error when opening - file_handle handle = { cmp.result }; - return(handle); + return(cmp.handle); } void file_close(file_handle file) @@ -34,16 +33,16 @@ void file_close(file_handle file) io_wait_single_req(&req); } -off_t file_pos(file_handle file) +i64 file_pos(file_handle file) { io_req req = {.op = IO_OP_POS, .handle = file}; io_cmp cmp = io_wait_single_req(&req); - return((size_t)cmp.result); + return(cmp.offset); } -off_t file_seek(file_handle file, long offset, file_whence whence) +i64 file_seek(file_handle file, long offset, file_whence whence) { io_req req = {.op = IO_OP_SEEK, .handle = file, @@ -51,10 +50,10 @@ off_t file_seek(file_handle file, long offset, file_whence whence) .whence = whence}; io_cmp cmp = io_wait_single_req(&req); - return((size_t)cmp.result); + return(cmp.offset); } -size_t file_write(file_handle file, size_t size, char* buffer) +u64 file_write(file_handle file, u64 size, char* buffer) { io_req req = {.op = IO_OP_WRITE, .handle = file, @@ -62,10 +61,10 @@ size_t file_write(file_handle file, size_t size, char* buffer) .buffer = buffer}; io_cmp cmp = io_wait_single_req(&req); - return((size_t)cmp.result); + return(cmp.size); } -size_t file_read(file_handle file, size_t size, char* buffer) +u64 file_read(file_handle file, u64 size, char* buffer) { io_req req = {.op = IO_OP_READ, .handle = file, @@ -73,16 +72,16 @@ size_t file_read(file_handle file, size_t size, char* buffer) .buffer = buffer}; io_cmp cmp = io_wait_single_req(&req); - return((size_t)cmp.result); + return(cmp.size); } -io_error io_last_error(file_handle file) +io_error file_last_error(file_handle file) { io_req req = {.op = IO_OP_ERROR, .handle = file}; io_cmp cmp = io_wait_single_req(&req); - return((int)cmp.result); + return((io_error)cmp.result); } file_status file_get_status(file_handle file) @@ -91,13 +90,13 @@ file_status file_get_status(file_handle file) io_req req = {.op = IO_OP_FSTAT, .handle = file, .size = sizeof(file_status), - .buffer = (char*)status}; + .buffer = (char*)&status}; io_cmp cmp = io_wait_single_req(&req); return(status); } -size_t file_size(file_handle file) +u64 file_size(file_handle file) { file_status status = file_get_status(file); return(status.size); diff --git a/src/platform/posix_io.c b/src/platform/posix_io.c index db34b2c..b6e9aa5 100644 --- a/src/platform/posix_io.c +++ b/src/platform/posix_io.c @@ -12,7 +12,7 @@ #include #include -#include"platform_io.h" +#include"platform_io_common.c" typedef struct file_slot { @@ -45,6 +45,7 @@ file_slot* file_slot_alloc(file_table* table) { slot = &table->slots[table->nextSlot]; slot->generation = 1; + slot->fd = -1; table->nextSlot++; } return(slot); @@ -71,7 +72,7 @@ file_slot* file_slot_from_handle(file_table* table, file_handle handle) u64 index = handle.h & 0xffffffff; u64 generation = handle.h>>32; - if(index < ORCA_MAX_FILE_SLOTS) + if(index < table->nextSlot) { file_slot* candidate = &table->slots[index]; if(candidate->generation == generation) @@ -211,6 +212,10 @@ int io_convert_open_flags(file_open_flags flags) { oflags |= O_NOFOLLOW; } + if(flags & FILE_OPEN_SYMLINK) + { + oflags |= O_SYMLINK; + } return(oflags); } @@ -226,18 +231,17 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req) } else { - file_handle handle = file_handle_from_slot(&__globalFileTable, slot); - cmp.result = handle.h; + cmp.handle = file_handle_from_slot(&__globalFileTable, slot); int flags = io_convert_open_flags(req->openFlags); //TODO: allow specifying access mode_t mode = S_IRUSR - | S_IWUSR - | S_IRGRP - | S_IWGRP - | S_IROTH - | S_IWOTH; + | S_IWUSR + | S_IRGRP + | S_IWGRP + | S_IROTH + | S_IWOTH; //NOTE: open mem_arena_scope scratch = mem_scratch_begin(); @@ -336,7 +340,7 @@ io_cmp io_fstat(file_slot* slot, io_req* req) { io_cmp cmp = {0}; - if(req->size <= sizeof(file_status)) + if(req->size < sizeof(file_status)) { cmp.error = IO_ERR_ARG; } @@ -444,9 +448,12 @@ io_cmp io_wait_single_req(io_req* req) io_cmp cmp = {0}; file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle); - if(!slot && (req->op != IO_OP_OPEN_AT)) + if(!slot) { - cmp.error = IO_ERR_HANDLE; + if(req->op != IO_OP_OPEN_AT) + { + cmp.error = IO_ERR_HANDLE; + } } else if(slot->fatal && req->op != IO_OP_CLOSE) { diff --git a/test/files/build.sh b/test/files/build.sh new file mode 100755 index 0000000..e8b06a4 --- /dev/null +++ b/test/files/build.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +LIBDIR=../../bin +RESDIR=../../resources +SRCDIR=../../src + +INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$SRCDIR/app" +LIBS="-L$LIBDIR -lmilepost" +FLAGS="-mmacos-version-min=10.15.4 -DDEBUG -DLOG_COMPILE_DEBUG" + +if [ ! \( -e bin \) ] ; then + mkdir ./bin +fi + + +clang -g $FLAGS $LIBS $INCLUDES -o ./bin/test_files main.c +install_name_tool -add_rpath "@executable_path/../../../bin" ./bin/test_files diff --git a/test/files/data/directory/dummy.txt b/test/files/data/directory/dummy.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/files/data/regular.txt b/test/files/data/regular.txt new file mode 100644 index 0000000..5dd01c1 --- /dev/null +++ b/test/files/data/regular.txt @@ -0,0 +1 @@ +Hello, world! \ No newline at end of file diff --git a/test/files/data/symlink b/test/files/data/symlink new file mode 120000 index 0000000..397e107 --- /dev/null +++ b/test/files/data/symlink @@ -0,0 +1 @@ +regular.txt \ No newline at end of file diff --git a/test/files/main.c b/test/files/main.c new file mode 100644 index 0000000..6531858 --- /dev/null +++ b/test/files/main.c @@ -0,0 +1,181 @@ +/************************************************************//** +* +* @file: main.c +* @author: Martin Fouilleul +* @date: 26/05/2023 +* +*****************************************************************/ + +#include +#include"milepost.h" + +int test_write(mem_arena* arena, str8 path, str8 test_string) +{ + log_info("writing\n"); + + file_handle f = file_open(path, FILE_OPEN_CREATE|FILE_OPEN_WRITE|FILE_OPEN_TRUNCATE); + if(file_last_error(f)) + { + log_error("Can't create/open file %.*s for writing\n", (int)path.len, path.ptr); + return(-1); + } + + file_write(f, test_string.len, test_string.ptr); + if(file_last_error(f)) + { + log_error("Error while writing %.*s\n", (int)path.len, path.ptr); + return(-1); + } + file_close(f); + + // check + char* pathCStr = str8_to_cstring(arena, path); + FILE* file = fopen(pathCStr, "r"); + if(!file) + { + log_error("File %.*s not found while checking\n", (int)path.len, path.ptr); + return(-1); + } + char buffer[256]; + int n = fread(buffer, 1, 256, file); + if(n != test_string.len || strncmp(buffer, test_string.ptr, test_string.len)) + { + log_error("Didn't recover test string\n"); + return(-1); + } + fclose(file); + + return(0); +} + +int test_read(mem_arena* arena, str8 path, str8 test_string) +{ + log_info("reading\n"); + + file_handle f = file_open(path, FILE_OPEN_READ); + if(file_last_error(f)) + { + log_error("Can't open file %.*s for reading\n", (int)path.len, path.ptr); + return(-1); + } + + char buffer[256]; + i64 n = file_read(f, 256, buffer); + if(file_last_error(f)) + { + log_error("Error while reading %.*s\n", (int)path.len, path.ptr); + return(-1); + } + file_close(f); + + if(str8_cmp(test_string, str8_from_buffer(n, buffer))) + { + log_error("Didn't recover test string\n"); + return(-1); + } + + return(0); +} + +int test_stat_size(str8 path, u64 size) +{ + log_info("stat size\n"); + + file_handle f = file_open(path, 0); + file_status status = file_get_status(f); + + if(file_last_error(f)) + { + log_error("Error while retrieving file status\n"); + return(-1); + } + + if(status.size != size) + { + log_error("size doesn't match\n"); + return(-1); + + } + + return(0); +} + +int test_stat_type(mem_arena* arena, str8 dataDir) +{ + str8 regular = path_append(arena, dataDir, STR8("regular.txt")); + str8 dir = path_append(arena, dataDir, STR8("directory")); + str8 link = path_append(arena, dataDir, STR8("symlink")); + + log_info("stat type, regular\n"); + + file_handle f = file_open(regular, 0); + file_status status = file_get_status(f); + if(file_last_error(f)) + { + log_error("Error while retrieving file status\n"); + return(-1); + } + if(status.type != FILE_REGULAR) + { + log_error("file type doesn't match\n"); + return(-1); + } + file_close(f); + + log_info("stat type, directory\n"); + + f = file_open(dir, 0); + status = file_get_status(f); + if(file_last_error(f)) + { + log_error("Error while retrieving file status\n"); + return(-1); + } + if(status.type != FILE_DIRECTORY) + { + log_error("file type doesn't match\n"); + return(-1); + } + file_close(f); + + log_info("stat type, symlink\n"); + + f = file_open(link, FILE_OPEN_SYMLINK); + status = file_get_status(f); + if(file_last_error(f)) + { + log_error("Error while retrieving file status\n"); + return(-1); + } + if(status.type != FILE_SYMLINK) + { + log_error("file type doesn't match\n"); + return(-1); + } + file_close(f); + + return(0); +} + +int main(int argc, char** argv) +{ + mem_arena* arena = mem_scratch(); + + str8 exePath = path_executable(arena); + str8 dirPath = path_slice_directory(exePath); + dirPath = path_append(arena, dirPath, STR8("..")); + + str8 dataDir = path_append(arena, dirPath, STR8("data")); + str8 path = path_append(arena, dirPath, STR8("test.txt")); + + str8 test_string = STR8("Hello, world!"); + + if(test_write(arena, path, test_string)) { return(-1); } + if(test_read(arena, path, test_string)) { return(-1); } + if(test_stat_size(path, test_string.len)) { return(-1); } + if(test_stat_type(arena, dataDir)) { return(-1); } + + log_info("OK\n"); + + return(0); +}