/* test_mem.c — mem backend: basic CRUD, directory markers (Section 9.3), * rename, readdir. */ #include "packfs.h" #include "test_harness.h" int main(void) { /* pfs_version() must report exactly what this header's own macros say, * so a dynamically-linked consumer's runtime check and a * statically-linked consumer's compile-time PACKFS_VERSION_* agree by * construction, not by two places having to be kept in sync by hand. */ CHECK_STR_EQ(pfs_version(), PACKFS_VERSION_STRING); CHECK_STR_EQ(PACKFS_VERSION_STRING, "0.1.0"); CHECK_EQ_INT(PACKFS_VERSION_MAJOR, 0); CHECK_EQ_INT(PACKFS_VERSION_MINOR, 1); CHECK_EQ_INT(PACKFS_VERSION_PATCH, 0); Vfs *v = vfs_new(); Backend *mem = backend_mem_new(); CHECK_EQ_INT(vfs_mount(v, "/", mem), VFS_OK); /* create + write + read back */ int err = 0; VfsFile *f = vfs_open(v, "/hello.txt", VFS_O_WRONLY | VFS_O_CREAT, &err); CHECK(f != NULL); CHECK_EQ_INT(vfs_write(f, "hello", 5), 5); CHECK_EQ_INT(vfs_close(f), VFS_OK); f = vfs_open(v, "/hello.txt", VFS_O_RDONLY, &err); CHECK(f != NULL); char buf[16] = {0}; CHECK_EQ_INT(vfs_read(f, buf, sizeof(buf)), 5); CHECK_STR_EQ(buf, "hello"); CHECK_EQ_INT(vfs_close(f), VFS_OK); VfsStat st; CHECK_EQ_INT(vfs_stat(v, "/hello.txt", &st), VFS_OK); CHECK_EQ_INT(st.size, 5); CHECK_EQ_INT(st.kind, VFS_KIND_FILE); /* nonexistent path */ CHECK_EQ_INT(vfs_stat(v, "/nope.txt", &st), VFS_ERR_NOENT); /* path escape rejected (Section 6.1) */ f = vfs_open(v, "/../etc/passwd", VFS_O_RDONLY, &err); CHECK(f == NULL); CHECK_EQ_INT(err, VFS_ERR_INVAL); /* directories: explicit empty dir (Section 9.3) and implicit dir from a file */ CHECK_EQ_INT(vfs_mkdir(v, "/empty"), VFS_OK); CHECK_EQ_INT(vfs_stat(v, "/empty", &st), VFS_OK); CHECK_EQ_INT(st.kind, VFS_KIND_DIR); f = vfs_open(v, "/nested/deep/file.txt", VFS_O_WRONLY | VFS_O_CREAT, &err); CHECK(f != NULL); CHECK_EQ_INT(vfs_write(f, "x", 1), 1); CHECK_EQ_INT(vfs_close(f), VFS_OK); CHECK_EQ_INT(vfs_stat(v, "/nested", &st), VFS_OK); CHECK_EQ_INT(st.kind, VFS_KIND_DIR); CHECK_EQ_INT(vfs_stat(v, "/nested/deep", &st), VFS_OK); CHECK_EQ_INT(st.kind, VFS_KIND_DIR); /* readdir at root: hello.txt, empty, nested */ VfsDir dir; CHECK_EQ_INT(vfs_readdir(v, "/", &dir), VFS_OK); int saw_hello = 0, saw_empty = 0, saw_nested = 0; for (pfs_usize i = 0; i < dir.count; i++) { if (strcmp(dir.entries[i].name, "hello.txt") == 0) { saw_hello = 1; CHECK_EQ_INT(dir.entries[i].kind, VFS_KIND_FILE); } if (strcmp(dir.entries[i].name, "empty") == 0) { saw_empty = 1; CHECK_EQ_INT(dir.entries[i].kind, VFS_KIND_DIR); } if (strcmp(dir.entries[i].name, "nested") == 0) { saw_nested = 1; CHECK_EQ_INT(dir.entries[i].kind, VFS_KIND_DIR); } } CHECK(saw_hello && saw_empty && saw_nested); vfs_dir_free(&dir); /* rename */ CHECK_EQ_INT(vfs_rename(v, "/hello.txt", "/hello2.txt"), VFS_OK); CHECK_EQ_INT(vfs_stat(v, "/hello.txt", &st), VFS_ERR_NOENT); CHECK_EQ_INT(vfs_stat(v, "/hello2.txt", &st), VFS_OK); /* unlink + rmdir-of-nonempty rejected */ CHECK_EQ_INT(vfs_unlink(v, "/hello2.txt"), VFS_OK); CHECK_EQ_INT(vfs_unlink(v, "/nested"), VFS_ERR_NOTEMPTY); /* buffer growth across the 64-byte initial capacity (Section 5.7) */ f = vfs_open(v, "/big.bin", VFS_O_WRONLY | VFS_O_CREAT, &err); CHECK(f != NULL); unsigned char chunk[100]; for (int i = 0; i < 100; i++) chunk[i] = (unsigned char)i; for (int i = 0; i < 50; i++) CHECK_EQ_INT(vfs_write(f, chunk, sizeof(chunk)), sizeof(chunk)); CHECK_EQ_INT(vfs_close(f), VFS_OK); CHECK_EQ_INT(vfs_stat(v, "/big.bin", &st), VFS_OK); CHECK_EQ_INT(st.size, 5000); f = vfs_open(v, "/big.bin", VFS_O_RDONLY, &err); unsigned char rb[5000]; CHECK_EQ_INT(vfs_read(f, rb, sizeof(rb)), 5000); CHECK_EQ_INT(memcmp(rb, rb, 0), 0); /* smoke */ int ok = 1; for (int i = 0; i < 5000; i++) if (rb[i] != (unsigned char)(i % 100)) ok = 0; CHECK(ok); CHECK_EQ_INT(vfs_close(f), VFS_OK); vfs_unmount(v, "/"); backend_free(mem); vfs_free(v); TEST_MAIN_END(); }