import test from 'brittle' import { wantPosixSocketFdBridge, wantPosixFcntlBlockingWait, bareOsPosixDgramRecvQueueMax, bareOsPosixAcceptQueueMax, bareOsFlattenSendmsgIovs, bareOsSendmsgAncillaryRejectReason, bareOsResolveCooperativeLockPath } from './lib/posix/bare-os-posix-syscall-helpers.js' import { redactAuditShellLine } from './lib/security/bare-os-audit-redact.js' import { isExecLineBuiltinDenied, shellCommandDeniedByPolicy, shellUnsafeRedirectPath } from './lib/shell/shell-policy.js' import { joinLogical, dirnameAbs, pathPrefixes, isHyperdriveRootPath } from './lib/vfs/vfs-path.js' import { bareOsProtomuxTuningFromEnv } from './lib/p2p/swarm-opts.js' import { bareOsDupLogicalFdForScm, prepareBridgeSendmsg, bareOsShutdownSocketBridgeFd } from './lib/posix/bare-os-socket-bridge.js' import { createStockBareOsHrpcRequest } from './lib/tools/bare-os-hrpc-stock-request.js' import { splitTopLevelStatements, isValidShellIdentifier, parseShellFunctionDeclaration, casePatternMatches } from './lib/shell/shell-syntax.js' import { createVfsPersonalLayout } from './lib/vfs/vfs-personal.js' import { listBareOsShellBuiltins, isShellBuiltin, bareOsShellReadSplitFields } from './lib/shell/shell-builtins.js' import { getBareOsPipelineLimits, mergePipelineChildCtx, DEFAULT_PIPELINE_MAX_STAGES } from './lib/shell/shell-runtime.js' import { expandWord, expandShellWordTokens } from './lib/shell/shell-expand.js' import { utf8Encode, createVfsPseudoLinux } from './lib/vfs/vfs-pseudo-linux.js' import { createBareOsVirtualSignalDeliverer } from './lib/posix/bare-os-virtual-signal.js' import { createKernelLoaderAuditAppend } from './lib/host/bare-os-loader-audit.js' import { createBooterBootEmitter } from './lib/boot/bare-os-boot-phases.js' import { createBooterProcHelpers } from './lib/proc/bare-os-booter-proc-helpers.js' import { tokenize } from './lib/shell/shell-token.js' import { defaultShellAliases, expandArgvAliases, applyAliasDefinition } from './lib/shell/shell-alias.js' import { parsePipeline, splitTokensBySemicolon, splitTokensByAndOr, planShellRedirections, buildShellExecutionGraph } from './lib/shell/shell-parse.js' import { assertUnionWriteNotDenied, assertNotBootPolicyDenyVfs } from './lib/vfs/vfs-policy.js' import { createVfsWarmReadCache } from './lib/vfs/vfs-warm-cache.js' import { createSessionReadMaskedLine } from './lib/cli/cli-readline.js' import { bareOsPipelineChildCtx, bareOsPipelineRawChunkToText, runWithShellPipelineStageTimeout } from './lib/shell/shell-pipeline.js' import { assignShellFunctionPositionalEnv, tryReportMisplacedReservedStatementStart, casePatternList, execDoubleBracketLimited, bareOsResetShellIdentityState, consumeInteractiveHeredoc } from './lib/shell/shell-stmt.js' import { createVfsRouteHelpers } from './lib/vfs/vfs-route.js' import { createBareOsPosixFdSimMethods } from './lib/posix/bare-os-posix-fd-sim.js' import { createBareOsPathconfMethods } from './lib/posix/bare-os-pathconf.js' import { createBareOsLogicalFdMethods } from './lib/posix/bare-os-logical-fd.js' import { createVfsPseudoFileBytes } from './lib/vfs/vfs-pseudo-bytes.js' import { createBareOsLifecycleHookMethods } from './lib/host/bare-os-lifecycle-hooks.js' import { createBareOsProcessControlMethods } from './lib/posix/bare-os-process-control.js' test('posix env flags and caps', (t) => { t.ok(wantPosixSocketFdBridge({ BARE_OS_POSIX_SOCKET_FD_BRIDGE: '1' })) t.ok(!wantPosixSocketFdBridge({})) t.ok(wantPosixFcntlBlockingWait({ BARE_OS_POSIX_FCNTL_BLOCKING_WAIT: 'yes' })) t.is(bareOsPosixDgramRecvQueueMax({}), 256) t.is(bareOsPosixDgramRecvQueueMax({ BARE_OS_POSIX_DGRAM_RECVQ_MAX: '8' }), 8) t.is(bareOsPosixAcceptQueueMax({}), 64) }) test('sendmsg flatten and ancillary reject', (t) => { const a = bareOsFlattenSendmsgIovs({ iovs: [{ buf: new Uint8Array([1, 2]) }] }) t.ok(a.ok) t.is(a.buf.byteLength, 2) t.is(bareOsSendmsgAncillaryRejectReason({ cmsgs: [{}] }), 'cmsgs') t.is(bareOsSendmsgAncillaryRejectReason({}), null) t.is( bareOsResolveCooperativeLockPath( { fd: 7 }, { bareOsLogicalFds: { 7: '/tmp/x' } } ), '/tmp/x' ) }) test('audit redact and shell policy', (t) => { t.is(redactAuditShellLine('PASSWORD=secret TOKEN=x', {}), 'PASSWORD=secret TOKEN=x') t.is( redactAuditShellLine('PASSWORD=secret TOKEN=x', { BARE_OS_AUDIT_REDACT: '1' }), 'PASSWORD= TOKEN=' ) t.ok(isExecLineBuiltinDenied('cd', { BARE_OS_BOOT_POLICY_DENY_EXEC_LINE_BUILTINS: 'cd,export' })) t.ok(!isExecLineBuiltinDenied('ls', { BARE_OS_BOOT_POLICY_DENY_EXEC_LINE_BUILTINS: 'cd' })) t.ok(shellCommandDeniedByPolicy('rm', { BARE_OS_SHELL_DENY_COMMANDS: 'rm' })) t.ok( shellUnsafeRedirectPath('/proc/self/mem', { BARE_OS_SHELL_REDIRECT_GUARD: '1' }) ) }) test('vfs path helpers', (t) => { t.ok(isHyperdriveRootPath('/')) t.is(joinLogical('/', 'bin'), '/bin') t.is(dirnameAbs('/home/user/x'), '/home/user') t.alike(pathPrefixes('/a/b'), ['/', '/a', '/a/b']) }) test('protomux tuning json', (t) => { t.is(bareOsProtomuxTuningFromEnv({}), null) t.alike(bareOsProtomuxTuningFromEnv({ BARE_OS_PROTOMUX_TUNING_JSON: '{' }), { schema: 1, parseError: true }) t.is( bareOsProtomuxTuningFromEnv({ BARE_OS_PROTOMUX_TUNING_JSON: '{"max":1}' }).max, 1 ) }) function mockBridgeCtx(extra = {}) { const logical = { 8: '/tmp/x' } const flags = { 8: 0x800 } const sim = { nextFd: 20, nextPairId: 1, queues: Object.create(null), byteTotals: Object.create(null) } const registered = [] return { env: {}, bareOsLogicalFds: logical, bareOsLogicalFdFlags: flags, bareOsPosixFdSimState: sim, bareOsSocketBridgeByFd: Object.create(null), bareOsRegisterLogicalFd(fd, target) { logical[String(fd)] = target registered.push([fd, target]) }, bareOsUnregisterLogicalFd() {}, _registered: registered, ...extra } } test('socket bridge logical dup and sendmsg payload', (t) => { const c = mockBridgeCtx() const n = bareOsDupLogicalFdForScm(c, 8) t.is(n, 20) t.is(c.bareOsLogicalFds['20'], '/tmp/x') t.is(c.bareOsLogicalFdFlags['20'], 0x800) const prep = prepareBridgeSendmsg(c, { iovs: [{ buf: new Uint8Array([9, 8, 7]) }] }) t.ok(prep.ok) t.is(prep.payload.byteLength, 3) t.alike(prep.scmRightsLocalDup, []) }) test('prepareBridgeSendmsg rejects ancillary without SCM_RIGHTS', (t) => { const c = mockBridgeCtx() const prep = prepareBridgeSendmsg(c, { iovs: [{ buf: new Uint8Array([1]) }], cmsgs: [{ fds: [8] }] }) t.absent(prep.ok) t.is(prep.err.code, 'ENOTSUP') }) test('bareOsShutdownSocketBridgeFd EINVAL on unknown fd', async (t) => { const c = mockBridgeCtx() const r = await bareOsShutdownSocketBridgeFd(c, 99) t.absent(r.ok) t.is(r.code, 'EINVAL') }) test('stock hrpc kernel.ping and allowlist', async (t) => { const audit = [] const hrpc = createStockBareOsHrpcRequest({ env: { BARE_OS_HRPC_AUDIT: '1' }, sessionId: 'sess1', vfs: { readFile: async () => new Uint8Array() }, disk: {}, capabilityWords: { primary: 1 }, protocolVersion: '0.0-test', booterVersion: '0.0-test', auditAppend: (row) => audit.push(row) }) t.ok(hrpc.__bareOsStockHrpcRequest) const ping = await hrpc('kernel', 'ping') t.ok(ping.ok) t.is(ping.sessionId, 'sess1') t.is(audit[0].route, 'kernel.ping') await hrpc('kernel', 'capabilities').then((r) => { t.ok(r.ok) t.ok(r.hrpcStockRoutes.includes('kernel.ping')) }) const denied = createStockBareOsHrpcRequest({ env: { BARE_OS_HRPC_ALLOWLIST_JSON: '["kernel.ping"]' }, sessionId: 's', vfs: {}, disk: {}, capabilityWords: {}, protocolVersion: 'x', booterVersion: 'x' }) await t.exception(denied('vfs', 'readText'), /allowlist/) await t.exception(hrpc('nope', 'x'), /unsupported route/) }) test('stock hrpc vfs.readText and echo', async (t) => { const hrpc = createStockBareOsHrpcRequest({ env: {}, sessionId: 's', vfs: { async readFile(p) { t.is(p, '/etc/os-release') return new TextEncoder().encode('ID=bare') } }, disk: {}, capabilityWords: {}, protocolVersion: 'x', booterVersion: 'x' }) const txt = await hrpc('vfs', 'readText', { path: '/etc/os-release' }) t.is(txt.text, 'ID=bare') const echo = await hrpc('bare_os', 'echo', { a: 1 }) t.alike(echo.payload, { a: 1 }) await t.exception(hrpc('vfs', 'readText', { path: 'relative' }), /absolute/) }) test('shell syntax split and function parse', (t) => { const toks = [ { type: 'word', value: 'echo' }, { type: 'word', value: 'a' }, { type: 'op', value: ';' }, { type: 'word', value: 'echo' }, { type: 'word', value: 'b' } ] const parts = splitTopLevelStatements(toks) t.is(parts.length, 2) t.ok(isValidShellIdentifier('foo_1')) t.ok(!isValidShellIdentifier('1foo')) const fn = parseShellFunctionDeclaration([ { type: 'word', value: 'greet' }, { type: 'op', value: '(' }, { type: 'op', value: ')' }, { type: 'op', value: '{' }, { type: 'word', value: 'echo' }, { type: 'word', value: 'hi' }, { type: 'op', value: '}' } ]) t.is(fn.name, 'greet') t.ok(casePatternMatches('abc', 'a*')) t.ok(!casePatternMatches('abc', 'x*')) }) test('vfs personal layout and guest-sensitive paths', (t) => { const guest = createVfsPersonalLayout( { HOME: '/home/guest/', BARE_OS_PERSONAL_ACCT_PREFIX: '1' }, { session: 'guest' } ) t.is(guest.normalizeHome(), '/home/guest') t.is(guest.activeHomeBasename(), 'guest') t.is(guest.personalLayoutRootAbs(), '/.bare-os/acct/_guest') t.is(guest.personalHomeStorageRoot(), '/.bare-os/acct/_guest/home/guest') t.ok(guest.bareOsGuestSensitivePersonalDenied('/.bare/vault/key')) t.ok(!guest.environKeyAllowed('PASSWORD')) t.ok(guest.environKeyAllowed('HOME')) const unlocked = createVfsPersonalLayout( { HOME: '/home/guest' }, { session: 'unlocked' } ) t.ok(!unlocked.bareOsGuestSensitivePersonalDenied('/.bare/vault/key')) t.is(unlocked.personalLayoutRootAbs(), '/.bare-os') }) test('shell builtins and read field split', (t) => { t.ok(isShellBuiltin('cd', {})) t.ok(!isShellBuiltin('read', {})) t.ok(isShellBuiltin('read', { BARE_OS_SHELL_READ_BUILTIN: '1' })) t.ok(listBareOsShellBuiltins({ BARE_OS_SHELL_READ_BUILTIN: '1' }).includes('read')) t.alike(bareOsShellReadSplitFields('a b c', ' ', 2), ['a', 'b c']) }) test('shell runtime pipeline limits and child merge', (t) => { t.is(getBareOsPipelineLimits({}).maxStages, DEFAULT_PIPELINE_MAX_STAGES) t.is(getBareOsPipelineLimits({ BARE_OS_PIPELINE_MAX_STAGES: '4' }).maxStages, 4) const ctx = { exitCode: 0 } mergePipelineChildCtx(ctx, { exitCode: 7, identity: { state: 'unlocked', publicKey: 1, secretKey: 2 } }) t.is(ctx.exitCode, 7) t.is(ctx.identity.state, 'unlocked') }) test('expandWord param and arithmetic', (t) => { t.is(expandWord('x${HOME}y', { HOME: '/h' }), 'x/hy') t.is(expandWord('$(( $a + 1 ))', { a: '2' }), '3') }) test('expandShellWordTokens traces and expands unquoted words', async (t) => { const ctx = {} const env = { HOME: '/h', BARE_OS_SHELL_EXPANSION_TRACE: '1', BARE_OS_SHELL_CMDSUBST: '0' } const out = await expandShellWordTokens( ctx, { type: 'word', value: '$HOME', parts: [{ q: 'u', t: '$HOME' }] }, env, { disablePathnameExpansion: true } ) t.alike(out, ['/h']) t.ok(Array.isArray(ctx.shellExpansionTrace)) t.ok(ctx.shellExpansionTrace.some((r) => r.stage === 'expand-pre')) t.ok(ctx.shellExpansionTrace.some((r) => r.stage === 'split-glob')) }) test('vfs linux-shaped /proc texts', (t) => { const linux = createVfsPseudoLinux({ env: { BARE_OS_CTX_API_VERSION: '1.2.3', BARE_OS_SESSION_ID: 'sess-1', HOME: '/home/guest', PASSWORD: 'nope' }, procSnapshot: { version: '1.2.3', cmdline: 'bare-os' }, bootStartedMs: Date.now() - 2500, environKeyAllowed: (k) => k !== 'PASSWORD', getProcSyntheticLinuxCompat: () => ({ sessionId: 'sess-1', swarmPeerCount: 1, peerIds: ['aabbccdd'] }) }) t.ok(linux.pseudoVersionText().includes('1.2.3')) t.ok(linux.pseudoMeminfoText().includes('MemTotal:')) t.ok(linux.pseudoCpuinfoText().includes('processor')) t.ok(linux.pseudoNetTcpText().includes('ESTABLISHED')) t.ok(linux.pseudoSelfCgroupsText().includes('sess-1')) t.ok(utf8Encode('hi').byteLength >= 2) }) test('virtual signal deliverer ignore and existence probe', (t) => { const state = new Map() const traps = [] const deliver = createBareOsVirtualSignalDeliverer({ dispatchShellTrapSignal: (ctx, sig) => { traps.push(sig) }, virtualSignalState: state }) const ctx = { bareOsLogicalSigaction: { TERM: 'IGNORE' }, requestBooterExit() { t.fail('should not exit when ignored') } } const ignored = deliver(ctx, 3, 'TERM') t.ok(ignored.ignored) t.is(traps.length, 0) const probe = deliver({ requestBooterExit() {} }, 3, '0') t.ok(probe.exists) t.absent(probe.delivered) }) test('loader audit appends when enabled', async (t) => { const files = new Map() const append = createKernelLoaderAuditAppend({ env: { BARE_OS_LOADER_AUDIT: '1' }, sessionId: 's1', vfs: { async readFile(p) { if (!files.has(p)) throw new Error('missing') return files.get(p) }, async writeFile(p, buf) { files.set(p, buf) } } }) await append({ phase: 'vfs' }) const txt = new TextDecoder().decode(files.get('/run/bare-os/loader-audit.ndjson')) t.ok(txt.includes('"type":"loader_audit"')) t.ok(txt.includes('"phase":"vfs"')) }) test('booter boot emitter records phases', (t) => { const bootReadyStateRef = { booterPhases: [], booterStages: [] } const events = [] const { emitBooterBootStep } = createBooterBootEmitter({ bootReadyStateRef, bootStartedMs: Date.now(), bootEventSubs: [(ev) => events.push(ev)], diagnosticsSubs: [], sessionId: 's', lifecycleSchemaVersion: 1 }) emitBooterBootStep('vfs') emitBooterBootStep('vfs') t.alike(bootReadyStateRef.booterPhases, ['vfs']) t.is(events.length, 2) t.is(events[0].phase, 'booter:vfs') }) test('booter proc helpers chat/meshdrop off notes', (t) => { const helpers = createBooterProcHelpers({ interactiveCtxRef: { ctx: { bareOsLogicalFds: { 7: '/tmp/x' } } }, disk: {}, shellEnv: {}, hostEnv: {} }) t.alike(helpers.bareOsCollectLogicalFdRows(), [{ fd: 7, target: '/tmp/x' }]) t.ok(helpers.bareOsChatProcSnapshotRecord().note) t.ok(helpers.bareOsMeshdropProcSnapshotRecord().note) }) test('shell token alias parse', (t) => { const toks = tokenize('echo a; echo b') t.ok(toks.length >= 3) t.alike(expandArgvAliases(['ll'], defaultShellAliases()).slice(0, 1), ['ls']) const ctx = { shellAliases: {} } t.ok(applyAliasDefinition(ctx, "gst='git status'")) t.is(ctx.shellAliases.gst, 'git status') const pipe = parsePipeline(tokenize('echo hi > out')) t.is(planShellRedirections(pipe[0]).stdout, 'truncate') t.is(splitTokensBySemicolon(tokenize('a; b')).length, 2) t.alike(splitTokensByAndOr(tokenize('a && b')).ops, ['&&']) t.is(buildShellExecutionGraph('echo x').schema, 1) }) test('vfs deny asserts and warm cache evict', (t) => { t.exception( () => assertUnionWriteNotDenied('/mnt/x/a', ['/mnt/x'], ['/mnt/x']), /union write denied/ ) assertUnionWriteNotDenied('/home/g/a', ['/mnt/x'], ['/mnt/x']) t.exception( () => assertNotBootPolicyDenyVfs('/etc/shadow', 'read', { BARE_OS_BOOT_POLICY_DENY_VFS: '/etc' }), /boot policy denies/ ) const ref = { current: null } const cache = createVfsWarmReadCache({ env: { BARE_OS_VFS_BIN_CACHE: '1' }, warmReadCacheStatsRef: ref }) t.ok(cache.binReadCache) cache.binReadCache.set('/bin/ls', new Uint8Array([1])) t.is(cache.bareOsEvictWarmReadPrefixes(['/bin/']), 1) t.is(cache.binReadCache.size, 0) }) test('session masked line falls back without TTY', async (t) => { const read = createSessionReadMaskedLine({ stdin: {}, stdout: {}, fallbackReadLine: async (p) => 'fb:' + p }) t.is(await read('pw> '), 'fb:pw> ') }) test('pipeline child ctx chunk and timeout', async (t) => { const child = bareOsPipelineChildCtx({ a: 1 }, { X: '1' }, 'in', true) t.is(child.shellStdin, 'in') t.ok(child.bareOsStdoutCaptured) t.is(child.env.X, '1') t.is(bareOsPipelineRawChunkToText('ab'), 'ab') t.is(bareOsPipelineRawChunkToText(new Uint8Array([65, 66])), 'AB') const v = await runWithShellPipelineStageTimeout({}, async () => 7, 'x') t.is(v, 7) }) test('shell stmt helpers local-style', async (t) => { const env = {} assignShellFunctionPositionalEnv(env, ['fn', 'a', 'b']) t.is(env['0'], 'fn') t.is(env['1'], 'a') t.is(env['2'], 'b') t.is(env['#'], '2') const errs = [] const ctx = { console: { error: (m) => errs.push(m) }, exitCode: 0 } t.ok(tryReportMisplacedReservedStatementStart(ctx, [{ type: 'word', value: 'then' }])) t.is(ctx.exitCode, 2) t.alike(casePatternList([{ type: 'word', value: 'x*' }, { type: 'op', value: '|' }, { type: 'word', value: 'y' }], {}), [ 'x*', 'y' ]) const br = { vfs: { env: { A: 'hi' } }, console: { error: () => {} }, exitCode: 99 } await execDoubleBracketLimited(br, [ { type: 'word', value: '[[' }, { type: 'word', value: '$A' }, { type: 'word', value: '==' }, { type: 'word', value: 'hi' }, { type: 'word', value: ']]' } ]) t.is(br.exitCode, 0) const jobs = { shellBackgroundJobs: { nextId: 9, list: [1] } } bareOsResetShellIdentityState(jobs) t.is(jobs.shellBackgroundJobs.nextId, 1) t.is(jobs.shellBackgroundJobs.list.length, 0) }) test('vfs route tilde mnt and ro alias', (t) => { const helpers = createVfsRouteHelpers({ getCwd: () => '/home/g', normalizeHome: () => '/home/g', mntRef: { getMounts: () => new Map([['data', { drive: { get: () => 1 }, writable: true }]]) }, systemRoAliasNorm: '/sysro', systemDrive: { id: 'sys' } }) t.is(helpers.expandTilde('~/x'), '/home/g/x') t.is(helpers.resolveLogical('y'), '/home/g/y') t.ok(helpers.routeMnt('/mnt').virtualMntRoot) t.is(helpers.routeMnt('/mnt/data/a').path, '/a') t.ok(helpers.routeSystemRoAlias('/sysro/etc').mntReadOnly) t.ok(helpers.pseudoMountsText().includes('/mnt/data')) }) test('vfs route tmp home union snapshots', (t) => { const personal = { id: 'p' } const system = { id: 'sys', checkout: (v) => ({ id: 'chk' + v }) } const helpers = createVfsRouteHelpers({ getCwd: () => '/home/g', normalizeHome: () => '/home/g', systemRoAliasNorm: '', systemDrive: system, personalDrive: personal, env: { BARE_OS_VFS_SNAPSHOTS: '1' }, tmpStorageRoot: () => '/.bare-os/tmp/g', varLogStorageRoot: () => '/.bare-os/var/log/g', activeHomeBasename: () => 'g', personalHomeStorageRoot: () => '/.bare-os/home/g', usePersonalAcctPrefix: () => false }) t.is(helpers.route('/tmp/x').path, '/.bare-os/tmp/g/x') t.ok(helpers.route('/var').virtualVarRoot) t.is(helpers.route('/home/g/a').drive, personal) t.is(helpers.route('/snapshots/system/3/etc').snapshotReadOnly, true) t.is(helpers.route('/.bare/holesail/state.json').path, '/.bare/holesail/state.json') }) test('pathconf and getconf helpers', (t) => { const m = createBareOsPathconfMethods({ env: { BARE_OS_NPROC: '8', BARE_OS_PIPELINE_MAX_STAGES: '12' }, resolveLogical: (p) => p, bootHrtimeNowNs: null }) t.is(m.bareOsPathconf('/etc', 'NAME_MAX'), 255) t.is(m.bareOsPathconf('/mirror/x', '_PC_CHOWN_RESTRICTED'), 0) t.is(m.bareOsPathconf('/dev/shm/a', '_PC_NAME_MAX'), 128) t.is(m.bareOsGetconfSysconf('_SC_NPROCESSORS_ONLN'), '8') t.is(m.bareOsGetconfSysconf('_SC_PAGESIZE'), '4096') t.is(m.bareOsGetconfSysconf('_SC_BARE_OS_PIPELINE_MAX_STAGES'), '12') t.is(m.bareOsGetconfSysconf('_SC_MONOTONIC_CLOCK_RES'), '0') }) test('logical fd register and sigaction', (t) => { const ctx = { bareOsLogicalFds: {}, bareOsLogicalFdFlags: { 9: 1 }, bareOsLogicalSigaction: {}, bareOsSnapshotHandles: [], ...createBareOsLogicalFdMethods() } ctx.bareOsRegisterLogicalFd(9, '/tmp/x') t.is(ctx.bareOsLogicalFds['9'], '/tmp/x') ctx.bareOsUnregisterLogicalFd(9) t.is(ctx.bareOsLogicalFds['9'], undefined) t.is(ctx.bareOsSigaction('INT', 'IGNORE').mode, 'IGNORE') t.is(ctx.bareOsLogicalSigaction.INT, 'IGNORE') ctx.bareOsRegisterSnapshotHandle({ id: 's1' }) t.is(ctx.bareOsSnapshotHandles[0].id, 's1') }) test('consumeInteractiveHeredoc reads until delim', async (t) => { const lines = ['one', 'two', 'END'] const ctx = { readLine: async () => lines.shift(), vfs: { env: {} }, console: { error: () => {} } } const r = await consumeInteractiveHeredoc( ctx, "cat < {} ) t.ok(r.ok) t.is(r.execLine, 'cat') t.is(ctx.shellHeredocOnce, 'one\ntwo\n') }) test('posix fd sim pipe write read', (t) => { const methods = createBareOsPosixFdSimMethods({ env: { BARE_OS_POSIX_FD_SIM: '1' }, bootHrtimeNowNs: null }) const ctx = { bareOsLogicalFds: {}, bareOsLogicalFdFlags: {}, bareOsPosixFdSimState: { nextFd: 10, nextPairId: 1, queues: Object.create(null), byteTotals: Object.create(null) }, bareOsRegisterLogicalFd(fd, target) { this.bareOsLogicalFds[String(fd)] = target }, ...methods } const pipe = ctx.bareOsPosixFdSimPipe() t.ok(pipe) const w = ctx.bareOsPosixFdSimWrite(pipe.writeFd, 'hi') t.ok(w.ok) const r = ctx.bareOsPosixFdSimRead(pipe.readFd) t.ok(r.ok) t.is(r.data, 'hi') const probe = ctx.bareOsPosixPollProbe({ fds: [{ fd: 1, events: 'w' }] }) t.ok(probe.ready.some((x) => x.fd === 1 && x.revents === 'w')) }) test('pseudo file bytes version and shm', (t) => { const shm = new Map() shm.set('q', new Uint8Array([9])) const fn = createVfsPseudoFileBytes({ bareOsDevShm: shm, pseudoVersionText: () => 'Bare OS\n', pseudoUrandomBytes: () => new Uint8Array([1]) }) const ver = fn({ file: 'version', kind: 'proc' }) t.ok(ver.byteLength > 0) const empty = fn({ file: 'null', kind: 'dev' }) t.is(empty.byteLength, 0) const got = fn({ file: 'shm', kind: 'dev', shmName: 'q' }) t.is(got[0], 9) }) test('lifecycle hooks register invoke and reload', async (t) => { const suspendHooks = [] const resumeHooks = [] const m = createBareOsLifecycleHookMethods({ suspendHooks, resumeHooks, env: { BARE_OS_KERNEL_PROFILE_WARM: '1' } }) let n = 0 m.bareOsRegisterSuspendHook(() => { n++ }) await m.bareOsInvokeSuspendHooks() t.is(n, 1) t.exception(() => m.bareOsRequestKernelReload(), /BARE_OS_KERNEL_RELOAD/) t.exception( () => m.bareOsRequestKernelProfileReload(), /BARE_OS_KERNEL_PROFILE_RELOAD/ ) }) test('process control existence probe and renice', (t) => { const delivered = [] const nice = new Map() const ctx = { bareOsLogicalSigaction: {}, ...createBareOsProcessControlMethods({ deliverBareOsVirtualSignalToPid: (_c, pid, sig) => { delivered.push([pid, sig]) return { delivered: true, atMs: 1 } }, getInteractiveCtx: () => ({ shellBackgroundJobs: { list: [] } }), niceByPid: nice }) } const z = ctx.bareOsSendSignal('kernel', '0') t.ok(z.exists) t.is(z.pid, 1) const r = ctx.bareOsRenice(1, 2) t.is(r.nice, 2) t.is(nice.get(1), 2) })