calog/tests/testArchive.c

152 lines
5.3 KiB
C

// 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 <stdatomic.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#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;
}