diff --git a/lib/worker.js b/lib/worker.js index c1e5cbaa3..c146a298f 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -1,6 +1,7 @@ 'use strict' const { isElectronRenderer, isWindows, isBare } = require('which-runtime') const fs = isBare ? require('bare-fs') : require('fs') +const teardown = isBare ? require('./teardown') : (fn) => fn() const { spawn } = isBare ? require('bare-subprocess') : require('child_process') const { command } = require('paparam') const Pipe = isBare @@ -16,6 +17,7 @@ class Worker { #ref = null #unref = null #ipc = null + static RUNTIME = constants.RUNTIME constructor ({ ref = noop, unref = noop, ipc = null } = {}) { this.#ref = ref this.#unref = unref @@ -41,7 +43,7 @@ class Worker { run (link, args = []) { if (isElectronRenderer) return this.#ipc.workerRun(link, args) args = [...this.#args(link), ...args] - const sp = spawn(constants.RUNTIME, args, { + const sp = spawn(this.constructor.RUNTIME, args, { stdio: ['inherit', 'inherit', 'inherit', 'overlapped'], windowsHide: true }) @@ -67,11 +69,12 @@ class Worker { return null } const pipe = new Pipe(fd) - pipe.on('end', () => pipe.end()) + pipe.on('end', () => { + teardown(() => pipe.end(), Number.MAX_SAFE_INTEGER) + }) this.#pipe = pipe pipe.once('close', () => { - // allow close event to propagate between processes before exiting: - setImmediate(() => program.exit()) + teardown(() => program.exit(), Number.MAX_SAFE_INTEGER) }) return pipe } diff --git a/test/.gitignore b/test/.gitignore index b334f9221..35975d3c1 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -1,3 +1,4 @@ corestores node_modules -.DS_Store \ No newline at end of file +.DS_Store +package-lock.json diff --git a/test/01-smoke.test.js b/test/01-smoke.test.js index b7e01f0b1..a5f1d17e7 100644 --- a/test/01-smoke.test.js +++ b/test/01-smoke.test.js @@ -3,92 +3,162 @@ const test = require('brittle') const path = require('bare-path') const hypercoreid = require('hypercore-id-encoding') const Helper = require('./helper') -const harness = path.join(Helper.localDir, 'test', 'fixtures', 'harness') -const assets = path.join(Helper.localDir, 'test', 'fixtures', 'app-with-assets') +const versionsDir = path.join(Helper.localDir, 'test', 'fixtures', 'versions') +const dhtBootstrapDir = path.join(Helper.localDir, 'test', 'fixtures', 'dht-bootstrap') +const requireAssets = path.join(Helper.localDir, 'test', 'fixtures', 'require-assets') +const subDepRequireAssets = path.join(Helper.localDir, 'test', 'fixtures', 'sub-dep-require-assets') -test('smoke', async function ({ ok, is, plan, comment, teardown, timeout, end }) { +test('smoke', async function ({ ok, is, alike, plan, comment, teardown, timeout }) { timeout(180000) - plan(6) - const stager = new Helper() - teardown(() => stager.close(), { order: Infinity }) - await stager.ready() - const dir = harness + plan(10) + + const testVersions = async () => { + const dir = versionsDir + + const helper = new Helper() + teardown(() => helper.close(), { order: Infinity }) + await helper.ready() + + const id = Math.floor(Math.random() * 10000) + + comment('staging') + const staging = helper.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) + teardown(() => Helper.teardownStream(staging)) + const staged = await Helper.pick(staging, { tag: 'final' }) + ok(staged.success, 'stage succeeded') + + comment('seeding') + const seeding = helper.seed({ channel: `test-${id}`, name: `test-${id}`, dir, key: null, cmdArgs: [] }) + teardown(() => Helper.teardownStream(seeding)) + const until = await Helper.pick(seeding, [{ tag: 'key' }, { tag: 'announced' }]) + const announced = await until.announced + ok(announced, 'seeding is announced') + + const key = await until.key + ok(hypercoreid.isValid(key), 'app key is valid') + + const link = `pear://${key}` + const run = await Helper.run({ link }) + + const result = await Helper.untilResult(run.pipe) + const versions = JSON.parse(result) + is(versions.app.key, key, 'app version matches staged key') + + await Helper.untilClose(run.pipe) + ok(true, 'ended') + } + + const testDhtBootstrap = async () => { + const dir = dhtBootstrapDir + + const helper = new Helper() + teardown(() => helper.close(), { order: Infinity }) + await helper.ready() + + const id = Math.floor(Math.random() * 10000) + + comment('staging') + const staging = helper.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) + teardown(() => Helper.teardownStream(staging)) + const staged = await Helper.pick(staging, { tag: 'final' }) + ok(staged.success, 'stage succeeded') + + comment('seeding') + const seeding = helper.seed({ channel: `test-${id}`, name: `test-${id}`, dir, key: null, cmdArgs: [] }) + teardown(() => Helper.teardownStream(seeding)) + const until = await Helper.pick(seeding, [{ tag: 'key' }, { tag: 'announced' }]) + const announced = await until.announced + ok(announced, 'seeding is announced') + + const key = await until.key + ok(hypercoreid.isValid(key), 'app key is valid') + + const link = `pear://${key}` + const run = await Helper.run({ link }) + + const result = await Helper.untilResult(run.pipe) + const dhtBootstrap = JSON.parse(result) + alike(dhtBootstrap, Pear.config.dht.bootstrap, 'dht bootstrap matches Pear.config.dht.bootstrap') + + await Helper.untilClose(run.pipe) + ok(true, 'ended') + } + + await Promise.all([testVersions(), testDhtBootstrap()]) +}) + +test('app with assets', async function ({ ok, is, plan, comment, teardown, timeout }) { + timeout(180000) + plan(5) + + const dir = requireAssets + + const helper = new Helper() + teardown(() => helper.close(), { order: Infinity }) + await helper.ready() const id = Math.floor(Math.random() * 10000) comment('staging') - const staging = stager.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) + const staging = helper.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) teardown(() => Helper.teardownStream(staging)) - const final = await Helper.pick(staging, { tag: 'final' }) - ok(final.success, 'stage succeeded') + const staged = await Helper.pick(staging, { tag: 'final' }) + ok(staged.success, 'stage succeeded') comment('seeding') - const seeder = new Helper() - teardown(() => seeder.close(), { order: Infinity }) - await seeder.ready() - const seeding = seeder.seed({ channel: `test-${id}`, name: `test-${id}`, dir, key: null, cmdArgs: [] }) + const seeding = helper.seed({ channel: `test-${id}`, name: `test-${id}`, dir, key: null, cmdArgs: [] }) teardown(() => Helper.teardownStream(seeding)) const until = await Helper.pick(seeding, [{ tag: 'key' }, { tag: 'announced' }]) - - const key = await until.key const announced = await until.announced - - ok(hypercoreid.isValid(key), 'app key is valid') ok(announced, 'seeding is announced') - comment('running') - const link = 'pear://' + key - const running = await Helper.open(link, { tags: ['exit'] }) + const key = await until.key + ok(hypercoreid.isValid(key), 'app key is valid') - const { value } = await running.inspector.evaluate('Pear.versions()', { awaitPromise: true }) - is(value?.app?.key, key, 'app version matches staged key') + const link = `pear://${key}` + const run = await Helper.run({ link }) - const dhtBootstrap = await running.inspector.evaluate('Pear.config.dht.bootstrap') - is(JSON.stringify(dhtBootstrap.value), JSON.stringify(Pear.config.dht.bootstrap), 'dht bootstrap matches Pear.config.dth.bootstrap') + const result = await Helper.untilResult(run.pipe) + is(result.trim(), 'This is the content of the asset', 'Read asset from entrypoint') - await running.inspector.close() - const { code } = await running.until.exit - is(code, 0, 'exit code is 0') + await Helper.untilClose(run.pipe) + ok(true, 'ended') }) -test('app with assets', async function ({ is, plan, comment, teardown, timeout }) { +test('app with assets in sub dep', async function ({ ok, is, plan, comment, teardown, timeout }) { timeout(180000) - plan(3) - const stager = new Helper() - teardown(() => stager.close(), { order: Infinity }) - await stager.ready() - const dir = assets + plan(5) + + const dir = subDepRequireAssets + + const helper = new Helper() + teardown(() => helper.close(), { order: Infinity }) + await helper.ready() const id = Math.floor(Math.random() * 10000) comment('staging') - const staging = stager.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) + const staging = helper.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) teardown(() => Helper.teardownStream(staging)) - await Helper.pick(staging, { tag: 'final' }) + const staged = await Helper.pick(staging, { tag: 'final' }) + ok(staged.success, 'stage succeeded') comment('seeding') - const seeder = new Helper() - teardown(() => seeder.close(), { order: Infinity }) - await seeder.ready() - const seeding = seeder.seed({ channel: `test-${id}`, name: `test-${id}`, dir, key: null, cmdArgs: [] }) + const seeding = helper.seed({ channel: `test-${id}`, name: `test-${id}`, dir, key: null, cmdArgs: [] }) teardown(() => Helper.teardownStream(seeding)) const until = await Helper.pick(seeding, [{ tag: 'key' }, { tag: 'announced' }]) + const announced = await until.announced + ok(announced, 'seeding is announced') const key = await until.key - await until.announced - - comment('running') - const link = 'pear://' + key - const running = await Helper.open(link, { tags: ['exit'] }) + ok(hypercoreid.isValid(key), 'app key is valid') - const { value: fromIndex } = await running.inspector.evaluate('global.readAsset()', { awaitPromise: true }) - is(fromIndex.trim(), 'This is the content of the asset', 'Read asset from entrypoint') + const link = `pear://${key}` + const run = await Helper.run({ link }) - const { value: fromUtils } = await running.inspector.evaluate('global.readAssetFromUtils()', { awaitPromise: true }) - is(fromUtils.trim(), 'This is the content of the asset', 'Read asset from lib') + const result = await Helper.untilResult(run.pipe) + is(result.trim(), 'This is the content of the asset', 'Read asset from entrypoint') - await running.inspector.evaluate('disableInspector()') - await running.inspector.close() - const { code } = await running.until.exit - is(code, 0, 'exit code is 0') + await Helper.untilClose(run.pipe) + ok(true, 'ended') }) diff --git a/test/02-teardown.test.js b/test/02-teardown.test.js index 13213d04f..a710d4658 100644 --- a/test/02-teardown.test.js +++ b/test/02-teardown.test.js @@ -1,149 +1,157 @@ 'use strict' +const { isWindows } = require('which-runtime') const test = require('brittle') const path = require('bare-path') +const os = require('bare-os') const hypercoreid = require('hypercore-id-encoding') const Helper = require('./helper') -const harness = path.join(Helper.localDir, 'test', 'fixtures', 'harness') +const teardownDir = path.join(Helper.localDir, 'test', 'fixtures', 'teardown') +const teardownOsKillDir = path.join(Helper.localDir, 'test', 'fixtures', 'teardown-os-kill') +const teardownExitCodeDir = path.join(Helper.localDir, 'test', 'fixtures', 'teardown-exit-code') -test('teardown', async function ({ is, ok, plan, comment, teardown, timeout }) { +test('teardown on pipe end', async function ({ ok, is, plan, comment, teardown, timeout }) { + if (isWindows) return + timeout(180000) + plan(4) - plan(5) - - const stager = new Helper() - teardown(() => stager.close(), { order: Infinity }) - await stager.ready() + const dir = teardownDir - const dir = harness + const helper = new Helper() + teardown(() => helper.close(), { order: Infinity }) + await helper.ready() const id = Math.floor(Math.random() * 10000) comment('staging') - const stage = stager.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) - const final = await Helper.pick(stage, { tag: 'final' }) - ok(final.success, 'stage succeeded') + const staging = helper.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) + teardown(() => Helper.teardownStream(staging)) + const staged = await Helper.pick(staging, { tag: 'final' }) + ok(staged.success, 'stage succeeded') comment('seeding') - const seeder = new Helper() - teardown(() => seeder.close(), { order: Infinity }) - await seeder.ready() - const seed = seeder.seed({ channel: `test-${id}`, name: `test-${id}`, dir }) - teardown(() => Helper.teardownStream(seed)) - const until = await Helper.pick(seed, [{ tag: 'key' }, { tag: 'announced' }]) - const key = await until.key + const seeding = helper.seed({ channel: `test-${id}`, name: `test-${id}`, dir, key: null, cmdArgs: [] }) + teardown(() => Helper.teardownStream(seeding)) + const until = await Helper.pick(seeding, [{ tag: 'key' }, { tag: 'announced' }]) const announced = await until.announced + ok(announced, 'seeding is announced') + const key = await until.key ok(hypercoreid.isValid(key), 'app key is valid') - ok(announced, 'seeding is announced') - comment('running') - const link = 'pear://' + key - const running = await Helper.open(link, { tags: ['teardown', 'exit'] }) + const link = `pear://${key}` + const run = await Helper.run({ link }) + const { pipe } = run - await running.inspector.evaluate('Pear.teardown(() => console.log(\'teardown\'))') - await running.inspector.evaluate('Pear.shutdown()') - await running.inspector.close() + const teardownPromise = Helper.untilResult(run.pipe) - const td = await running.until.teardown - is(td, 'teardown', 'teardown has been triggered') + pipe.end() - const { code } = await running.until.exit - is(code, 0, 'exit code is 0') + const td = await teardownPromise + is(td, 'teardown', 'teardown executed') }) -test('teardown during teardown', async function ({ is, ok, plan, comment, teardown, timeout }) { +test('teardown on os kill', async function ({ ok, is, plan, comment, teardown, timeout }) { + if (isWindows) return + timeout(180000) plan(5) - const stager = new Helper() - teardown(() => stager.close(), { order: Infinity }) - await stager.ready() + const dir = teardownOsKillDir - const dir = harness + const helper = new Helper() + teardown(() => helper.close(), { order: Infinity }) + await helper.ready() const id = Math.floor(Math.random() * 10000) comment('staging') - const stage = stager.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) - const final = await Helper.pick(stage, { tag: 'final' }) - ok(final.success, 'stage succeeded') + const staging = helper.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) + teardown(() => Helper.teardownStream(staging)) + const staged = await Helper.pick(staging, { tag: 'final' }) + ok(staged.success, 'stage succeeded') comment('seeding') - const seeder = new Helper() - teardown(() => seeder.close(), { order: Infinity }) - await seeder.ready() - const seed = seeder.seed({ channel: `test-${id}`, name: `test-${id}`, dir }) - teardown(() => Helper.teardownStream(seed)) - const until = await Helper.pick(seed, [{ tag: 'key' }, { tag: 'announced' }]) - const key = await until.key + const seeding = helper.seed({ channel: `test-${id}`, name: `test-${id}`, dir, key: null, cmdArgs: [] }) + teardown(() => Helper.teardownStream(seeding)) + const until = await Helper.pick(seeding, [{ tag: 'key' }, { tag: 'announced' }]) const announced = await until.announced + ok(announced, 'seeding is announced') + const key = await until.key ok(hypercoreid.isValid(key), 'app key is valid') - ok(announced, 'seeding is announced') - comment('running') - const link = 'pear://' + key - const running = await Helper.open(link, { tags: ['teardown', 'exit'] }) + const link = `pear://${key}` + const run = await Helper.run({ link }) + const { pipe } = run - await running.inspector.evaluate( - `(() => { - const { teardown } = Pear - const a = () => { b() } - const b = () => { teardown(() => console.log('teardown from b')) } - teardown( () => a() ) - })()`) + const pidPromise = Helper.untilResult(pipe) - await running.inspector.evaluate('Pear.shutdown()') - await running.inspector.close() + pipe.write('start') - const td = await running.until.teardown - is(td, 'teardown from b', 'teardown from b has been triggered') + const pid = +(await pidPromise) + ok(pid > 0, 'worker pid is valid') - const { code } = await running.until.exit - is(code, 0, 'exit code is 0') + const teardownPromise = Helper.untilResult(pipe, 5000, () => os.kill(pid)) + + const td = await teardownPromise + ok(td, 'teardown executed') }) -// TODO: fixme -test.skip('exit with non-zero code in teardown', async function ({ is, ok, plan, comment, teardown }) { - plan(4) +test('teardown on os kill with exit code', async function ({ ok, is, plan, comment, teardown, timeout }) { + if (isWindows) return + + timeout(180000) + plan(6) - const stager = new Helper() - teardown(() => stager.close(), { order: Infinity }) - await stager.ready() + const dir = teardownExitCodeDir - const dir = harness + const helper = new Helper() + teardown(() => helper.close(), { order: Infinity }) + await helper.ready() const id = Math.floor(Math.random() * 10000) comment('staging') - const stage = stager.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) - const final = await Helper.pick(stage, { tag: 'final' }) - ok(final.success, 'stage succeeded') + const staging = helper.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) + teardown(() => Helper.teardownStream(staging)) + const staged = await Helper.pick(staging, { tag: 'final' }) + ok(staged.success, 'stage succeeded') comment('seeding') - const seeder = new Helper() - teardown(() => seeder.close()) - teardown(() => seeder.close(), { order: Infinity }) - await seeder.ready() - const seed = seeder.seed({ channel: `test-${id}`, name: `test-${id}`, dir }) - teardown(() => Helper.teardownStream(seed)) - const until = await Helper.pick(seed, [{ tag: 'key' }, { tag: 'announced' }]) - const key = await until.key + const seeding = helper.seed({ channel: `test-${id}`, name: `test-${id}`, dir, key: null, cmdArgs: [] }) + teardown(() => Helper.teardownStream(seeding)) + const until = await Helper.pick(seeding, [{ tag: 'key' }, { tag: 'announced' }]) const announced = await until.announced + ok(announced, 'seeding is announced') + const key = await until.key ok(hypercoreid.isValid(key), 'app key is valid') - ok(announced, 'seeding is announced') - comment('running') - const link = 'pear://' + key - const running = await Helper.open(link, { tags: ['teardown', 'exit'] }) + const link = `pear://${key}` + const run = await Helper.run({ link }) + const { pipe } = run + + const pidPromise = Helper.untilResult(pipe) + + pipe.write('start') + + const pid = +(await pidPromise) + ok(pid > 0, 'worker pid is valid') - await running.inspector.evaluate('Pear.teardown(() => Pear.exit(124))') + const exitCodePromise = new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => reject(new Error('timed out')), 5000) + pipe.on('crash', (data) => { + clearTimeout(timeoutId) + resolve(data.exitCode) + }) + }) + + const teardownPromise = Helper.untilResult(pipe, 5000, () => os.kill(pid)) - await running.inspector.evaluate('__PEAR_TEST__.close()') - await running.inspector.close() - // running.subprocess.kill('SIGINT') <-- this was forcing the exit code, which false-positives the test + const td = await teardownPromise + ok(td, 'teardown executed') - const { code } = await running.until.exit - is(code, 124, 'exit code is 124') + const exitCode = await exitCodePromise + is(exitCode, 124, 'exit code is 124') }) diff --git a/test/04-encrypted.test.js b/test/04-encrypted.test.js index 38739408f..310d1b47c 100644 --- a/test/04-encrypted.test.js +++ b/test/04-encrypted.test.js @@ -12,12 +12,13 @@ test.hook('encrypted setup', rig.setup) test('stage, seed and run encrypted app', async function ({ ok, is, plan, comment, timeout, teardown }) { timeout(180000) - plan(7) + plan(8) + + const dir = encrypted const helper = new Helper(rig) teardown(() => helper.close(), { order: Infinity }) await helper.ready() - const dir = encrypted const id = Math.floor(Math.random() * 10000) @@ -31,11 +32,13 @@ test('stage, seed and run encrypted app', async function ({ ok, is, plan, commen comment('staging throws without encryption key') const stagingA = helper.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) + teardown(() => Helper.teardownStream(stagingA)) const error = await Helper.pick(stagingA, { tag: 'error' }) is(error.code, 'ERR_PERMISSION_REQUIRED') comment('staging with encryption key') const stagingB = helper.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, encryptionKey: name, bare: true }) + teardown(() => Helper.teardownStream(stagingB)) const final = await Helper.pick(stagingB, { tag: 'final' }) ok(final.success, 'stage succeeded') @@ -43,27 +46,27 @@ test('stage, seed and run encrypted app', async function ({ ok, is, plan, commen const seeding = helper.seed({ channel: `test-${id}`, name: `test-${id}`, dir, key: null, encryptionKey: name, cmdArgs: [] }) teardown(() => Helper.teardownStream(seeding)) const until = await Helper.pick(seeding, [{ tag: 'key' }, { tag: 'announced' }]) - const appKey = await until.key const announced = await until.announced ok(announced, 'seeding is announced') - comment('run encrypted pear application') - const link = 'pear://' + appKey - const running = await Helper.open(link, { tags: ['exit'] }, { platformDir: rig.platformDir, encryptionKey: name }) - const { value } = await running.inspector.evaluate('Pear.versions()', { awaitPromise: true }) + const key = await until.key + ok(hypercoreid.isValid(key), 'app key is valid') - is(value?.app?.key, appKey, 'app version matches staged key') + const link = `pear://${key}` + const { pipe } = await Helper.run({ link, encryptionKey: name, platformDir: rig.platformDir }) - await running.inspector.evaluate('disableInspector()') - await running.inspector.close() - const { code } = await running.until.exit - is(code, 0, 'exit code is 0') + const result = await Helper.untilResult(pipe) + const versions = JSON.parse(result) + is(versions.app.key, key, 'app version matches staged key') comment('pear info encrypted app') const infoCmd = helper.info({ link, encryptionKey: name, cmdArgs: [] }) const untilInfo = await Helper.pick(infoCmd, [{ tag: 'info' }]) const info = await untilInfo.info ok(info, 'retrieves info from encrypted app') + + await Helper.untilClose(pipe) + ok(true, 'ended') }) test.hook('encrypted cleanup', rig.cleanup) diff --git a/test/fixtures/app-with-assets/index.js b/test/fixtures/app-with-assets/index.js deleted file mode 100644 index c36a32984..000000000 --- a/test/fixtures/app-with-assets/index.js +++ /dev/null @@ -1,28 +0,0 @@ -const bareInspector = require('bare-inspector') -const { Inspector } = require('pear-inspect') -const fsp = require('bare-fs/promises') -const readAssetFromUtils = require('./lib/utils.js') - -const inspector = new Inspector({ inspector: bareInspector }) - -async function readAsset () { - const text = await fsp.readFile(require.asset('./text-file.txt')) - return text.toString() -} - -async function run () { - const key = await inspector.enable() - const inspectorKey = key.toString('hex') - console.log(`{ "tag": "inspector", "data": { "key": "${inspectorKey}" }}`) -} - -run() - - -function disableInspector () { - inspector.disable() -} - -global.disableInspector = disableInspector -global.readAsset = readAsset -global.readAssetFromUtils = readAssetFromUtils diff --git a/test/fixtures/app-with-assets/lib/utils.js b/test/fixtures/app-with-assets/lib/utils.js deleted file mode 100644 index 5e27af61b..000000000 --- a/test/fixtures/app-with-assets/lib/utils.js +++ /dev/null @@ -1,6 +0,0 @@ -const fsp = require('bare-fs/promises') - -module.exports = async () => { - const text = await fsp.readFile(require.asset('../text-file.txt')) - return text.toString() -} diff --git a/test/fixtures/app-with-assets/package.json b/test/fixtures/app-with-assets/package.json deleted file mode 100644 index 606cc7d4b..000000000 --- a/test/fixtures/app-with-assets/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "app-with-assets", - "main": "index.js", - "pear": { - "name": "app-with-assets", - "type": "terminal" - }, - "devDependencies": { - "bare-inspector": "^3.0.1", - "pear-inspect": "^1.2.2" - }, - "dependencies": { - "bare-fs": "^3.1.1" - } -} diff --git a/test/fixtures/dht-bootstrap/index.js b/test/fixtures/dht-bootstrap/index.js new file mode 100644 index 000000000..9a9477366 --- /dev/null +++ b/test/fixtures/dht-bootstrap/index.js @@ -0,0 +1,9 @@ +const pipe = Pear.worker.pipe() +pipe.on('data', () => { + try { + pipe.write(JSON.stringify(Pear.config.dht.bootstrap)) + } catch (err) { + console.error(err) + Pear.exit() + } +}) diff --git a/test/fixtures/dht-bootstrap/package.json b/test/fixtures/dht-bootstrap/package.json new file mode 100644 index 000000000..a45982743 --- /dev/null +++ b/test/fixtures/dht-bootstrap/package.json @@ -0,0 +1,8 @@ +{ + "name": "dht-bootstrap", + "main": "index.js", + "pear": { + "name": "dht-bootstrap", + "type": "terminal" + } +} diff --git a/test/fixtures/encrypted/.npmrc b/test/fixtures/encrypted/.npmrc deleted file mode 100644 index b15cbc2c0..000000000 --- a/test/fixtures/encrypted/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -package-lock=false - diff --git a/test/fixtures/encrypted/index.js b/test/fixtures/encrypted/index.js index 6ef4509c5..904c6250d 100644 --- a/test/fixtures/encrypted/index.js +++ b/test/fixtures/encrypted/index.js @@ -1,13 +1,8 @@ -import bareInspector from 'bare-inspector' -import { Inspector } from 'pear-inspect' - -const inspector = new Inspector({ inspector: bareInspector }) -const key = await inspector.enable() -const inspectorKey = key.toString('hex') -console.log(`{ "tag": "inspector", "data": { "key": "${inspectorKey}" }}`) - -function disableInspector () { - inspector.disable() -} - -global.disableInspector = disableInspector +const pipe = Pear.worker.pipe() +pipe.on('data', () => { + Pear.versions().then((versions) => { + pipe.write(JSON.stringify(versions)) + }).catch((err) => { + pipe.write(`${err}`) + }) +}) diff --git a/test/fixtures/encrypted/package.json b/test/fixtures/encrypted/package.json index dc30072de..cf1b91ce5 100644 --- a/test/fixtures/encrypted/package.json +++ b/test/fixtures/encrypted/package.json @@ -1,18 +1,9 @@ { "name": "encrypted", - "version": "1.0.0", - "description": "", "main": "index.js", - "type": "module", "pear": { "name": "encrypted", "type": "terminal", "encrypted": true - }, - "keywords": [], - "author": "Holepunch", - "dependencies": { - "bare-inspector": "^3.0.1", - "pear-inspect": "^1.2.2" } } diff --git a/test/fixtures/require-assets/index.js b/test/fixtures/require-assets/index.js new file mode 100644 index 000000000..9699ee397 --- /dev/null +++ b/test/fixtures/require-assets/index.js @@ -0,0 +1,11 @@ +const fs = require('bare-fs') + +const pipe = Pear.worker.pipe() +pipe.on('data', () => { + try { + pipe.write(fs.readFileSync(require.asset('./text-file.txt'))) + } catch (err) { + console.error(err) + Pear.exit() + } +}) diff --git a/test/fixtures/require-assets/package.json b/test/fixtures/require-assets/package.json new file mode 100644 index 000000000..c74ebe47d --- /dev/null +++ b/test/fixtures/require-assets/package.json @@ -0,0 +1,11 @@ +{ + "name": "require-assets", + "main": "index.js", + "pear": { + "name": "require-assets", + "type": "terminal" + }, + "dependencies": { + "bare-fs": "^3.1.1" + } +} diff --git a/test/fixtures/app-with-assets/text-file.txt b/test/fixtures/require-assets/text-file.txt similarity index 100% rename from test/fixtures/app-with-assets/text-file.txt rename to test/fixtures/require-assets/text-file.txt diff --git a/test/fixtures/sub-dep-require-assets/index.js b/test/fixtures/sub-dep-require-assets/index.js new file mode 100644 index 000000000..10cec31e2 --- /dev/null +++ b/test/fixtures/sub-dep-require-assets/index.js @@ -0,0 +1,11 @@ +const readAsset = require('./lib/utils.js') + +const pipe = Pear.worker.pipe() +pipe.on('data', () => { + try { + pipe.write(readAsset()) + } catch (err) { + console.error(err) + Pear.exit() + } +}) diff --git a/test/fixtures/sub-dep-require-assets/lib/utils.js b/test/fixtures/sub-dep-require-assets/lib/utils.js new file mode 100644 index 000000000..e204d2fea --- /dev/null +++ b/test/fixtures/sub-dep-require-assets/lib/utils.js @@ -0,0 +1,3 @@ +const fs = require('bare-fs') + +module.exports = () => fs.readFileSync(require.asset('../text-file.txt')) diff --git a/test/fixtures/sub-dep-require-assets/package.json b/test/fixtures/sub-dep-require-assets/package.json new file mode 100644 index 000000000..6b371a5f9 --- /dev/null +++ b/test/fixtures/sub-dep-require-assets/package.json @@ -0,0 +1,11 @@ +{ + "name": "sub-dep-require-assets", + "main": "index.js", + "pear": { + "name": "sub-dep-require-assets", + "type": "terminal" + }, + "dependencies": { + "bare-fs": "^3.1.1" + } +} diff --git a/test/fixtures/sub-dep-require-assets/text-file.txt b/test/fixtures/sub-dep-require-assets/text-file.txt new file mode 100644 index 000000000..d0912597b --- /dev/null +++ b/test/fixtures/sub-dep-require-assets/text-file.txt @@ -0,0 +1 @@ +This is the content of the asset diff --git a/test/fixtures/teardown-exit-code/index.js b/test/fixtures/teardown-exit-code/index.js new file mode 100644 index 000000000..b55b32d6c --- /dev/null +++ b/test/fixtures/teardown-exit-code/index.js @@ -0,0 +1,11 @@ +const program = global.Bare || global.process + +const pipe = Pear.worker.pipe() +pipe.on('data', () => pipe.write(`${program.pid}`)) + +Pear.teardown(async () => { + await new Promise((resolve) => { + pipe.write('teardown', resolve) + }) + Pear.exit(124) +}) diff --git a/test/fixtures/teardown-exit-code/package.json b/test/fixtures/teardown-exit-code/package.json new file mode 100644 index 000000000..f18581e31 --- /dev/null +++ b/test/fixtures/teardown-exit-code/package.json @@ -0,0 +1,8 @@ +{ + "name": "teardown-exit-code", + "main": "index.js", + "pear": { + "name": "teardown-exit-code", + "type": "terminal" + } +} diff --git a/test/fixtures/teardown-os-kill/index.js b/test/fixtures/teardown-os-kill/index.js new file mode 100644 index 000000000..9002591b0 --- /dev/null +++ b/test/fixtures/teardown-os-kill/index.js @@ -0,0 +1,10 @@ +const program = global.Bare || global.process + +const pipe = Pear.worker.pipe() +pipe.on('data', () => pipe.write(`${program.pid}`)) + +Pear.teardown(async () => { + await new Promise((resolve) => { + pipe.write('teardown', resolve) + }) +}) diff --git a/test/fixtures/teardown-os-kill/package.json b/test/fixtures/teardown-os-kill/package.json new file mode 100644 index 000000000..a4c47ab80 --- /dev/null +++ b/test/fixtures/teardown-os-kill/package.json @@ -0,0 +1,8 @@ +{ + "name": "teardown-os-kill", + "main": "index.js", + "pear": { + "name": "teardown-os-kill", + "type": "terminal" + } +} diff --git a/test/fixtures/teardown/index.js b/test/fixtures/teardown/index.js new file mode 100644 index 000000000..026f6cf8c --- /dev/null +++ b/test/fixtures/teardown/index.js @@ -0,0 +1,9 @@ +const program = global.Bare || global.process + +const pipe = Pear.worker.pipe() + +Pear.teardown(async () => { + await new Promise((resolve) => { + pipe.write('teardown', resolve) + }) +}) diff --git a/test/fixtures/teardown/package.json b/test/fixtures/teardown/package.json new file mode 100644 index 000000000..679785645 --- /dev/null +++ b/test/fixtures/teardown/package.json @@ -0,0 +1,8 @@ +{ + "name": "teardown", + "main": "index.js", + "pear": { + "name": "teardown", + "type": "terminal" + } +} diff --git a/test/fixtures/versions/index.js b/test/fixtures/versions/index.js new file mode 100644 index 000000000..69ebc295e --- /dev/null +++ b/test/fixtures/versions/index.js @@ -0,0 +1,9 @@ +const pipe = Pear.worker.pipe() +pipe.on('data', () => { + Pear.versions().then((versions) => { + pipe.write(JSON.stringify(versions)) + }).catch((err) => { + console.error(err) + Pear.exit() + }) +}) diff --git a/test/fixtures/versions/package.json b/test/fixtures/versions/package.json new file mode 100644 index 000000000..238ff9dce --- /dev/null +++ b/test/fixtures/versions/package.json @@ -0,0 +1,8 @@ +{ + "name": "versions", + "main": "index.js", + "pear": { + "name": "versions", + "type": "terminal" + } +} diff --git a/test/helper.js b/test/helper.js index 0e7f64cb9..8414f8886 100644 --- a/test/helper.js +++ b/test/helper.js @@ -16,12 +16,14 @@ const updaterBootstrap = require('pear-updater-bootstrap') const b4a = require('b4a') const HOST = platform + '-' + arch const BY_ARCH = path.join('by-arch', HOST, 'bin', `pear-runtime${isWindows ? '.exe' : ''}`) -const { PLATFORM_DIR } = require('../constants') +const constants = require('../constants') +const { PLATFORM_DIR, RUNTIME } = constants const { pathname } = new URL(global.Pear.config.applink) const NO_GC = global.Pear.config.args.includes('--no-tmp-gc') const MAX_OP_STEP_WAIT = env.CI ? 360000 : 120000 const tmp = fs.realpathSync(os.tmpdir()) Error.stackTraceLimit = Infinity +const program = global.Bare || global.process const rigPear = path.join(tmp, 'rig-pear') @@ -143,6 +145,61 @@ class Helper extends IPC { // ONLY ADD STATICS, NEVER ADD PUBLIC METHODS OR PROPERTIES (see pear-ipc) static localDir = isWindows ? path.normalize(pathname.slice(1)) : pathname + static async run ({ link, encryptionKey, platformDir }) { + if (encryptionKey) program.argv.splice(2, 0, '--encryption-key', encryptionKey) + if (platformDir) Pear.worker.constructor.RUNTIME = path.join(platformDir, 'current', BY_ARCH) + + const pipe = Pear.worker.run(link) + + if (platformDir) Pear.worker.constructor.RUNTIME = RUNTIME + if (encryptionKey) program.argv.splice(2, 2) + + return { pipe } + } + + static async untilResult (pipe, timeout = 5000, runFn) { + const res = new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => reject(new Error('timed out')), timeout) + pipe.on('data', (data) => { + clearTimeout(timeoutId) + resolve(data.toString()) + }) + pipe.on('close', () => { + clearTimeout(timeoutId) + reject(new Error('unexpected closed')) + }) + pipe.on('end', () => { + clearTimeout(timeoutId) + reject(new Error('unexpected ended')) + }) + }) + if (runFn) { + await runFn() + } else { + pipe.write('start') + } + return res + } + + static async untilClose (pipe, timeout = 5000) { + // TODO: fix the "Error: RPC destroyed" when calling pipe.end() too fast, then remove this hack delay + await new Promise((resolve) => setTimeout(resolve, 1000)) + + const res = new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => reject(new Error('timed out')), timeout) + pipe.on('close', () => { + clearTimeout(timeoutId) + resolve('closed') + }) + pipe.on('end', () => { + clearTimeout(timeoutId) + resolve('ended') + }) + }) + pipe.end() + return res + } + static async open (link, { tags = [] } = {}, opts = {}) { if (!link) throw new Error('Key is missing')