[io] testing abstracted io
This commit is contained in:
parent
c75661cfc9
commit
41b6128a35
|
@ -1,6 +1,6 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.dSYM
|
*.dSYM
|
||||||
bin/*
|
bin
|
||||||
*.metallib
|
*.metallib
|
||||||
|
|
||||||
*.pdb
|
*.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
|
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.
|
# 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
|
install_name_tool -add_rpath "@executable_path" $BINDIR/libmilepost.dylib
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
|
@ -25,8 +25,9 @@ enum {
|
||||||
FILE_OPEN_TRUNCATE = 1<<3,
|
FILE_OPEN_TRUNCATE = 1<<3,
|
||||||
FILE_OPEN_CREATE = 1<<4,
|
FILE_OPEN_CREATE = 1<<4,
|
||||||
|
|
||||||
FILE_OPEN_NO_FOLLOW = 1<<5,
|
FILE_OPEN_SYMLINK = 1<<5,
|
||||||
FILE_OPEN_RESTRICT = 1<<6,
|
FILE_OPEN_NO_FOLLOW = 1<<6,
|
||||||
|
FILE_OPEN_RESTRICT = 1<<7,
|
||||||
//...
|
//...
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,12 +105,12 @@ enum {
|
||||||
|
|
||||||
typedef struct io_cmp
|
typedef struct io_cmp
|
||||||
{
|
{
|
||||||
io_req id;
|
io_req_id id;
|
||||||
io_error error;
|
io_error error;
|
||||||
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
u64 result;
|
i64 result;
|
||||||
u64 size;
|
u64 size;
|
||||||
i64 offset;
|
i64 offset;
|
||||||
file_handle handle;
|
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);
|
file_handle file_open_relative(file_handle base, str8 path, file_open_flags flags);
|
||||||
void file_close(file_handle file);
|
void file_close(file_handle file);
|
||||||
|
|
||||||
off_t file_pos(file_handle file);
|
i64 file_pos(file_handle file);
|
||||||
off_t file_seek(file_handle file, long offset, file_whence whence);
|
i64 file_seek(file_handle file, long offset, file_whence whence);
|
||||||
|
|
||||||
size_t file_write(file_handle file, size_t size, char* buffer);
|
u64 file_write(file_handle file, u64 size, char* buffer);
|
||||||
size_t file_read(file_handle file, size_t 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
|
// File System wrapper API
|
||||||
|
@ -185,7 +186,7 @@ typedef struct file_status
|
||||||
} file_status;
|
} file_status;
|
||||||
|
|
||||||
file_status file_get_status(file_handle file);
|
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...
|
//TODO: Complete as needed...
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
file_handle file_open(str8 path, file_open_flags flags)
|
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,
|
.size = path.len,
|
||||||
.buffer = path.ptr,
|
.buffer = path.ptr,
|
||||||
.openFlags = flags};
|
.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
|
//WARN: we always return a handle that can be queried for errors. Handles must be closed
|
||||||
// even if there was an error when opening
|
// even if there was an error when opening
|
||||||
file_handle handle = { cmp.result };
|
return(cmp.handle);
|
||||||
return(handle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void file_close(file_handle file)
|
void file_close(file_handle file)
|
||||||
|
@ -34,16 +33,16 @@ void file_close(file_handle file)
|
||||||
io_wait_single_req(&req);
|
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,
|
io_req req = {.op = IO_OP_POS,
|
||||||
.handle = file};
|
.handle = file};
|
||||||
|
|
||||||
io_cmp cmp = io_wait_single_req(&req);
|
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,
|
io_req req = {.op = IO_OP_SEEK,
|
||||||
.handle = file,
|
.handle = file,
|
||||||
|
@ -51,10 +50,10 @@ off_t file_seek(file_handle file, long offset, file_whence whence)
|
||||||
.whence = whence};
|
.whence = whence};
|
||||||
|
|
||||||
io_cmp cmp = io_wait_single_req(&req);
|
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,
|
io_req req = {.op = IO_OP_WRITE,
|
||||||
.handle = file,
|
.handle = file,
|
||||||
|
@ -62,10 +61,10 @@ size_t file_write(file_handle file, size_t size, char* buffer)
|
||||||
.buffer = buffer};
|
.buffer = buffer};
|
||||||
|
|
||||||
io_cmp cmp = io_wait_single_req(&req);
|
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,
|
io_req req = {.op = IO_OP_READ,
|
||||||
.handle = file,
|
.handle = file,
|
||||||
|
@ -73,16 +72,16 @@ size_t file_read(file_handle file, size_t size, char* buffer)
|
||||||
.buffer = buffer};
|
.buffer = buffer};
|
||||||
|
|
||||||
io_cmp cmp = io_wait_single_req(&req);
|
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,
|
io_req req = {.op = IO_OP_ERROR,
|
||||||
.handle = file};
|
.handle = file};
|
||||||
|
|
||||||
io_cmp cmp = io_wait_single_req(&req);
|
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)
|
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,
|
io_req req = {.op = IO_OP_FSTAT,
|
||||||
.handle = file,
|
.handle = file,
|
||||||
.size = sizeof(file_status),
|
.size = sizeof(file_status),
|
||||||
.buffer = (char*)status};
|
.buffer = (char*)&status};
|
||||||
|
|
||||||
io_cmp cmp = io_wait_single_req(&req);
|
io_cmp cmp = io_wait_single_req(&req);
|
||||||
return(status);
|
return(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t file_size(file_handle file)
|
u64 file_size(file_handle file)
|
||||||
{
|
{
|
||||||
file_status status = file_get_status(file);
|
file_status status = file_get_status(file);
|
||||||
return(status.size);
|
return(status.size);
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include<unistd.h>
|
#include<unistd.h>
|
||||||
#include<limits.h>
|
#include<limits.h>
|
||||||
|
|
||||||
#include"platform_io.h"
|
#include"platform_io_common.c"
|
||||||
|
|
||||||
typedef struct file_slot
|
typedef struct file_slot
|
||||||
{
|
{
|
||||||
|
@ -45,6 +45,7 @@ file_slot* file_slot_alloc(file_table* table)
|
||||||
{
|
{
|
||||||
slot = &table->slots[table->nextSlot];
|
slot = &table->slots[table->nextSlot];
|
||||||
slot->generation = 1;
|
slot->generation = 1;
|
||||||
|
slot->fd = -1;
|
||||||
table->nextSlot++;
|
table->nextSlot++;
|
||||||
}
|
}
|
||||||
return(slot);
|
return(slot);
|
||||||
|
@ -71,7 +72,7 @@ file_slot* file_slot_from_handle(file_table* table, file_handle handle)
|
||||||
u64 index = handle.h & 0xffffffff;
|
u64 index = handle.h & 0xffffffff;
|
||||||
u64 generation = handle.h>>32;
|
u64 generation = handle.h>>32;
|
||||||
|
|
||||||
if(index < ORCA_MAX_FILE_SLOTS)
|
if(index < table->nextSlot)
|
||||||
{
|
{
|
||||||
file_slot* candidate = &table->slots[index];
|
file_slot* candidate = &table->slots[index];
|
||||||
if(candidate->generation == generation)
|
if(candidate->generation == generation)
|
||||||
|
@ -211,6 +212,10 @@ int io_convert_open_flags(file_open_flags flags)
|
||||||
{
|
{
|
||||||
oflags |= O_NOFOLLOW;
|
oflags |= O_NOFOLLOW;
|
||||||
}
|
}
|
||||||
|
if(flags & FILE_OPEN_SYMLINK)
|
||||||
|
{
|
||||||
|
oflags |= O_SYMLINK;
|
||||||
|
}
|
||||||
return(oflags);
|
return(oflags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,18 +231,17 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
file_handle handle = file_handle_from_slot(&__globalFileTable, slot);
|
cmp.handle = file_handle_from_slot(&__globalFileTable, slot);
|
||||||
cmp.result = handle.h;
|
|
||||||
|
|
||||||
int flags = io_convert_open_flags(req->openFlags);
|
int flags = io_convert_open_flags(req->openFlags);
|
||||||
|
|
||||||
//TODO: allow specifying access
|
//TODO: allow specifying access
|
||||||
mode_t mode = S_IRUSR
|
mode_t mode = S_IRUSR
|
||||||
| S_IWUSR
|
| S_IWUSR
|
||||||
| S_IRGRP
|
| S_IRGRP
|
||||||
| S_IWGRP
|
| S_IWGRP
|
||||||
| S_IROTH
|
| S_IROTH
|
||||||
| S_IWOTH;
|
| S_IWOTH;
|
||||||
|
|
||||||
//NOTE: open
|
//NOTE: open
|
||||||
mem_arena_scope scratch = mem_scratch_begin();
|
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};
|
io_cmp cmp = {0};
|
||||||
|
|
||||||
if(req->size <= sizeof(file_status))
|
if(req->size < sizeof(file_status))
|
||||||
{
|
{
|
||||||
cmp.error = IO_ERR_ARG;
|
cmp.error = IO_ERR_ARG;
|
||||||
}
|
}
|
||||||
|
@ -444,9 +448,12 @@ io_cmp io_wait_single_req(io_req* req)
|
||||||
io_cmp cmp = {0};
|
io_cmp cmp = {0};
|
||||||
|
|
||||||
file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle);
|
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)
|
else if(slot->fatal && req->op != IO_OP_CLOSE)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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