152 lines
5.3 KiB
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;
|
|
}
|