media:cpp docs | http | tests

This commit is contained in:
lovebird 2026-04-14 13:43:50 +02:00
parent c448bfb626
commit 96e21b2282
4 changed files with 235 additions and 151 deletions

View File

@ -298,17 +298,30 @@ export function renderMarkdownReport(payload) {
.replace(/\r\n/g, '<br>')
.replace(/\n/g, '<br>');
const fmtBytesCell = (n) => {
if (n == null || n === '') return '—';
const num = Number(n);
if (!Number.isFinite(num)) return escCell(String(n));
return `${formatBytes(num)} (${num} B)`;
};
const fmtMsCell = (ms) => {
if (ms == null || ms === '') return '—';
const n = Number(ms);
if (!Number.isFinite(n)) return '—';
return String(Math.round(n * 100) / 100);
};
if (Array.isArray(payload.images) && payload.images.length > 0) {
lines.push('## Images & transfers');
lines.push('');
lines.push('| Label | Size | Dimensions / detail |');
lines.push('| --- | --- | --- |');
lines.push('| Label | Input | Output | Time (ms) | Detail |');
lines.push('| --- | --- | --- | ---: | --- |');
for (const row of payload.images) {
const label = escCell(row.label ?? '—');
const bytes =
row.bytes != null && row.bytes !== ''
? `${formatBytes(Number(row.bytes))} (${row.bytes} B)`
: '—';
const outRaw = row.outputBytes != null ? row.outputBytes : row.bytes;
const inputCol = fmtBytesCell(row.inputBytes);
const outputCol = fmtBytesCell(outRaw);
const timeCol = fmtMsCell(row.computeMs != null ? row.computeMs : row.ms);
const parts = [];
if (row.widthPx != null && row.heightPx != null) {
parts.push(`${row.widthPx}×${row.heightPx} px`);
@ -317,7 +330,7 @@ export function renderMarkdownReport(payload) {
if (row.note) parts.push(String(row.note));
if (row.detail) parts.push(String(row.detail));
const detailCol = parts.length ? escCell(parts.join(' · ')) : '—';
lines.push(`| ${label} | ${bytes} | ${detailCol} |`);
lines.push(`| ${label} | ${inputCol} | ${outputCol} | ${timeCol} | ${detailCol} |`);
}
lines.push('');
}

View File

@ -158,7 +158,7 @@ function registerFixtureImages(rep, assetsDir) {
const d = describePngFile(p);
rep.addImage({
label,
bytes: d.bytes,
inputBytes: d.bytes,
widthPx: d.widthPx,
heightPx: d.heightPx,
note: 'on-disk fixture',
@ -171,7 +171,7 @@ function timeSync(rep, label, fn, detailFn) {
const result = fn();
const ms = performance.now() - t0;
rep.step(label, ms, detailFn ? detailFn(result) : '');
return result;
return { result, ms };
}
async function timeAsync(rep, label, fn, detailFn) {
@ -179,7 +179,7 @@ async function timeAsync(rep, label, fn, detailFn) {
const result = await fn();
const ms = performance.now() - t0;
rep.step(label, ms, detailFn ? detailFn(result) : '');
return result;
return { result, ms };
}
function writeTestReportFile(rep, metricsCollector, startedAtIso) {
@ -220,9 +220,10 @@ function writeTestReportFile(rep, metricsCollector, startedAtIso) {
async function multipartResizeTests(base, inPng, rep) {
const pngBytes = readFileSync(inPng);
const srcDim = pngDimensionsFromBuffer(pngBytes);
const inPngSize = fileByteSize(inPng) ?? pngBytes.length;
rep.addImage({
label: 'multipart upload source (PNG body in form)',
bytes: pngBytes.length,
inputBytes: inPngSize,
widthPx: srcDim?.width ?? null,
heightPx: srcDim?.height ?? null,
note: 'fixture bytes attached to each multipart request',
@ -233,25 +234,30 @@ async function multipartResizeTests(base, inPng, rep) {
form.append('file', blob(), 'square-64.png');
form.append('max_width', '32');
form.append('max_height', '32');
const r1 = await timeAsync(
const { result: r1, ms: r1Ms } = await timeAsync(
rep,
'multipart POST (file → jpeg)',
() =>
fetch(`${base}/v1/resize`, {
async () => {
const res = await fetch(`${base}/v1/resize`, {
method: 'POST',
body: form,
signal: AbortSignal.timeout(timeouts.httpMs),
}),
(res) => `HTTP ${res.status}, ${res.headers.get('content-type') ?? ''}`,
});
const ab = await res.arrayBuffer();
return { res, ab };
},
(x) => `HTTP ${x.res.status}, ${x.res.headers.get('content-type') ?? ''}`,
);
assert(r1.ok, 'multipart: field file');
assert(r1.headers.get('content-type') === 'image/jpeg', 'multipart default format → image/jpeg');
assert(r1.res.ok, 'multipart: field file');
assert(r1.res.headers.get('content-type') === 'image/jpeg', 'multipart default format → image/jpeg');
{
const ab = await r1.arrayBuffer();
const ab = r1.ab;
rep.addImage({
label: 'multipart HTTP response (→ jpeg)',
bytes: ab.byteLength,
contentType: r1.headers.get('content-type') ?? '',
inputBytes: inPngSize,
outputBytes: ab.byteLength,
computeMs: r1Ms,
contentType: r1.res.headers.get('content-type') ?? '',
note: 'resized output body',
});
assert(Buffer.from(ab).length > 0, 'multipart file: non-empty body');
@ -261,25 +267,30 @@ async function multipartResizeTests(base, inPng, rep) {
fImage.append('image', blob(), 'x.png');
fImage.append('max_width', '24');
fImage.append('format', 'webp');
const r2 = await timeAsync(
const { result: r2, ms: r2Ms } = await timeAsync(
rep,
'multipart POST (image → webp)',
() =>
fetch(`${base}/v1/resize`, {
async () => {
const res = await fetch(`${base}/v1/resize`, {
method: 'POST',
body: fImage,
signal: AbortSignal.timeout(timeouts.httpMs),
}),
(res) => `HTTP ${res.status}, ${res.headers.get('content-type') ?? ''}`,
});
const ab = await res.arrayBuffer();
return { res, ab };
},
(x) => `HTTP ${x.res.status}, ${x.res.headers.get('content-type') ?? ''}`,
);
assert(r2.ok, 'multipart: field image + format webp');
assert(r2.headers.get('content-type') === 'image/webp', 'multipart webp → image/webp');
assert(r2.res.ok, 'multipart: field image + format webp');
assert(r2.res.headers.get('content-type') === 'image/webp', 'multipart webp → image/webp');
{
const ab = await r2.arrayBuffer();
const ab = r2.ab;
rep.addImage({
label: 'multipart HTTP response (→ webp)',
bytes: ab.byteLength,
contentType: r2.headers.get('content-type') ?? '',
inputBytes: inPngSize,
outputBytes: ab.byteLength,
computeMs: r2Ms,
contentType: r2.res.headers.get('content-type') ?? '',
note: 'resized output body',
});
assert(Buffer.from(ab).byteLength > 8, 'multipart webp: RIFF/WebP header size');
@ -288,25 +299,30 @@ async function multipartResizeTests(base, inPng, rep) {
const fUpload = new FormData();
fUpload.append('upload', blob(), 'up.png');
fUpload.append('max_width', '20');
const r3 = await timeAsync(
const { result: r3, ms: r3Ms } = await timeAsync(
rep,
'multipart POST (upload alias → jpeg)',
() =>
fetch(`${base}/v1/resize`, {
async () => {
const res = await fetch(`${base}/v1/resize`, {
method: 'POST',
body: fUpload,
signal: AbortSignal.timeout(timeouts.httpMs),
}),
(res) => `HTTP ${res.status}, ${res.headers.get('content-type') ?? ''}`,
});
const ab = await res.arrayBuffer();
return { res, ab };
},
(x) => `HTTP ${x.res.status}, ${x.res.headers.get('content-type') ?? ''}`,
);
assert(r3.ok, 'multipart: field upload');
assert(r3.headers.get('content-type') === 'image/jpeg', 'multipart upload alias → jpeg');
assert(r3.res.ok, 'multipart: field upload');
assert(r3.res.headers.get('content-type') === 'image/jpeg', 'multipart upload alias → jpeg');
{
const ab = await r3.arrayBuffer();
const ab = r3.ab;
rep.addImage({
label: 'multipart HTTP response (upload field → jpeg)',
bytes: ab.byteLength,
contentType: r3.headers.get('content-type') ?? '',
inputBytes: inPngSize,
outputBytes: ab.byteLength,
computeMs: r3Ms,
contentType: r3.res.headers.get('content-type') ?? '',
note: 'resized output body',
});
assert(Buffer.from(ab).length > 0, 'multipart upload: non-empty body');
@ -316,54 +332,62 @@ async function multipartResizeTests(base, inPng, rep) {
fPng.append('file', blob(), 'square-64.png');
fPng.append('max_width', '16');
fPng.append('format', 'png');
const r4 = await timeAsync(
const { result: r4, ms: r4Ms } = await timeAsync(
rep,
'multipart POST (file → png)',
() =>
fetch(`${base}/v1/resize`, {
async () => {
const res = await fetch(`${base}/v1/resize`, {
method: 'POST',
body: fPng,
signal: AbortSignal.timeout(timeouts.httpMs),
}),
(res) => `HTTP ${res.status}, ${res.headers.get('content-type') ?? ''}`,
});
const ab = await res.arrayBuffer();
return { res, ab };
},
(x) => `HTTP ${x.res.status}, ${x.res.headers.get('content-type') ?? ''}`,
);
assert(r4.ok, 'multipart: format png');
assert(r4.headers.get('content-type') === 'image/png', 'multipart png → image/png');
assert(r4.res.ok, 'multipart: format png');
assert(r4.res.headers.get('content-type') === 'image/png', 'multipart png → image/png');
{
const ab = await r4.arrayBuffer();
const ab = r4.ab;
const dim = pngDimensionsFromBuffer(Buffer.from(ab));
rep.addImage({
label: 'multipart HTTP response (→ png)',
bytes: ab.byteLength,
inputBytes: inPngSize,
outputBytes: ab.byteLength,
computeMs: r4Ms,
widthPx: dim?.width ?? null,
heightPx: dim?.height ?? null,
contentType: r4.headers.get('content-type') ?? '',
contentType: r4.res.headers.get('content-type') ?? '',
note: 'resized output body',
});
}
const noFile = new FormData();
noFile.append('max_width', '10');
const r5 = await timeAsync(
const { result: r5, ms: r5Ms } = await timeAsync(
rep,
'multipart POST (no file → 400)',
() =>
fetch(`${base}/v1/resize`, {
async () => {
const res = await fetch(`${base}/v1/resize`, {
method: 'POST',
body: noFile,
signal: AbortSignal.timeout(timeouts.httpMs),
}),
(res) => `HTTP ${res.status}`,
});
const errText = await res.text();
return { res, errText };
},
(x) => `HTTP ${x.res.status}`,
);
assert(r5.status === 400, 'multipart without image → 400');
const errText = await r5.text();
assert(r5.res.status === 400, 'multipart without image → 400');
rep.addImage({
label: 'multipart HTTP 400 error body',
bytes: Buffer.byteLength(errText, 'utf8'),
contentType: r5.headers.get('content-type') ?? '',
outputBytes: Buffer.byteLength(r5.errText, 'utf8'),
computeMs: r5Ms,
contentType: r5.res.headers.get('content-type') ?? '',
note: 'JSON error',
});
const jErr = JSON.parse(errText);
const jErr = JSON.parse(r5.errText);
assert(jErr?.error && typeof jErr.error === 'string', 'multipart 400 JSON error body');
}
@ -383,7 +407,7 @@ async function suiteMultipartOnly(assetsDir, rep) {
try {
await timeAsync(rep, 'wait until HTTP server accepts', () => waitListen('127.0.0.1', port, 'serve'), () => `127.0.0.1:${port}`);
const base = `http://127.0.0.1:${port}`;
const h = await timeAsync(
const { result: h } = await timeAsync(
rep,
'GET /health',
() => fetch(`${base}/health`, { signal: AbortSignal.timeout(timeouts.httpMs) }),
@ -418,7 +442,7 @@ async function suiteRest(assetsDir, rep) {
await timeAsync(rep, 'wait until HTTP server accepts', () => waitListen('127.0.0.1', port, 'serve'), () => `127.0.0.1:${port}`);
const base = `http://127.0.0.1:${port}`;
const h = await timeAsync(
const { result: h } = await timeAsync(
rep,
'GET /health',
() => fetch(`${base}/health`, { signal: AbortSignal.timeout(timeouts.httpMs) }),
@ -430,11 +454,12 @@ async function suiteRest(assetsDir, rep) {
const outDir = mkdtempSync(join(tmpdir(), 'media-rest-'));
const outPng = join(outDir, 'out-32.png');
const r1 = await timeAsync(
const inPngBytes = fileByteSize(inPng);
const { result: r1, ms: r1Ms } = await timeAsync(
rep,
'POST /v1/resize JSON → PNG on disk',
() =>
fetch(`${base}/v1/resize`, {
async () => {
const res = await fetch(`${base}/v1/resize`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@ -444,18 +469,23 @@ async function suiteRest(assetsDir, rep) {
max_height: 32,
}),
signal: AbortSignal.timeout(timeouts.httpMs),
}),
(res) => `HTTP ${res.status}`,
});
const j = await res.json();
return { res, j };
},
(x) => `HTTP ${x.res.status}`,
);
assert(r1.ok, 'POST /v1/resize ok');
const j1 = await r1.json();
assert(r1.res.ok, 'POST /v1/resize ok');
const j1 = r1.j;
assert(j1?.ok === true, 'resize response ok');
assert(existsSync(outPng), 'output png exists');
{
const d = describePngFile(outPng);
rep.addImage({
label: 'REST JSON → disk (out-32.png)',
bytes: d.bytes,
inputBytes: inPngBytes,
outputBytes: d.bytes,
computeMs: r1Ms,
widthPx: d.widthPx,
heightPx: d.heightPx,
note: 'server wrote from JSON resize',
@ -463,11 +493,11 @@ async function suiteRest(assetsDir, rep) {
}
const outJpg = join(outDir, 'out.jpg');
const r2 = await timeAsync(
const { result: r2, ms: r2Ms } = await timeAsync(
rep,
'POST /v1/resize JSON → JPEG on disk',
() =>
fetch(`${base}/v1/resize`, {
async () => {
const res = await fetch(`${base}/v1/resize`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@ -477,17 +507,22 @@ async function suiteRest(assetsDir, rep) {
format: 'jpeg',
}),
signal: AbortSignal.timeout(timeouts.httpMs),
}),
(res) => `HTTP ${res.status}`,
});
const j = await res.json();
return { res, j };
},
(x) => `HTTP ${x.res.status}`,
);
assert(r2.ok, 'POST /v1/resize jpeg');
assert(r2.res.ok, 'POST /v1/resize jpeg');
assert(existsSync(outJpg), 'output jpg exists');
{
const b = fileByteSize(outJpg);
if (b != null) {
rep.addImage({
label: 'REST JSON → disk (out.jpg)',
bytes: b,
inputBytes: inPngBytes,
outputBytes: b,
computeMs: r2Ms,
contentType: 'image/jpeg',
note: 'server wrote from JSON resize',
});
@ -496,7 +531,7 @@ async function suiteRest(assetsDir, rep) {
await multipartResizeTests(base, inPng, rep);
const bad = await timeAsync(
const { result: bad } = await timeAsync(
rep,
'POST /v1/resize JSON missing input file → 500',
() =>
@ -537,7 +572,7 @@ async function suiteDstTemplateRest(assetsDir, rep) {
const outDir = mkdtempSync(join(tmpdir(), 'media-dst-rest-'));
const outPattern = join(outDir, '${SRC_NAME}_thumb.webp');
const rt = await timeAsync(
const { result: rt } = await timeAsync(
rep,
'POST ${SRC_NAME}_thumb.webp template',
() =>
@ -561,7 +596,7 @@ async function suiteDstTemplateRest(assetsDir, rep) {
assert(existsSync(expectedWebp), `expected ${expectedWebp}`);
const outAmp = join(outDir, '&{SRC_NAME}_tiny.png');
const r2 = await timeAsync(
const { result: r2 } = await timeAsync(
rep,
'POST &{SRC_NAME} + expand_glob false',
() =>
@ -583,7 +618,7 @@ async function suiteDstTemplateRest(assetsDir, rep) {
const subDir = join(assetsDir, 'tpl-sub');
const outNested = join(subDir, '${SRC_NAME}_nested.png');
const rn = await timeAsync(
const { result: rn } = await timeAsync(
rep,
'POST nested ${SRC_NAME} template',
() =>
@ -604,7 +639,7 @@ async function suiteDstTemplateRest(assetsDir, rep) {
assert(existsSync(expectedNested), `nested template ${expectedNested}`);
const outViaSrcDir = '${SRC_DIR}/tpl-from-srcdir/${SRC_NAME}_sd.webp';
const rsd = await timeAsync(
const { result: rsd } = await timeAsync(
rep,
'POST ${SRC_DIR} in output path',
() =>
@ -654,8 +689,8 @@ async function suiteIpcTcp(assetsDir, rep) {
const outDir = mkdtempSync(join(tmpdir(), 'media-ipc-tcp-'));
const outPng = join(outDir, 'ipc-out.png');
const sock = await timeAsync(rep, 'TCP connect (line 1)', () => connectTcp('127.0.0.1', port), () => '');
const res = await timeAsync(
const { result: sock } = await timeAsync(rep, 'TCP connect (line 1)', () => connectTcp('127.0.0.1', port), () => '');
const { result: res, ms: ipcMs } = await timeAsync(
rep,
'IPC JSON line (ok resize)',
() =>
@ -676,17 +711,20 @@ async function suiteIpcTcp(assetsDir, rep) {
assert(existsSync(outPng), 'IPC output file exists');
{
const d = describePngFile(outPng);
const inB = fileByteSize(inPng);
rep.addImage({
label: 'IPC TCP → disk (ipc-out.png)',
bytes: d.bytes,
inputBytes: inB,
outputBytes: d.bytes,
computeMs: ipcMs,
widthPx: d.widthPx,
heightPx: d.heightPx,
note: 'line-JSON resize',
});
}
const sock2 = await timeAsync(rep, 'TCP connect (line 2)', () => connectTcp('127.0.0.1', port), () => '');
const res2 = await timeAsync(
const { result: sock2 } = await timeAsync(rep, 'TCP connect (line 2)', () => connectTcp('127.0.0.1', port), () => '');
const { result: res2 } = await timeAsync(
rep,
'IPC JSON line (missing input)',
() =>
@ -722,8 +760,8 @@ async function suiteDstTemplateIpcTcp(assetsDir, rep) {
const outDir = mkdtempSync(join(tmpdir(), 'media-dst-ipc-'));
const outPattern = join(outDir, '${SRC_NAME}_ipc.webp');
const sock = await timeAsync(rep, 'TCP connect', () => connectTcp('127.0.0.1', port), () => '');
const res = await timeAsync(
const { result: sock } = await timeAsync(rep, 'TCP connect', () => connectTcp('127.0.0.1', port), () => '');
const { result: res } = await timeAsync(
rep,
'IPC JSON line (${SRC_NAME}_ipc.webp)',
() =>
@ -762,7 +800,7 @@ function suiteDstTemplateCli(assetsDir, rep) {
rep.beginSuite('CLI: resize --src / --dst templates');
try {
const r = timeSync(
const { result: r } = timeSync(
rep,
'spawnSync resize (template dst)',
() =>
@ -804,7 +842,7 @@ function suiteUrlResizeCli(rep) {
/* ignore */
}
}
const r = timeSync(
const { result: r, ms: urlMs } = timeSync(
rep,
`spawnSync resize URL → ${expect}`,
() =>
@ -821,6 +859,13 @@ function suiteUrlResizeCli(rep) {
);
assert(r.status === 0, `resize URL exit 0 (${expect}): ${r.stderr || r.stdout}`);
assert(existsSync(outPath), `expected output ${outPath}`);
const outSz = fileByteSize(outPath);
rep.addImage({
label: `CLI URL resize → ${expect}`,
outputBytes: outSz,
computeMs: urlMs,
note: 'HTTPS source + resize',
});
}
} finally {
rep.endSuite();
@ -838,26 +883,35 @@ function suiteGlobBatchCli(assetsDir, rep) {
assert(existsSync(rootPng), `fixture ${rootPng}`);
assert(existsSync(leafPng), `fixture ${leafPng}`);
const globIn = join(assetsDir, 'glob-in', '**', '*.png').replace(/\\/g, '/');
const dstTmpl = '${SRC_DIR}/out/${SRC_NAME}_medium.jpg';
const jobs = [
{ src: rootPng, out: join(assetsDir, 'glob-in', 'out', 'root_medium.jpg') },
{ src: leafPng, out: join(assetsDir, 'glob-in', 'sub', 'out', 'leaf_medium.jpg') },
];
rep.beginSuite('CLI: recursive glob batch + dst templates');
try {
const r = timeSync(
rep,
'spawnSync resize (glob **/*.png → _medium.jpg)',
() =>
spawnSync(EXE, ['resize', '--src', globIn, '--dst', dstTmpl, '--max-width', '40', '--format', 'jpeg'], {
encoding: 'utf8',
}),
(x) => `exit ${x.status}, 2 files`,
);
assert(r.status === 0, `glob batch exit 0, stderr: ${r.stderr}`);
const rootOut = join(assetsDir, 'glob-in', 'out', 'root_medium.jpg');
const leafOut = join(assetsDir, 'glob-in', 'sub', 'out', 'leaf_medium.jpg');
assert(existsSync(rootOut), `expected ${rootOut}`);
assert(existsSync(leafOut), `expected ${leafOut}`);
for (const job of jobs) {
const srcAbs = resolve(job.src).replace(/\\/g, '/');
const { result: r, ms } = timeSync(
rep,
`spawnSync resize (${basename(job.src)} → _medium.jpg)`,
() =>
spawnSync(EXE, ['resize', '--src', srcAbs, '--dst', dstTmpl, '--max-width', '40', '--format', 'jpeg'], {
encoding: 'utf8',
}),
(x) => `exit ${x.status}`,
);
assert(r.status === 0, `glob batch exit 0, stderr: ${r.stderr}`);
assert(existsSync(job.out), `expected ${job.out}`);
rep.addImage({
label: `CLI glob: ${basename(job.src)}${basename(job.out)}`,
inputBytes: fileByteSize(job.src),
outputBytes: fileByteSize(job.out),
computeMs: ms,
note: '${SRC_DIR}/out/${SRC_NAME}_medium.jpg',
});
}
} finally {
rep.endSuite();
}
@ -903,28 +957,32 @@ function suiteGlobBatchRawArw(assetsDir, rep) {
return;
}
// Explicit --src list: recursive *.arw glob is case-sensitive; enumerate paths so .ARW matches.
const absSrc = arwFiles.map((p) => resolve(p).replace(/\\/g, '/'));
const srcArgs = absSrc.flatMap((p) => ['--src', p]);
const dstTmpl = '${SRC_DIR}/out/${SRC_NAME}_medium.jpg';
rep.beginSuite('CLI: recursive glob batch (raw **/*.arw) + dst templates');
try {
const r = timeSync(
rep,
`spawnSync resize (${arwFiles.length}× --src .arw/.ARW → _medium.jpg)`,
() =>
spawnSync(EXE, ['resize', ...srcArgs, '--dst', dstTmpl, '--max-width', '40', '--format', 'jpeg'], {
encoding: 'utf8',
}),
(x) => `exit ${x.status}, ${arwFiles.length} file(s)`,
);
assert(r.status === 0, `glob batch raw exit 0, stderr: ${r.stderr}`);
for (const arwPath of arwFiles) {
const srcAbs = resolve(arwPath).replace(/\\/g, '/');
const stem = basename(arwPath).replace(/\.arw$/i, '');
const expected = join(dirname(arwPath), 'out', `${stem}_medium.jpg`);
const { result: r, ms } = timeSync(
rep,
`spawnSync resize (${basename(arwPath)} → _medium.jpg)`,
() =>
spawnSync(EXE, ['resize', '--src', srcAbs, '--dst', dstTmpl, '--max-width', '40', '--format', 'jpeg'], {
encoding: 'utf8',
}),
(x) => `exit ${x.status}`,
);
assert(r.status === 0, `glob batch raw exit 0, stderr: ${r.stderr}`);
assert(existsSync(expected), `expected ${expected}`);
rep.addImage({
label: `CLI raw: ${basename(arwPath)}${basename(expected)}`,
inputBytes: fileByteSize(arwPath),
outputBytes: fileByteSize(expected),
computeMs: ms,
note: 'ARW → JPEG',
});
}
} finally {
rep.endSuite();
@ -964,7 +1022,7 @@ async function suiteIpcUnix(assetsDir, rep) {
throw new Error('unix socket path did not appear');
}, () => path);
const res = await timeAsync(
const { result: res, ms: udsMs } = await timeAsync(
rep,
'Unix connect + IPC JSON line',
async () => {
@ -996,9 +1054,12 @@ async function suiteIpcUnix(assetsDir, rep) {
assert(existsSync(outPng), 'UDS output file exists');
{
const d = describePngFile(outPng);
const inB = fileByteSize(inPng);
rep.addImage({
label: 'IPC Unix → disk (uds-out.png)',
bytes: d.bytes,
inputBytes: inB,
outputBytes: d.bytes,
computeMs: udsMs,
widthPx: d.widthPx,
heightPx: d.heightPx,
note: 'UDS line-JSON resize',

View File

@ -5,37 +5,37 @@
| Key | Value |
| --- | --- |
| Result | PASS |
| Assertions passed | 4 |
| Assertions passed | 8 |
| Assertions failed | 0 |
| CWD | `C:\Users\zx\Desktop\polymech\polymech-mono\packages\media\cpp` |
| Test binary | `C:\Users\zx\Desktop\polymech\polymech-mono\packages\media\cpp\dist\pm-image.exe` |
| Assets dir | `C:\Users\zx\Desktop\polymech\polymech-mono\packages\media\cpp\tests\assets` |
| CLI args | `--url-only` |
| Wall clock (total script) | **1221.57 ms** |
| CLI args | `--glob-batch-only --glob-raw` |
| Wall clock (total script) | **10594.59 ms** |
## Timing
| Metric | Value |
| --- | --- |
| Started (ISO) | 2026-04-14T11:31:18.200Z |
| Finished (ISO) | 2026-04-14T11:31:19.422Z |
| Wall time (perf) | 1225.109 ms |
| Wall time (clock) | 1225 ms |
| Started (ISO) | 2026-04-14T11:43:16.994Z |
| Finished (ISO) | 2026-04-14T11:43:27.589Z |
| Wall time (perf) | 10597.975 ms |
| Wall time (clock) | 10598 ms |
## Process (Node)
| Metric | Value |
| --- | --- |
| PID | 33980 |
| PID | 28312 |
| Node | v24.13.0 |
| process.uptime() | 1.266 s |
| CPU user (process.cpuUsage Δ) | 0.000 ms (0 µs) |
| process.uptime() | 10.633 s |
| CPU user (process.cpuUsage Δ) | 32.000 ms (32000 µs) |
| CPU system (process.cpuUsage Δ) | 0.000 ms (0 µs) |
| CPU user (resourceUsage) | 62.000 ms |
| CPU user (resourceUsage) | 78.000 ms |
| CPU system (resourceUsage) | 15.000 ms |
| Max RSS (resourceUsage) | 39 MB |
| RSS | 39 MB (40816640 B) |
| Heap used | 6.0 MB |
| RSS | 39 MB (40824832 B) |
| Heap used | 6.2 MB |
| Heap total | 11 MB |
| External | 2.2 MB |
| Array buffers | 16 KB |
@ -50,33 +50,39 @@
| CPUs | 16 |
| CPU model | AMD Ryzen 7 3700X 8-Core Processor |
| RAM total | 64 GB |
| RAM free | 38 GB |
| RAM used | 26 GB |
| RAM free | 36 GB |
| RAM used | 27 GB |
| Load avg (1/5/15) | 0.00 / 0.00 / 0.00 |
| OS uptime | 123.71 h |
| OS uptime | 123.91 h |
## Images & transfers
| Label | Size | Dimensions / detail |
| --- | --- | --- |
| fixture square-64.png | 551 B (551 B) | 64×64 px · on-disk fixture |
| fixture checker-128x128.png | 455 B (455 B) | 128×128 px · on-disk fixture |
| fixture glob-in/root.png | 384 B (384 B) | 48×48 px · on-disk fixture |
| fixture glob-in/sub/leaf.png | 88 B (88 B) | 24×24 px · on-disk fixture |
| Label | Input | Output | Time (ms) | Detail |
| --- | --- | --- | ---: | --- |
| fixture square-64.png | 551 B (551 B) | — | — | 64×64 px · on-disk fixture |
| fixture checker-128x128.png | 455 B (455 B) | — | — | 128×128 px · on-disk fixture |
| fixture glob-in/root.png | 384 B (384 B) | — | — | 48×48 px · on-disk fixture |
| fixture glob-in/sub/leaf.png | 88 B (88 B) | — | — | 24×24 px · on-disk fixture |
| CLI raw: DSC02717.ARW → DSC02717_medium.jpg | 24 MB (24824576 B) | 1.1 KB (1123 B) | 2555.37 | ARW → JPEG |
| CLI raw: DSC02718.ARW → DSC02718_medium.jpg | 24 MB (24791808 B) | 1.0 KB (1039 B) | 2669.04 | ARW → JPEG |
| CLI raw: DSC02719.ARW → DSC02719_medium.jpg | 24 MB (24836864 B) | 1.1 KB (1125 B) | 2660.4 | ARW → JPEG |
| CLI raw: DSC02720.ARW → DSC02720_medium.jpg | 24 MB (24820480 B) | 1.0 KB (1029 B) | 2701.57 | ARW → JPEG |
## Integration: performance by suite
### CLI: resize HTTPS URL (picsum.photos)
### CLI: recursive glob batch (raw **/*.arw) + dst templates
- **Suite wall time:** 1216.33 ms
- **Suite wall time:** 10589.98 ms
| Step | ms | Detail |
| --- | ---: | --- |
| spawnSync resize URL → 200.jpg | 251.58 | exit 0, network+libvips |
| spawnSync resize URL → 1600.jpg | 963.61 | exit 0, network+libvips |
| spawnSync resize (DSC02717.ARW → _medium.jpg) | 2555.37 | exit 0 |
| spawnSync resize (DSC02718.ARW → _medium.jpg) | 2669.04 | exit 0 |
| spawnSync resize (DSC02719.ARW → _medium.jpg) | 2660.4 | exit 0 |
| spawnSync resize (DSC02720.ARW → _medium.jpg) | 2701.57 | exit 0 |
---
*Written 2026-04-14T11:31:19.422Z*
*Written 2026-04-14T11:43:27.589Z*
---

View File

@ -0,0 +1,4 @@
## Videos
https://www.mltframework.org/features/
https://github.com/olive-editor/olive?tab=readme-ov-file