#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <cx/string.h>
#include <cx/list.h>
#include <cx/map.h>
#include "../daemon/session.h"
#include "testutils.h"
#include "vfs.h"
typedef struct TestVFS {
CxMap *files;
int count_unlink;
int count_rmdir;
} TestVFS;
typedef struct TestVFSFile {
VFSFile file;
cxmutstr path;
int isdir;
CxBuffer content;
} TestVFSFile;
typedef struct TestVFSDir {
VFSDir dir;
TestVFSFile *file;
CxIterator i;
cxmutstr name;
} TestVFSDir;
static const char* test_resource_name(
char *url) {
cxstring urlstr = cx_str(url);
if(urlstr.ptr[urlstr.length-
1] ==
'/') {
urlstr.length--;
}
cxstring resname = cx_strrchr(urlstr,
'/');
if(resname.length >
1) {
return resname.ptr+
1;
}
else {
return url;
}
}
int testvfs_readdir(
VFS_DIR dir,
VFS_ENTRY *entry,
int getstat) {
TestVFS *vfs = dir->ctx->vfs->instance;
TestVFSDir *vfsdir = (TestVFSDir*)dir;
cxmutstr prefix = cx_strcat(
2, vfsdir->file->path, cx_str(
"/"));
TestVFSFile *file =
NULL;
cx_foreach(TestVFSFile *, entry, vfsdir->i) {
if(file)
break;
cxmutstr file_path = cx_strcat(
2,
prefix,
cx_str(test_resource_name(entry->path.ptr)));
file = cxMapGet(vfs->files, cx_hash_key(file_path.ptr, file_path.length));
free(file_path.ptr);
}
free(prefix.ptr);
if(file) {
vfsdir->name = cx_strdup_a(
pool_allocator(dir->ctx->sn->pool),
cx_str(test_resource_name(file->path.ptr)));
ZERO(entry,
sizeof(
VFS_ENTRY));
entry->name = vfsdir->name.ptr;
if(getstat) {
ZERO(&entry->stat,
sizeof(
struct stat));
if(file->isdir) {
entry->stat.st_mode =
S_IFDIR;
}
}
return 1;
}
else {
return 0;
}
}
void testvfs_dir_close(
VFS_DIR dir) {
TestVFSDir *testdir = (TestVFSDir*)dir;
pool_free(testdir->dir.ctx->sn->pool, dir);
}
ssize_t testvfs_read(
SYS_FILE fd,
void *buf,
size_t nbyte) {
TestVFSFile *file = (TestVFSFile*)fd;
return (
ssize_t)cxBufferRead(buf,
1, nbyte, &file->content);
}
ssize_t testvfs_write(
SYS_FILE fd,
const void *buf,
size_t nbyte) {
TestVFSFile *file = (TestVFSFile*)fd;
return (
ssize_t)cxBufferWrite(buf,
1, nbyte, &file->content);
}
ssize_t testvfs_pread(
SYS_FILE fd,
void *buf,
size_t nbyte,
off_t offset) {
TestVFSFile *file = (TestVFSFile*)fd;
file->content.pos = (
size_t)offset;
return testvfs_read(fd, buf, nbyte);
}
ssize_t testvfs_pwrite(
SYS_FILE fd,
const void *buf,
size_t nbyte,
off_t offset) {
TestVFSFile *file = (TestVFSFile*)fd;
file->content.pos = (
size_t)offset;
return testvfs_write(fd, buf, nbyte);
}
off_t testvfs_seek(
SYS_FILE fd,
off_t offset,
int whence) {
TestVFSFile *file = (TestVFSFile*)fd;
cxBufferSeek(&file->content, offset, whence);
return (
off_t)file->content.pos;
}
void testvfs_close(
SYS_FILE fd) {
TestVFSFile *file = (TestVFSFile*)fd;
file->content.pos =
0;
}
VFS_IO test_file_io = {
testvfs_read,
testvfs_write,
testvfs_pread,
testvfs_pwrite,
testvfs_seek,
testvfs_close,
NULL,
NULL
};
VFS_DIRIO test_dir_io = {
testvfs_readdir,
testvfs_dir_close
};
SYS_FILE testvfs_open(VFSContext *ctx,
const char *path,
int oflags) {
TestVFS *vfs = ctx->vfs->instance;
TestVFSFile *file =
NULL;
cxstring s_path = cx_str((
char*)path);
if(cx_strsuffix(s_path, cx_str(
"/"))) {
s_path.length--;
}
file = cxMapGet(vfs->files, cx_hash_key_bytes((
const unsigned char*)s_path.ptr, s_path.length));
if(!file) {
if((oflags &
O_CREAT) ==
O_CREAT) {
file = pool_malloc(ctx->sn->pool,
sizeof(TestVFSFile));
ZERO(file,
sizeof(TestVFSFile));
file->file.ctx = ctx;
file->path = cx_strdup_a(pool_allocator(ctx->sn->pool), s_path);
file->file.io = &test_file_io;
cxBufferInit(&file->content, pool_malloc(ctx->sn->pool,
2048),
2048, pool_allocator(ctx->sn->pool),
0);
cxMapPut(vfs->files, cx_hash_key((
void*)s_path.ptr, s_path.length), file);
}
else {
ctx->vfs_errno =
ENOENT;
}
}
return (
SYS_FILE)file;
}
int testvfs_stat(VFSContext *ctx,
const char *path,
struct stat *buf) {
TestVFS *vfs = ctx->vfs->instance;
TestVFSFile *file =
NULL;
cxstring s_path = cx_str((
char*)path);
if(cx_strsuffix(s_path, cx_str(
"/"))) {
s_path.length--;
}
file = cxMapGet(vfs->files, cx_hash_key((
void*)s_path.ptr, s_path.length));
if(!file) {
ctx->vfs_errno =
ENOENT;
return 1;
}
ZERO(buf,
sizeof(
struct stat));
if(file->isdir) {
buf->st_mode =
S_IFDIR;
}
return 0;
}
int testvfs_fstat(VFSContext *ctx,
SYS_FILE fd,
struct stat *buf) {
return 0;
}
VFS_DIR testvfs_opendir(VFSContext *ctx,
const char *path) {
TestVFS *vfs = ctx->vfs->instance;
TestVFSFile *file =
NULL;
cxstring s_path = cx_str((
char*)path);
if(cx_strsuffix(s_path, cx_str(
"/"))) {
s_path.length--;
}
file = cxMapGet(vfs->files, cx_hash_key((
void*)s_path.ptr, s_path.length));
if(!file) {
ctx->vfs_errno =
ENOENT;
return NULL;
}
if(!file->isdir) {
return NULL;
}
TestVFSDir *dir = pool_malloc(ctx->sn->pool,
sizeof(TestVFSDir));
ZERO(dir,
sizeof(TestVFSDir));
dir->file = file;
dir->i = cxMapIteratorValues(vfs->files);
dir->dir.ctx = ctx;
dir->dir.io = &test_dir_io;
return (
VFS_DIR)dir;
}
VFS_DIR testvfs_fdopendir(VFSContext *ctx,
SYS_FILE fd) {
TestVFS *vfs = ctx->vfs->instance;
TestVFSFile *file = (TestVFSFile*)fd;
if(!file->isdir) {
return NULL;
}
TestVFSDir *dir = pool_malloc(ctx->sn->pool,
sizeof(TestVFSDir));
ZERO(dir,
sizeof(TestVFSDir));
dir->file = file;
dir->i = cxMapIteratorValues(vfs->files);
dir->dir.ctx = ctx;
dir->dir.io = &test_dir_io;
return (
VFS_DIR)dir;
}
int testvfs_mkdir(VFSContext *ctx,
const char *path) {
SYS_FILE fd = testvfs_open(ctx, path,
O_CREAT);
if(!fd) {
return 1;
}
TestVFSFile *file = (TestVFSFile*)fd;
file->isdir =
1;
return 0;
}
int testvfs_unlink(VFSContext *ctx,
const char *path) {
TestVFS *vfs = ctx->vfs->instance;
CxHashKey path_key = cx_hash_key_str(path);
TestVFSFile *file = cxMapGet(vfs->files, path_key);
if(!file) {
return 1;
}
if(file->isdir) {
return 1;
}
cxMapRemove(vfs->files, path_key);
vfs->count_unlink++;
return 0;
}
int testvfs_rmdir(VFSContext *ctx,
const char *path) {
TestVFS *vfs = ctx->vfs->instance;
CxHashKey path_key = cx_hash_key_str(path);
TestVFSFile *dir = cxMapGet(vfs->files, path_key);
if(!dir) {
ctx->vfs_errno =
ENOENT;
return 1;
}
if(!dir->isdir) {
return 1;
}
CxIterator i = cxMapIteratorValues(vfs->files);
cx_foreach(TestVFSFile *, f, i) {
if(f->path.length > dir->path.length && cx_strprefix(cx_strcast(f->path), cx_strcast(dir->path))){
return 1;
}
}
cxMapRemove(vfs->files, path_key);
vfs->count_rmdir++;
return 0;
}
static VFS testVFSClass = {
testvfs_open,
testvfs_stat,
testvfs_fstat,
testvfs_opendir,
testvfs_fdopendir,
testvfs_mkdir,
testvfs_unlink,
testvfs_rmdir,
0,
NULL
};
VFS* testvfs_create(Session *sn) {
TestVFS *vfs = pool_malloc(sn->pool,
sizeof(TestVFS));
vfs->count_unlink =
0;
vfs->count_rmdir =
0;
vfs->files = cxHashMapCreate(pool_allocator(sn->pool),
CX_STORE_POINTERS,
64);
testVFSClass.instance = vfs;
return &testVFSClass;
}
UCX_TEST(test_vfs_open) {
Session *sn = testutil_session();
Request *rq = testutil_request(sn->pool,
"PUT",
"/");
rq->vfs = testvfs_create(sn);
VFSContext *vfs = vfs_request_context(sn, rq);
UCX_TEST_BEGIN;
UCX_TEST_ASSERT(vfs,
"vfs is NULL");
SYS_FILE f1 = vfs_open(vfs,
"/file1",
O_CREAT);
UCX_TEST_ASSERT(f1,
"f1 not opened");
SYS_FILE f2 = vfs_open(vfs,
"/file1",
0);
UCX_TEST_ASSERT(f2,
"f2 not opened");
UCX_TEST_END;
testutil_destroy_session(sn);
}
UCX_TEST(test_vfs_mkdir) {
Session *sn = testutil_session();
Request *rq = testutil_request(sn->pool,
"PUT",
"/");
rq->vfs = testvfs_create(sn);
VFSContext *vfs = vfs_request_context(sn, rq);
UCX_TEST_BEGIN;
int err = vfs_mkdir(vfs,
"/dir");
UCX_TEST_ASSERT(err ==
0,
"error not 0");
SYS_FILE fd = vfs_open(vfs,
"/dir",
0);
UCX_TEST_ASSERT(fd,
"no fd");
UCX_TEST_END;
testutil_destroy_session(sn);
}
UCX_TEST(test_vfs_opendir) {
Session *sn = testutil_session();
Request *rq = testutil_request(sn->pool,
"PUT",
"/");
rq->vfs = testvfs_create(sn);
VFSContext *vfs = vfs_request_context(sn, rq);
UCX_TEST_BEGIN;
int err = vfs_mkdir(vfs,
"/dir");
UCX_TEST_ASSERT(err ==
0,
"error not 0");
VFSDir *dir = vfs_opendir(vfs,
"/dir");
UCX_TEST_ASSERT(dir,
"no dir");
UCX_TEST_END;
testutil_destroy_session(sn);
}
UCX_TEST(test_vfs_readdir) {
Session *sn = testutil_session();
Request *rq = testutil_request(sn->pool,
"PUT",
"/");
rq->vfs = testvfs_create(sn);
VFSContext *vfs = vfs_request_context(sn, rq);
UCX_TEST_BEGIN;
int err = vfs_mkdir(vfs,
"/dir");
UCX_TEST_ASSERT(err ==
0,
"error not 0");
UCX_TEST_ASSERT(vfs_open(vfs,
"/dir/file1",
O_CREAT),
"creation of file1 failed");
UCX_TEST_ASSERT(vfs_open(vfs,
"/dir/file2",
O_CREAT),
"creation of file2 failed");
UCX_TEST_ASSERT(vfs_open(vfs,
"/dir/file3",
O_CREAT),
"creation of file3 failed");
UCX_TEST_ASSERT(vfs_open(vfs,
"/dir/file4",
O_CREAT),
"creation of file4 failed");
VFSDir *dir = vfs_opendir(vfs,
"/dir");
UCX_TEST_ASSERT(dir,
"dir not opened");
CxMap *files = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
8);
VFSEntry entry;
while(vfs_readdir(dir, &entry)) {
cxMapPut(files, cx_hash_key_str(entry.name), dir);
}
UCX_TEST_ASSERT(files->size ==
4,
"wrong files count");
UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str(
"file1")),
"file1 missing");
UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str(
"file2")),
"file2 missing");
UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str(
"file3")),
"file3 missing");
UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str(
"file4")),
"file4 missing");
cxMapDestroy(files);
UCX_TEST_END;
testutil_destroy_session(sn);
}
UCX_TEST(test_vfs_unlink) {
Session *sn = testutil_session();
Request *rq = testutil_request(sn->pool,
"PUT",
"/");
rq->vfs = testvfs_create(sn);
VFSContext *vfs = vfs_request_context(sn, rq);
UCX_TEST_BEGIN;
int err;
err = vfs_mkdir(vfs,
"/dir1");
UCX_TEST_ASSERT(err ==
0,
"mkdir 1: error not 0");
err = vfs_mkdir(vfs,
"/dir2");
UCX_TEST_ASSERT(err ==
0,
"mkdir 1: error not 0");
SYS_FILE f1 = vfs_open(vfs,
"/file1",
O_CREAT);
UCX_TEST_ASSERT(f1,
"f1 not opened");
SYS_FILE f2 = vfs_open(vfs,
"/file2",
O_CREAT);
UCX_TEST_ASSERT(f1,
"f2 not opened");
SYS_FILE f3 = vfs_open(vfs,
"/dir1/file3",
O_CREAT);
UCX_TEST_ASSERT(f1,
"f3 not opened");
err = vfs_unlink(vfs,
"/file1");
UCX_TEST_ASSERT(err ==
0,
"unlink /file1 failed");
err = vfs_unlink(vfs,
"/dir1/file3");
UCX_TEST_ASSERT(err ==
0,
"unlink /dir1/file3 failed");
err = vfs_unlink(vfs,
"/filex");
UCX_TEST_ASSERT(err !=
0,
"unlink /filex should fail");
SYS_FILE o1 = vfs_open(vfs,
"/file1",
O_RDONLY);
UCX_TEST_ASSERT(o1 ==
NULL,
"/file1 not deleted");
SYS_FILE o3 = vfs_open(vfs,
"/dir1/file3",
O_RDONLY);
UCX_TEST_ASSERT(o1 ==
NULL,
"/dir1/file3 not deleted");
SYS_FILE o2 = vfs_open(vfs,
"/file2",
O_RDONLY);
UCX_TEST_ASSERT(o2,
"/file2 deleted");
err = vfs_unlink(vfs,
"/dir1");
UCX_TEST_ASSERT(err !=
0,
"unlink dir1 should fail");
UCX_TEST_END;
testutil_destroy_session(sn);
}
UCX_TEST(test_vfs_rmdir) {
Session *sn = testutil_session();
Request *rq = testutil_request(sn->pool,
"PUT",
"/");
rq->vfs = testvfs_create(sn);
VFSContext *vfs = vfs_request_context(sn, rq);
UCX_TEST_BEGIN;
int err;
err = vfs_mkdir(vfs,
"/dir1");
UCX_TEST_ASSERT(err ==
0,
"mkdir 1: error not 0");
err = vfs_mkdir(vfs,
"/dir2");
UCX_TEST_ASSERT(err ==
0,
"mkdir 1: error not 0");
SYS_FILE f1 = vfs_open(vfs,
"/dir1/file1",
O_CREAT);
UCX_TEST_ASSERT(f1,
"f1 not opened");
err = vfs_rmdir(vfs,
"/dir1");
UCX_TEST_ASSERT(err !=
0,
"rmdir /dir1 should fail");
err = vfs_rmdir(vfs,
"/dir2");
UCX_TEST_ASSERT(err ==
0,
"rmdir /dir2 failed");
err = vfs_unlink(vfs,
"/dir1/file1");
UCX_TEST_ASSERT(err ==
0,
"unlink failed");
err = vfs_rmdir(vfs,
"/dir1");
UCX_TEST_ASSERT(err ==
0,
"rmdir /dir1 (2) failed");
UCX_TEST_END;
testutil_destroy_session(sn);
}