[io] testing abstracted io

This commit is contained in:
Martin Fouilleul 2023-05-26 11:40:00 +02:00
parent c75661cfc9
commit 41b6128a35
10 changed files with 246 additions and 38 deletions

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
.DS_Store
*.dSYM
bin/*
bin
*.metallib
*.pdb

View File

@ -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

View File

@ -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...

View File

@ -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);

View File

@ -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,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)
{

17
test/files/build.sh Executable file
View File

@ -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

View File

View File

@ -0,0 +1 @@
Hello, world!

1
test/files/data/symlink Symbolic link
View File

@ -0,0 +1 @@
regular.txt

181
test/files/main.c Normal file
View File

@ -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);
}