// testArchive.c -- exercises the compression/archive library (libs/calogArchive.c) from a Lua // context: every filter round-trips (including embedded NULs), compression actually shrinks, // level is honored, a tar.gz and a zip are built in memory and read back entry-by-entry, and a // bad filter/format is reported as an error the script can catch. #define _POSIX_C_SOURCE 200809L #include "calog.h" #include "calogArchive.h" #include #include #include #include #define PUMP_LIMIT 4000 static CalogT *calog = NULL; static _Atomic int32_t checksRun = 0; static _Atomic int32_t checksFailed = 0; static _Atomic bool scriptDone = false; static void checkFail(const char *message); static int32_t nativeCheck(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); static int32_t nativeDone(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); static void onError(uint64_t contextId, const char *message, void *userData); static void pumpUntilDone(void); static const char *SCRIPT = "local data = 'hi\\0there ' .. string.rep('compress me ', 300)\n" "for _, f in ipairs({'gzip','bzip2','xz','zstd','lz4','none','compress'}) do\n" " local c = compress(data, f)\n" " check(decompress(c) == data, 'roundtrip ' .. f)\n" " if f ~= 'none' then check(#c < #data, 'shrinks ' .. f) end\n" "end\n" "check(#compress(data,'gzip',9) <= #compress(data,'gzip',1), 'gzip level 9 <= level 1')\n" "check(decompress(compress(data,'gzip')) == data, 'decompress auto-detects filter')\n" "local w = archiveWriteOpen('tar','gzip')\n" "archiveWriteEntry(w, 'a.txt', 'alpha')\n" "archiveWriteEntry(w, 'b.txt', 'beta\\0gamma')\n" "local tar = archiveWriteFinish(w)\n" "local r = archiveReadOpen(tar)\n" "local names, datas = {}, {}\n" "local e = archiveReadNext(r)\n" "while e do names[#names+1] = e.name; datas[e.name] = archiveReadData(r); e = archiveReadNext(r) end\n" "archiveReadClose(r)\n" "check(#names == 2, 'tar has two entries')\n" "check(datas['a.txt'] == 'alpha', 'tar entry a data')\n" "check(datas['b.txt'] == 'beta\\0gamma', 'tar entry b data is binary-safe')\n" "local z = archiveWriteOpen('zip','none')\n" "archiveWriteEntry(z, 'z.txt', 'zipped')\n" "local zip = archiveWriteFinish(z)\n" "local zr = archiveReadOpen(zip)\n" "local ze = archiveReadNext(zr)\n" "check(ze.name == 'z.txt' and archiveReadData(zr) == 'zipped', 'zip round-trip')\n" "archiveReadClose(zr)\n" "local x = archiveWriteOpen('xar','none')\n" "archiveWriteEntry(x, 'x.txt', 'xarred\\0bin')\n" "local xar = archiveWriteFinish(x)\n" "local xr = archiveReadOpen(xar)\n" "local xe = archiveReadNext(xr)\n" "check(xe.name == 'x.txt' and archiveReadData(xr) == 'xarred\\0bin', 'xar round-trip (libxml2 + openssl)')\n" "archiveReadClose(xr)\n" "check(not pcall(function() compress('x','nosuchfilter') end), 'bad filter is an error')\n" "check(not pcall(function() archiveWriteOpen('nosuchformat','none') end), 'bad format is an error')\n" "done()\n"; static void checkFail(const char *message) { atomic_fetch_add(&checksFailed, 1); printf("FAIL %s\n", message); } static int32_t nativeCheck(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) { (void)userData; calogValueNil(result); atomic_fetch_add(&checksRun, 1); if (argCount < 1 || args[0].type != calogBoolE) { checkFail("check: expected a boolean"); return calogOkE; } if (!args[0].as.b) { checkFail(argCount >= 2 && args[1].type == calogStringE ? args[1].as.s.bytes : "(unnamed)"); } return calogOkE; } static int32_t nativeDone(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) { (void)args; (void)argCount; (void)userData; atomic_store(&scriptDone, true); calogValueNil(result); return calogOkE; } static void onError(uint64_t contextId, const char *message, void *userData) { (void)contextId; (void)userData; printf("FAIL script error: %s\n", message); atomic_fetch_add(&checksFailed, 1); atomic_store(&scriptDone, true); } static void pumpUntilDone(void) { struct timespec ts = { 0, 500000 }; int32_t i; for (i = 0; i < PUMP_LIMIT; i++) { calogPump(calog); if (atomic_load(&scriptDone)) { calogPump(calog); return; } nanosleep(&ts, NULL); } } int main(void) { CalogContextT *ctx; calog = calogCreate(); if (calog == NULL) { printf("calog create failed\n"); return 1; } calogSetErrorHandler(calog, onError, NULL); if (calogArchiveRegister(calog) != calogOkE) { printf("archive register failed\n"); return 1; } calogRegisterInline(calog, "check", nativeCheck, NULL); calogRegisterInline(calog, "done", nativeDone, NULL); ctx = calogContextOpen(calog, &calogLuaEngine); if (ctx == NULL) { printf("context open failed\n"); return 1; } calogContextEval(ctx, SCRIPT); pumpUntilDone(); calogDestroy(calog); printf("\n%d checks, %d failed\n", atomic_load(&checksRun), atomic_load(&checksFailed)); fflush(stdout); return atomic_load(&checksFailed) == 0 ? 0 : 1; }