[io] testing abstracted io
This commit is contained in:
parent
c75661cfc9
commit
41b6128a35
|
@ -1,6 +1,6 @@
|
|||
.DS_Store
|
||||
*.dSYM
|
||||
bin/*
|
||||
bin
|
||||
*.metallib
|
||||
|
||||
*.pdb
|
||||
|
|
1
build.sh
1
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
|
||||
|
|
|
@ -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...
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include<unistd.h>
|
||||
#include<limits.h>
|
||||
|
||||
#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,8 +231,7 @@ 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);
|
||||
|
||||
|
@ -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,10 +448,13 @@ 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)
|
||||
{
|
||||
if(req->op != IO_OP_OPEN_AT)
|
||||
{
|
||||
cmp.error = IO_ERR_HANDLE;
|
||||
}
|
||||
}
|
||||
else if(slot->fatal && req->op != IO_OP_CLOSE)
|
||||
{
|
||||
cmp.error = IO_ERR_PREV;
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
Hello, world!
|
|
@ -0,0 +1 @@
|
|||
regular.txt
|
|
@ -0,0 +1,181 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: main.c
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 26/05/2023
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
#include<stdio.h>
|
||||
#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);
|
||||
}
|
Loading…
Reference in New Issue