electron/spec/node-spec.js

406 lines
12 KiB
JavaScript
Raw Normal View History

2016-03-25 20:03:49 +00:00
const assert = require('assert')
2016-06-29 16:37:10 +00:00
const ChildProcess = require('child_process')
2016-03-25 20:03:49 +00:00
const fs = require('fs')
const path = require('path')
const os = require('os')
const {ipcRenderer, remote} = require('electron')
2016-03-25 20:03:49 +00:00
2016-06-20 02:16:17 +00:00
const isCI = remote.getGlobal('isCi')
describe('node feature', () => {
const fixtures = path.join(__dirname, 'fixtures')
describe('child_process', () => {
describe('child_process.fork', () => {
it('works in current process', (done) => {
const child = ChildProcess.fork(path.join(fixtures, 'module', 'ping.js'))
child.on('message', (msg) => {
2016-03-25 20:03:49 +00:00
assert.equal(msg, 'message')
done()
})
child.send('message')
})
it('preserves args', (done) => {
const args = ['--expose_gc', '-test', '1']
const child = ChildProcess.fork(path.join(fixtures, 'module', 'process_args.js'), args)
child.on('message', (msg) => {
2016-03-25 20:03:49 +00:00
assert.deepEqual(args, msg.slice(2))
done()
})
child.send('message')
})
it('works in forked process', (done) => {
const child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js'))
child.on('message', (msg) => {
2016-03-25 20:03:49 +00:00
assert.equal(msg, 'message')
done()
})
child.send('message')
})
it('works in forked process when options.env is specifed', (done) => {
const child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js'), [], {
2016-01-12 02:40:23 +00:00
path: process.env['PATH']
2016-03-25 20:03:49 +00:00
})
child.on('message', (msg) => {
2016-03-25 20:03:49 +00:00
assert.equal(msg, 'message')
done()
})
child.send('message')
})
it('works in browser process', (done) => {
const fork = remote.require('child_process').fork
const child = fork(path.join(fixtures, 'module', 'ping.js'))
child.on('message', (msg) => {
2016-03-25 20:03:49 +00:00
assert.equal(msg, 'message')
done()
})
child.send('message')
})
it('has String::localeCompare working in script', (done) => {
const child = ChildProcess.fork(path.join(fixtures, 'module', 'locale-compare.js'))
child.on('message', (msg) => {
2016-03-25 20:03:49 +00:00
assert.deepEqual(msg, [0, -1, 1])
done()
})
child.send('message')
})
it('has setImmediate working in script', (done) => {
const child = ChildProcess.fork(path.join(fixtures, 'module', 'set-immediate.js'))
child.on('message', (msg) => {
2016-03-25 20:03:49 +00:00
assert.equal(msg, 'ok')
done()
})
child.send('message')
})
it('pipes stdio', (done) => {
const child = ChildProcess.fork(path.join(fixtures, 'module', 'process-stdout.js'), {silent: true})
let data = ''
child.stdout.on('data', (chunk) => {
data += String(chunk)
})
child.on('close', (code) => {
assert.equal(code, 0)
assert.equal(data, 'pipes stdio')
done()
})
})
it('works when sending a message to a process forked with the --eval argument', (done) => {
2017-04-06 16:52:52 +00:00
const source = "process.on('message', (message) => { process.send(message) })"
const forked = ChildProcess.fork('--eval', [source])
2017-04-06 16:52:52 +00:00
forked.once('message', (message) => {
assert.equal(message, 'hello')
done()
})
forked.send('hello')
})
2016-03-25 20:03:49 +00:00
})
describe('child_process.spawn', () => {
let child
afterEach(() => {
if (child != null) child.kill()
})
it('supports spawning Electron as a node process via the ELECTRON_RUN_AS_NODE env var', (done) => {
child = ChildProcess.spawn(process.execPath, [path.join(__dirname, 'fixtures', 'module', 'run-as-node.js')], {
env: {
ELECTRON_RUN_AS_NODE: true
}
})
let output = ''
child.stdout.on('data', (data) => {
output += data
})
child.stdout.on('close', () => {
assert.deepEqual(JSON.parse(output), {
processLog: process.platform === 'win32' ? 'function' : 'undefined',
processType: 'undefined',
window: 'undefined'
})
done()
})
})
})
2016-03-25 20:03:49 +00:00
})
describe('contexts', () => {
describe('setTimeout in fs callback', () => {
it('does not crash', (done) => {
fs.readFile(__filename, () => {
2016-03-25 20:03:49 +00:00
setTimeout(done, 0)
})
})
})
describe('error thrown in renderer process node context', () => {
it('gets emitted as a process uncaughtException event', (done) => {
const error = new Error('boo!')
const listeners = process.listeners('uncaughtException')
2016-03-25 20:03:49 +00:00
process.removeAllListeners('uncaughtException')
process.on('uncaughtException', (thrown) => {
assert.strictEqual(thrown, error)
2016-03-25 20:03:49 +00:00
process.removeAllListeners('uncaughtException')
listeners.forEach((listener) => {
process.on('uncaughtException', listener)
})
2016-03-25 20:03:49 +00:00
done()
})
fs.readFile(__filename, () => {
2016-03-25 20:03:49 +00:00
throw error
})
})
})
describe('error thrown in main process node context', () => {
it('gets emitted as a process uncaughtException event', () => {
const error = ipcRenderer.sendSync('handle-uncaught-exception', 'hello')
assert.equal(error, 'hello')
})
})
describe('promise rejection in main process node context', () => {
it('gets emitted as a process unhandledRejection event', () => {
const error = ipcRenderer.sendSync('handle-unhandled-rejection', 'hello')
assert.equal(error, 'hello')
})
})
describe('setTimeout called under Chromium event loop in browser process', () => {
it('can be scheduled in time', (done) => {
2016-03-25 20:03:49 +00:00
remote.getGlobal('setTimeout')(done, 0)
})
})
describe('setInterval called under Chromium event loop in browser process', () => {
it('can be scheduled in time', (done) => {
Fix some flaky tests in CI (#12153) * Guard whole InitPrefs with ScopedAllowIO Saw a crash: 0 0x7f8d2f7d918d base::debug::StackTrace::StackTrace() 1 0x7f8d2f7d755c base::debug::StackTrace::StackTrace() 2 0x7f8d2f867caa logging::LogMessage::~LogMessage() 3 0x7f8d2fa157c7 base::ThreadRestrictions::AssertIOAllowed() 4 0x7f8d2f83453a base::OpenFile() 5 0x7f8d2f82a967 base::ReadFileToStringWithMaxSize() 6 0x7f8d2f82ad44 base::ReadFileToString() 7 0x7f8d2f846f73 JSONFileValueDeserializer::ReadFileToString() 8 0x7f8d2f84738c JSONFileValueDeserializer::Deserialize() 9 0x7f8d35a5d1f6 <unknown> 10 0x7f8d35a5c217 JsonPrefStore::ReadPrefs() 11 0x7f8d35a87d3e PrefService::InitFromStorage() 12 0x7f8d35a87c60 PrefService::PrefService() 13 0x7f8d35a91a10 PrefServiceFactory::Create() 14 0x000000e86e1b brightray::BrowserContext::InitPrefs() 15 0x000000c2bd64 atom::AtomBrowserContext::AtomBrowserContext() 16 0x000000c320db atom::AtomBrowserContext::From() 17 0x000000b4b8b5 atom::api::Session::FromPartition() * Fix done being called twice in setInterval test The callback passed to browser process is called asyncly, so it is possible that multiple callbacks has already been scheduled before we can clearInternval. * Fix failing test when dir name has special chars The pdfSource is not escaped while parsedURL.search is. * Call done with Error instead of string * Fix crash caused by not removing input observer Solve crash: 0 libcontent.dylib content::RenderWidgetHostImpl::DispatchInputEventWithLatencyInfo(blink::WebInputEvent const&, ui::LatencyInfo*) + 214 1 libcontent.dylib content::RenderWidgetHostImpl::ForwardMouseEventWithLatencyInfo(blink::WebMouseEvent const&, ui::LatencyInfo const&) + 1350 2 libcontent.dylib content::RenderWidgetHostViewMac::ProcessMouseEvent(blink::WebMouseEvent const&, ui::LatencyInfo const&) + 44 3 libcontent.dylib content::RenderWidgetHostInputEventRouter::RouteMouseEvent(content::RenderWidgetHostViewBase*, blink::WebMouseEvent*, ui::LatencyInfo const&) + 1817 * Print detailed error * Run tests after server is ready
2018-03-07 05:40:27 +00:00
let interval = null
2018-03-14 05:51:47 +00:00
let clearing = false
Fix some flaky tests in CI (#12153) * Guard whole InitPrefs with ScopedAllowIO Saw a crash: 0 0x7f8d2f7d918d base::debug::StackTrace::StackTrace() 1 0x7f8d2f7d755c base::debug::StackTrace::StackTrace() 2 0x7f8d2f867caa logging::LogMessage::~LogMessage() 3 0x7f8d2fa157c7 base::ThreadRestrictions::AssertIOAllowed() 4 0x7f8d2f83453a base::OpenFile() 5 0x7f8d2f82a967 base::ReadFileToStringWithMaxSize() 6 0x7f8d2f82ad44 base::ReadFileToString() 7 0x7f8d2f846f73 JSONFileValueDeserializer::ReadFileToString() 8 0x7f8d2f84738c JSONFileValueDeserializer::Deserialize() 9 0x7f8d35a5d1f6 <unknown> 10 0x7f8d35a5c217 JsonPrefStore::ReadPrefs() 11 0x7f8d35a87d3e PrefService::InitFromStorage() 12 0x7f8d35a87c60 PrefService::PrefService() 13 0x7f8d35a91a10 PrefServiceFactory::Create() 14 0x000000e86e1b brightray::BrowserContext::InitPrefs() 15 0x000000c2bd64 atom::AtomBrowserContext::AtomBrowserContext() 16 0x000000c320db atom::AtomBrowserContext::From() 17 0x000000b4b8b5 atom::api::Session::FromPartition() * Fix done being called twice in setInterval test The callback passed to browser process is called asyncly, so it is possible that multiple callbacks has already been scheduled before we can clearInternval. * Fix failing test when dir name has special chars The pdfSource is not escaped while parsedURL.search is. * Call done with Error instead of string * Fix crash caused by not removing input observer Solve crash: 0 libcontent.dylib content::RenderWidgetHostImpl::DispatchInputEventWithLatencyInfo(blink::WebInputEvent const&, ui::LatencyInfo*) + 214 1 libcontent.dylib content::RenderWidgetHostImpl::ForwardMouseEventWithLatencyInfo(blink::WebMouseEvent const&, ui::LatencyInfo const&) + 1350 2 libcontent.dylib content::RenderWidgetHostViewMac::ProcessMouseEvent(blink::WebMouseEvent const&, ui::LatencyInfo const&) + 44 3 libcontent.dylib content::RenderWidgetHostInputEventRouter::RouteMouseEvent(content::RenderWidgetHostViewBase*, blink::WebMouseEvent*, ui::LatencyInfo const&) + 1817 * Print detailed error * Run tests after server is ready
2018-03-07 05:40:27 +00:00
const clear = () => {
2018-03-14 05:51:47 +00:00
if (interval === null || clearing) {
Fix some flaky tests in CI (#12153) * Guard whole InitPrefs with ScopedAllowIO Saw a crash: 0 0x7f8d2f7d918d base::debug::StackTrace::StackTrace() 1 0x7f8d2f7d755c base::debug::StackTrace::StackTrace() 2 0x7f8d2f867caa logging::LogMessage::~LogMessage() 3 0x7f8d2fa157c7 base::ThreadRestrictions::AssertIOAllowed() 4 0x7f8d2f83453a base::OpenFile() 5 0x7f8d2f82a967 base::ReadFileToStringWithMaxSize() 6 0x7f8d2f82ad44 base::ReadFileToString() 7 0x7f8d2f846f73 JSONFileValueDeserializer::ReadFileToString() 8 0x7f8d2f84738c JSONFileValueDeserializer::Deserialize() 9 0x7f8d35a5d1f6 <unknown> 10 0x7f8d35a5c217 JsonPrefStore::ReadPrefs() 11 0x7f8d35a87d3e PrefService::InitFromStorage() 12 0x7f8d35a87c60 PrefService::PrefService() 13 0x7f8d35a91a10 PrefServiceFactory::Create() 14 0x000000e86e1b brightray::BrowserContext::InitPrefs() 15 0x000000c2bd64 atom::AtomBrowserContext::AtomBrowserContext() 16 0x000000c320db atom::AtomBrowserContext::From() 17 0x000000b4b8b5 atom::api::Session::FromPartition() * Fix done being called twice in setInterval test The callback passed to browser process is called asyncly, so it is possible that multiple callbacks has already been scheduled before we can clearInternval. * Fix failing test when dir name has special chars The pdfSource is not escaped while parsedURL.search is. * Call done with Error instead of string * Fix crash caused by not removing input observer Solve crash: 0 libcontent.dylib content::RenderWidgetHostImpl::DispatchInputEventWithLatencyInfo(blink::WebInputEvent const&, ui::LatencyInfo*) + 214 1 libcontent.dylib content::RenderWidgetHostImpl::ForwardMouseEventWithLatencyInfo(blink::WebMouseEvent const&, ui::LatencyInfo const&) + 1350 2 libcontent.dylib content::RenderWidgetHostViewMac::ProcessMouseEvent(blink::WebMouseEvent const&, ui::LatencyInfo const&) + 44 3 libcontent.dylib content::RenderWidgetHostInputEventRouter::RouteMouseEvent(content::RenderWidgetHostViewBase*, blink::WebMouseEvent*, ui::LatencyInfo const&) + 1817 * Print detailed error * Run tests after server is ready
2018-03-07 05:40:27 +00:00
return
}
2018-03-14 05:51:47 +00:00
// interval might trigger while clearing (remote is slow sometimes)
clearing = true
2016-03-25 20:03:49 +00:00
remote.getGlobal('clearInterval')(interval)
2018-03-14 05:51:47 +00:00
clearing = false
Fix some flaky tests in CI (#12153) * Guard whole InitPrefs with ScopedAllowIO Saw a crash: 0 0x7f8d2f7d918d base::debug::StackTrace::StackTrace() 1 0x7f8d2f7d755c base::debug::StackTrace::StackTrace() 2 0x7f8d2f867caa logging::LogMessage::~LogMessage() 3 0x7f8d2fa157c7 base::ThreadRestrictions::AssertIOAllowed() 4 0x7f8d2f83453a base::OpenFile() 5 0x7f8d2f82a967 base::ReadFileToStringWithMaxSize() 6 0x7f8d2f82ad44 base::ReadFileToString() 7 0x7f8d2f846f73 JSONFileValueDeserializer::ReadFileToString() 8 0x7f8d2f84738c JSONFileValueDeserializer::Deserialize() 9 0x7f8d35a5d1f6 <unknown> 10 0x7f8d35a5c217 JsonPrefStore::ReadPrefs() 11 0x7f8d35a87d3e PrefService::InitFromStorage() 12 0x7f8d35a87c60 PrefService::PrefService() 13 0x7f8d35a91a10 PrefServiceFactory::Create() 14 0x000000e86e1b brightray::BrowserContext::InitPrefs() 15 0x000000c2bd64 atom::AtomBrowserContext::AtomBrowserContext() 16 0x000000c320db atom::AtomBrowserContext::From() 17 0x000000b4b8b5 atom::api::Session::FromPartition() * Fix done being called twice in setInterval test The callback passed to browser process is called asyncly, so it is possible that multiple callbacks has already been scheduled before we can clearInternval. * Fix failing test when dir name has special chars The pdfSource is not escaped while parsedURL.search is. * Call done with Error instead of string * Fix crash caused by not removing input observer Solve crash: 0 libcontent.dylib content::RenderWidgetHostImpl::DispatchInputEventWithLatencyInfo(blink::WebInputEvent const&, ui::LatencyInfo*) + 214 1 libcontent.dylib content::RenderWidgetHostImpl::ForwardMouseEventWithLatencyInfo(blink::WebMouseEvent const&, ui::LatencyInfo const&) + 1350 2 libcontent.dylib content::RenderWidgetHostViewMac::ProcessMouseEvent(blink::WebMouseEvent const&, ui::LatencyInfo const&) + 44 3 libcontent.dylib content::RenderWidgetHostInputEventRouter::RouteMouseEvent(content::RenderWidgetHostViewBase*, blink::WebMouseEvent*, ui::LatencyInfo const&) + 1817 * Print detailed error * Run tests after server is ready
2018-03-07 05:40:27 +00:00
interval = null
2016-03-25 20:03:49 +00:00
done()
}
interval = remote.getGlobal('setInterval')(clear, 10)
})
})
})
2017-11-06 14:25:48 +00:00
describe('inspector', () => {
let child
afterEach(() => {
if (child != null) child.kill()
})
it('supports starting the v8 inspector with --inspect/--inspect-brk', (done) => {
child = ChildProcess.spawn(process.execPath, ['--inspect-brk', path.join(__dirname, 'fixtures', 'module', 'run-as-node.js')], {
env: {
ELECTRON_RUN_AS_NODE: true
}
})
let output = ''
child.stderr.on('data', (data) => {
output += data
if (output.trim().startsWith('Debugger listening on ws://')) done()
})
child.stdout.on('data', (data) => {
done(new Error(`Unexpected output: ${data.toString()}`))
})
})
it('supports js binding', (done) => {
child = ChildProcess.spawn(process.execPath, ['--inspect', path.join(__dirname, 'fixtures', 'module', 'inspector-binding.js')], {
env: {
ELECTRON_RUN_AS_NODE: true
},
stdio: ['ipc']
})
child.on('message', ({cmd, debuggerEnabled, secondSessionOpened, success}) => {
if (cmd === 'assert') {
assert.equal(debuggerEnabled, true)
assert.equal(secondSessionOpened, false)
assert.equal(success, true)
done()
}
})
})
})
describe('message loop', () => {
describe('process.nextTick', () => {
it('emits the callback', (done) => {
2016-03-25 20:03:49 +00:00
process.nextTick(done)
})
it('works in nested calls', (done) => {
process.nextTick(() => {
process.nextTick(() => {
2016-03-25 20:03:49 +00:00
process.nextTick(done)
})
})
})
})
describe('setImmediate', () => {
it('emits the callback', (done) => {
2016-03-25 20:03:49 +00:00
setImmediate(done)
})
it('works in nested calls', (done) => {
setImmediate(() => {
setImmediate(() => {
2016-03-25 20:03:49 +00:00
setImmediate(done)
})
})
})
})
})
describe('net.connect', () => {
before(function () {
if (process.platform !== 'darwin') {
this.skip()
}
})
it('emit error when connect to a socket path without listeners', (done) => {
const socketPath = path.join(os.tmpdir(), 'atom-shell-test.sock')
const script = path.join(fixtures, 'module', 'create_socket.js')
const child = ChildProcess.fork(script, [socketPath])
child.on('exit', (code) => {
2016-03-25 20:03:49 +00:00
assert.equal(code, 0)
const client = require('net').connect(socketPath)
client.on('error', (error) => {
2016-03-25 20:03:49 +00:00
assert.equal(error.code, 'ECONNREFUSED')
done()
})
})
})
})
describe('Buffer', () => {
it('can be created from WebKit external string', () => {
const p = document.createElement('p')
2016-03-25 20:03:49 +00:00
p.innerText = '闲云潭影日悠悠,物换星移几度秋'
const b = Buffer.from(p.innerText)
2016-03-25 20:03:49 +00:00
assert.equal(b.toString(), '闲云潭影日悠悠,物换星移几度秋')
assert.equal(Buffer.byteLength(p.innerText), 45)
})
it('correctly parses external one-byte UTF8 string', () => {
const p = document.createElement('p')
2016-03-25 20:03:49 +00:00
p.innerText = 'Jøhänñéß'
const b = Buffer.from(p.innerText)
2016-03-25 20:03:49 +00:00
assert.equal(b.toString(), 'Jøhänñéß')
assert.equal(Buffer.byteLength(p.innerText), 13)
})
2016-04-05 08:08:27 +00:00
it('does not crash when creating large Buffers', () => {
let buffer = Buffer.from(new Array(4096).join(' '))
2016-06-29 16:44:38 +00:00
assert.equal(buffer.length, 4095)
buffer = Buffer.from(new Array(4097).join(' '))
2016-06-29 16:44:38 +00:00
assert.equal(buffer.length, 4096)
2016-04-05 08:08:27 +00:00
})
2017-12-27 11:14:41 +00:00
it('does not crash for crypto operations', () => {
const crypto = require('crypto')
const data = 'lG9E+/g4JmRmedDAnihtBD4Dfaha/GFOjd+xUOQI05UtfVX3DjUXvrS98p7kZQwY3LNhdiFo7MY5rGft8yBuDhKuNNag9vRx/44IuClDhdQ='
const key = 'q90K9yBqhWZnAMCMTOJfPQ=='
const cipherText = '{"error_code":114,"error_message":"Tham số không hợp lệ","data":null}'
for (let i = 0; i < 10000; ++i) {
let iv = Buffer.from('0'.repeat(32), 'hex')
let input = Buffer.from(data, 'base64')
let decipher = crypto.createDecipheriv('aes-128-cbc', Buffer.from(key, 'base64'), iv)
let result = Buffer.concat([decipher.update(input), decipher.final()])
assert.equal(cipherText, result)
}
})
2016-03-25 20:03:49 +00:00
})
describe('process.stdout', () => {
it('does not throw an exception when accessed', () => {
assert.doesNotThrow(() => {
2017-11-23 22:22:43 +00:00
// eslint-disable-next-line
2016-09-07 21:40:18 +00:00
process.stdout
})
2016-03-25 20:03:49 +00:00
})
it('does not throw an exception when calling write()', () => {
assert.doesNotThrow(() => {
2016-09-07 21:40:18 +00:00
process.stdout.write('test')
})
2016-03-25 20:03:49 +00:00
})
it('should have isTTY defined on Mac and Linux', function () {
if (isCI || process.platform === 'win32') {
// FIXME(alexeykuzmin): Skip the test.
// this.skip()
return
}
assert.equal(typeof process.stdout.isTTY, 'boolean')
})
2016-06-20 02:16:17 +00:00
it('should have isTTY undefined on Windows', function () {
if (isCI || process.platform !== 'win32') {
// FIXME(alexeykuzmin): Skip the test.
// this.skip()
return
2016-09-08 20:12:00 +00:00
}
assert.equal(process.stdout.isTTY, undefined)
2016-03-25 20:03:49 +00:00
})
})
describe('process.stdin', () => {
it('does not throw an exception when accessed', () => {
assert.doesNotThrow(() => {
2017-11-23 22:22:43 +00:00
process.stdin // eslint-disable-line
2016-09-07 21:40:18 +00:00
})
})
it('returns null when read from', () => {
2016-09-07 21:51:05 +00:00
assert.equal(process.stdin.read(), null)
2016-09-07 21:40:18 +00:00
})
})
describe('process.version', () => {
it('should not have -pre', () => {
assert(!process.version.endsWith('-pre'))
})
})
describe('vm.createContext', () => {
it('should not crash', () => {
2016-03-25 20:03:49 +00:00
require('vm').runInNewContext('')
})
})
it('includes the electron version in process.versions', () => {
2017-11-13 19:22:34 +00:00
assert(/^\d+\.\d+\.\d+(\S*)?$/.test(process.versions.electron))
})
it('includes the chrome version in process.versions', () => {
assert(/^\d+\.\d+\.\d+\.\d+$/.test(process.versions.chrome))
})
2016-03-25 20:03:49 +00:00
})