/** * POSIX-aligned edge cases for test / expr / printf (subset). */ import { readFile } from 'node:fs/promises' import path from 'node:path' import { fileURLToPath } from 'node:url' import test from 'brittle' import b4a from 'b4a' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor /** * @param {string} name */ async function loadBin(name) { const runtime = await readFile( path.join(__dirname, '../lib/runtime.js'), 'utf8' ) const bodyRaw = await readFile( path.join(__dirname, `../src/${name}.js`), 'utf8' ) const body = bodyRaw.replace(/^export\s+\{[^}]*\}\s*;?\s*$/gm, '') return new AsyncFunction( 'ctx', 'argv', `${runtime}\n${body}\nif (typeof run === 'function') return await run(ctx, argv)\n` ) } function createCtx() { const logs = [] return { b4a, exitCode: 0, vfs: { async stat() { return null } }, console: { log: (m) => logs.push(String(m)), error: (m) => logs.push(String(m)) }, _logs: logs } } test('/bin/test rejects non-decimal integers for -eq', async (t) => { const run = await loadBin('test') const ctx = createCtx() await run(ctx, ['test', '1x', '-eq', '1']) t.is(ctx.exitCode, 1) const ctx2 = createCtx() await run(ctx2, ['test', '008', '-eq', '8']) t.is(ctx2.exitCode, 1) }) test('/bin/printf honors width and zero pad for integers', async (t) => { const run = await loadBin('printf') const ctx = createCtx() await run(ctx, ['printf', '%05d', '7']) t.is(ctx._logs.join(''), '00007') const ctx2 = createCtx() await run(ctx2, ['printf', '%5s', 'hi']) t.is(ctx2._logs.join(''), ' hi') }) test('/bin/printf %c uses first character of string arg', async (t) => { const run = await loadBin('printf') const ctx = createCtx() await run(ctx, ['printf', '%c', 'xy']) t.is(ctx._logs.join(''), 'x') }) test('/bin/expr : returns match length (POSIX-shaped)', async (t) => { const run = await loadBin('expr') const ctx = createCtx() await run(ctx, ['expr', 'foobar', ':', 'foo']) t.is(ctx._logs.join(''), '3') const ctx2 = createCtx() await run(ctx2, ['expr', 'foo', ':', 'bar']) t.is(ctx2._logs.join(''), '0') }) test('/bin/test -nt and -ot use mtimeMs', async (t) => { const run = await loadBin('test') const ctx = { b4a, exitCode: 0, vfs: { async stat(p) { if (p === 'older') return { type: 'file', mtimeMs: 1000, ino: 1, dev: 0 } if (p === 'newer') return { type: 'file', mtimeMs: 2000, ino: 2, dev: 0 } return null } }, console: { log() {}, error() {} } } await run(ctx, ['test', 'newer', '-nt', 'older']) t.is(ctx.exitCode, 0) const ctx2 = { b4a, exitCode: 0, vfs: ctx.vfs, console: { log() {}, error() {} } } await run(ctx2, ['test', 'older', '-ot', 'newer']) t.is(ctx2.exitCode, 0) })