1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/test/test-child_process.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,546 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +'use strict'; 1.9 + 1.10 +const { spawn, exec, execFile, fork } = require('sdk/system/child_process'); 1.11 +const { env, platform, pathFor } = require('sdk/system'); 1.12 +const { isNumber } = require('sdk/lang/type'); 1.13 +const { after } = require('sdk/test/utils'); 1.14 +const { emit } = require('sdk/event/core'); 1.15 +const PROFILE_DIR= pathFor('ProfD'); 1.16 +const isWindows = platform.toLowerCase().indexOf('win') === 0; 1.17 +const { getScript, cleanUp } = require('./fixtures/child-process-scripts'); 1.18 + 1.19 +// We use direct paths to these utilities as we currently cannot 1.20 +// call non-absolute paths to utilities in subprocess.jsm 1.21 +const CAT_PATH = isWindows ? 'C:\\Windows\\System32\\more.com' : '/bin/cat'; 1.22 + 1.23 +exports.testExecCallbackSuccess = function (assert, done) { 1.24 + exec(isWindows ? 'DIR /A-D' : 'ls -al', { 1.25 + cwd: PROFILE_DIR 1.26 + }, function (err, stdout, stderr) { 1.27 + assert.ok(!err, 'no errors found'); 1.28 + assert.equal(stderr, '', 'stderr is empty'); 1.29 + assert.ok(/extensions\.ini/.test(stdout), 'stdout output of `ls -al` finds files'); 1.30 + 1.31 + if (isWindows) { 1.32 + // `DIR /A-D` does not display directories on WIN 1.33 + assert.ok(!/<DIR>/.test(stdout), 1.34 + 'passing arguments in `exec` works'); 1.35 + } 1.36 + else { 1.37 + // `ls -al` should list all the priviledge information on Unix 1.38 + assert.ok(/d(r[-|w][-|x]){3}/.test(stdout), 1.39 + 'passing arguments in `exec` works'); 1.40 + } 1.41 + done(); 1.42 + }); 1.43 +}; 1.44 + 1.45 +exports.testExecCallbackError = function (assert, done) { 1.46 + exec('not-real-command', { cwd: PROFILE_DIR }, function (err, stdout, stderr) { 1.47 + assert.ok(/not-real-command/.test(err.toString()), 1.48 + 'error contains error message'); 1.49 + assert.ok(err.lineNumber >= 0, 'error contains lineNumber'); 1.50 + assert.ok(/resource:\/\//.test(err.fileName), 'error contains fileName'); 1.51 + assert.ok(err.code && isNumber(err.code), 'non-zero error code property on error'); 1.52 + assert.equal(err.signal, null, 1.53 + 'null signal property when not manually terminated'); 1.54 + assert.equal(stdout, '', 'stdout is empty'); 1.55 + assert.ok(/not-real-command/.test(stderr), 'stderr contains error message'); 1.56 + done(); 1.57 + }); 1.58 +}; 1.59 + 1.60 +exports.testExecOptionsEnvironment = function (assert, done) { 1.61 + getScript('check-env').then(envScript => { 1.62 + exec(envScript, { 1.63 + cwd: PROFILE_DIR, 1.64 + env: { CHILD_PROCESS_ENV_TEST: 'my-value-test' } 1.65 + }, function (err, stdout, stderr) { 1.66 + assert.equal(stderr, '', 'stderr is empty'); 1.67 + assert.ok(!err, 'received `cwd` option'); 1.68 + assert.ok(/my-value-test/.test(stdout), 1.69 + 'receives environment option'); 1.70 + done(); 1.71 + }); 1.72 + }); 1.73 +}; 1.74 + 1.75 +exports.testExecOptionsTimeout = function (assert, done) { 1.76 + let count = 0; 1.77 + getScript('wait').then(script => { 1.78 + let child = exec(script, { timeout: 100 }, (err, stdout, stderr) => { 1.79 + assert.equal(err.killed, true, 'error has `killed` property as true'); 1.80 + assert.equal(err.code, null, 'error has `code` as null'); 1.81 + assert.equal(err.signal, 'SIGTERM', 1.82 + 'error has `signal` as SIGTERM by default'); 1.83 + assert.equal(stdout, '', 'stdout is empty'); 1.84 + assert.equal(stderr, '', 'stderr is empty'); 1.85 + if (++count === 3) complete(); 1.86 + }); 1.87 + 1.88 + function exitHandler (code, signal) { 1.89 + assert.equal(code, null, 'error has `code` as null'); 1.90 + assert.equal(signal, 'SIGTERM', 1.91 + 'error has `signal` as SIGTERM by default'); 1.92 + if (++count === 3) complete(); 1.93 + } 1.94 + 1.95 + function closeHandler (code, signal) { 1.96 + assert.equal(code, null, 'error has `code` as null'); 1.97 + assert.equal(signal, 'SIGTERM', 1.98 + 'error has `signal` as SIGTERM by default'); 1.99 + if (++count === 3) complete(); 1.100 + } 1.101 + 1.102 + child.on('exit', exitHandler); 1.103 + child.on('close', closeHandler); 1.104 + 1.105 + function complete () { 1.106 + child.off('exit', exitHandler); 1.107 + child.off('close', closeHandler); 1.108 + done(); 1.109 + } 1.110 + }); 1.111 +}; 1.112 + 1.113 +exports.testExecFileCallbackSuccess = function (assert, done) { 1.114 + getScript('args').then(script => { 1.115 + execFile(script, ['--myargs', '-j', '-s'], { cwd: PROFILE_DIR }, function (err, stdout, stderr) { 1.116 + assert.ok(!err, 'no errors found'); 1.117 + assert.equal(stderr, '', 'stderr is empty'); 1.118 + // Trim output since different systems have different new line output 1.119 + assert.equal(stdout.trim(), '--myargs -j -s'.trim(), 'passes in correct arguments'); 1.120 + done(); 1.121 + }); 1.122 + }); 1.123 +}; 1.124 + 1.125 +exports.testExecFileCallbackError = function (assert, done) { 1.126 + execFile('not-real-command', { cwd: PROFILE_DIR }, function (err, stdout, stderr) { 1.127 + assert.ok(/NS_ERROR_FILE_UNRECOGNIZED_PATH/.test(err.message), 1.128 + 'error contains error message'); 1.129 + assert.ok(err.lineNumber >= 0, 'error contains lineNumber'); 1.130 + assert.ok(/resource:\/\//.test(err.fileName), 'error contains fileName'); 1.131 + assert.equal(stdout, '', 'stdout is empty'); 1.132 + assert.equal(stderr, '', 'stdout is empty'); 1.133 + done(); 1.134 + }); 1.135 +}; 1.136 + 1.137 +exports.testExecFileOptionsEnvironment = function (assert, done) { 1.138 + getScript('check-env').then(script => { 1.139 + execFile(script, { 1.140 + cwd: PROFILE_DIR, 1.141 + env: { CHILD_PROCESS_ENV_TEST: 'my-value-test' } 1.142 + }, function (err, stdout, stderr) { 1.143 + assert.equal(stderr, '', 'stderr is empty'); 1.144 + assert.ok(!err, 'received `cwd` option'); 1.145 + assert.ok(/my-value-test/.test(stdout), 1.146 + 'receives environment option'); 1.147 + done(); 1.148 + }); 1.149 + }); 1.150 +}; 1.151 + 1.152 +exports.testExecFileOptionsTimeout = function (assert, done) { 1.153 + let count = 0; 1.154 + getScript('wait').then(script => { 1.155 + let child = execFile(script, { timeout: 100 }, (err, stdout, stderr) => { 1.156 + assert.equal(err.killed, true, 'error has `killed` property as true'); 1.157 + assert.equal(err.code, null, 'error has `code` as null'); 1.158 + assert.equal(err.signal, 'SIGTERM', 1.159 + 'error has `signal` as SIGTERM by default'); 1.160 + assert.equal(stdout, '', 'stdout is empty'); 1.161 + assert.equal(stderr, '', 'stderr is empty'); 1.162 + if (++count === 3) complete(); 1.163 + }); 1.164 + 1.165 + function exitHandler (code, signal) { 1.166 + assert.equal(code, null, 'error has `code` as null'); 1.167 + assert.equal(signal, 'SIGTERM', 1.168 + 'error has `signal` as SIGTERM by default'); 1.169 + if (++count === 3) complete(); 1.170 + } 1.171 + 1.172 + function closeHandler (code, signal) { 1.173 + assert.equal(code, null, 'error has `code` as null'); 1.174 + assert.equal(signal, 'SIGTERM', 1.175 + 'error has `signal` as SIGTERM by default'); 1.176 + if (++count === 3) complete(); 1.177 + } 1.178 + 1.179 + child.on('exit', exitHandler); 1.180 + child.on('close', closeHandler); 1.181 + 1.182 + function complete () { 1.183 + child.off('exit', exitHandler); 1.184 + child.off('close', closeHandler); 1.185 + done(); 1.186 + } 1.187 + }); 1.188 +}; 1.189 + 1.190 +/** 1.191 + * Not necessary to test for both `exec` and `execFile`, but 1.192 + * it is necessary to test both when the buffer is larger 1.193 + * and smaller than buffer size used by the subprocess library (1024) 1.194 + */ 1.195 +exports.testExecFileOptionsMaxBufferLargeStdOut = function (assert, done) { 1.196 + let count = 0; 1.197 + let stdoutChild; 1.198 + 1.199 + // Creates a buffer of 2000 to stdout, greater than 1024 1.200 + getScript('large-out').then(script => { 1.201 + stdoutChild = execFile(script, ['10000'], { maxBuffer: 50 }, (err, stdout, stderr) => { 1.202 + assert.ok(/stdout maxBuffer exceeded/.test(err.toString()), 1.203 + 'error contains stdout maxBuffer exceeded message'); 1.204 + assert.ok(stdout.length >= 50, 'stdout has full buffer'); 1.205 + assert.equal(stderr, '', 'stderr is empty'); 1.206 + if (++count === 3) complete(); 1.207 + }); 1.208 + stdoutChild.on('exit', exitHandler); 1.209 + stdoutChild.on('close', closeHandler); 1.210 + }); 1.211 + 1.212 + function exitHandler (code, signal) { 1.213 + assert.equal(code, null, 'Exit code is null in exit handler'); 1.214 + assert.equal(signal, 'SIGTERM', 'Signal is SIGTERM in exit handler'); 1.215 + if (++count === 3) complete(); 1.216 + } 1.217 + 1.218 + function closeHandler (code, signal) { 1.219 + assert.equal(code, null, 'Exit code is null in close handler'); 1.220 + assert.equal(signal, 'SIGTERM', 'Signal is SIGTERM in close handler'); 1.221 + if (++count === 3) complete(); 1.222 + } 1.223 + 1.224 + function complete () { 1.225 + stdoutChild.off('exit', exitHandler); 1.226 + stdoutChild.off('close', closeHandler); 1.227 + done(); 1.228 + } 1.229 +}; 1.230 + 1.231 +exports.testExecFileOptionsMaxBufferLargeStdOErr = function (assert, done) { 1.232 + let count = 0; 1.233 + let stderrChild; 1.234 + // Creates a buffer of 2000 to stderr, greater than 1024 1.235 + getScript('large-err').then(script => { 1.236 + stderrChild = execFile(script, ['10000'], { maxBuffer: 50 }, (err, stdout, stderr) => { 1.237 + assert.ok(/stderr maxBuffer exceeded/.test(err.toString()), 1.238 + 'error contains stderr maxBuffer exceeded message'); 1.239 + assert.ok(stderr.length >= 50, 'stderr has full buffer'); 1.240 + assert.equal(stdout, '', 'stdout is empty'); 1.241 + if (++count === 3) complete(); 1.242 + }); 1.243 + stderrChild.on('exit', exitHandler); 1.244 + stderrChild.on('close', closeHandler); 1.245 + }); 1.246 + 1.247 + function exitHandler (code, signal) { 1.248 + assert.equal(code, null, 'Exit code is null in exit handler'); 1.249 + assert.equal(signal, 'SIGTERM', 'Signal is SIGTERM in exit handler'); 1.250 + if (++count === 3) complete(); 1.251 + } 1.252 + 1.253 + function closeHandler (code, signal) { 1.254 + assert.equal(code, null, 'Exit code is null in close handler'); 1.255 + assert.equal(signal, 'SIGTERM', 'Signal is SIGTERM in close handler'); 1.256 + if (++count === 3) complete(); 1.257 + } 1.258 + 1.259 + function complete () { 1.260 + stderrChild.off('exit', exitHandler); 1.261 + stderrChild.off('close', closeHandler); 1.262 + done(); 1.263 + } 1.264 +}; 1.265 + 1.266 +/** 1.267 + * When total buffer is < process buffer (1024), the process will exit 1.268 + * and not get a chance to be killed for violating the maxBuffer, 1.269 + * although the error will still be sent through (node behaviour) 1.270 + */ 1.271 +exports.testExecFileOptionsMaxBufferSmallStdOut = function (assert, done) { 1.272 + let count = 0; 1.273 + let stdoutChild; 1.274 + 1.275 + // Creates a buffer of 60 to stdout, less than 1024 1.276 + getScript('large-out').then(script => { 1.277 + stdoutChild = execFile(script, ['60'], { maxBuffer: 50 }, (err, stdout, stderr) => { 1.278 + assert.ok(/stdout maxBuffer exceeded/.test(err.toString()), 1.279 + 'error contains stdout maxBuffer exceeded message'); 1.280 + assert.ok(stdout.length >= 50, 'stdout has full buffer'); 1.281 + assert.equal(stderr, '', 'stderr is empty'); 1.282 + if (++count === 3) complete(); 1.283 + }); 1.284 + stdoutChild.on('exit', exitHandler); 1.285 + stdoutChild.on('close', closeHandler); 1.286 + }); 1.287 + 1.288 + function exitHandler (code, signal) { 1.289 + // Sometimes the buffer limit is hit before the process closes successfully 1.290 + // on both OSX/Windows 1.291 + if (code === null) { 1.292 + assert.equal(code, null, 'Exit code is null in exit handler'); 1.293 + assert.equal(signal, 'SIGTERM', 'Signal is SIGTERM in exit handler'); 1.294 + } 1.295 + else { 1.296 + assert.equal(code, 0, 'Exit code is 0 in exit handler'); 1.297 + assert.equal(signal, null, 'Signal is null in exit handler'); 1.298 + } 1.299 + if (++count === 3) complete(); 1.300 + } 1.301 + 1.302 + function closeHandler (code, signal) { 1.303 + // Sometimes the buffer limit is hit before the process closes successfully 1.304 + // on both OSX/Windows 1.305 + if (code === null) { 1.306 + assert.equal(code, null, 'Exit code is null in close handler'); 1.307 + assert.equal(signal, 'SIGTERM', 'Signal is SIGTERM in close handler'); 1.308 + } 1.309 + else { 1.310 + assert.equal(code, 0, 'Exit code is 0 in close handler'); 1.311 + assert.equal(signal, null, 'Signal is null in close handler'); 1.312 + } 1.313 + if (++count === 3) complete(); 1.314 + } 1.315 + 1.316 + function complete () { 1.317 + stdoutChild.off('exit', exitHandler); 1.318 + stdoutChild.off('close', closeHandler); 1.319 + done(); 1.320 + } 1.321 +}; 1.322 + 1.323 +exports.testExecFileOptionsMaxBufferSmallStdErr = function (assert, done) { 1.324 + let count = 0; 1.325 + let stderrChild; 1.326 + // Creates a buffer of 60 to stderr, less than 1024 1.327 + getScript('large-err').then(script => { 1.328 + stderrChild = execFile(script, ['60'], { maxBuffer: 50 }, (err, stdout, stderr) => { 1.329 + assert.ok(/stderr maxBuffer exceeded/.test(err.toString()), 1.330 + 'error contains stderr maxBuffer exceeded message'); 1.331 + assert.ok(stderr.length >= 50, 'stderr has full buffer'); 1.332 + assert.equal(stdout, '', 'stdout is empty'); 1.333 + if (++count === 3) complete(); 1.334 + }); 1.335 + stderrChild.on('exit', exitHandler); 1.336 + stderrChild.on('close', closeHandler); 1.337 + }); 1.338 + 1.339 + function exitHandler (code, signal) { 1.340 + // Sometimes the buffer limit is hit before the process closes successfully 1.341 + // on both OSX/Windows 1.342 + if (code === null) { 1.343 + assert.equal(code, null, 'Exit code is null in exit handler'); 1.344 + assert.equal(signal, 'SIGTERM', 'Signal is SIGTERM in exit handler'); 1.345 + } 1.346 + else { 1.347 + assert.equal(code, 0, 'Exit code is 0 in exit handler'); 1.348 + assert.equal(signal, null, 'Signal is null in exit handler'); 1.349 + } 1.350 + if (++count === 3) complete(); 1.351 + } 1.352 + 1.353 + function closeHandler (code, signal) { 1.354 + // Sometimes the buffer limit is hit before the process closes successfully 1.355 + // on both OSX/Windows 1.356 + if (code === null) { 1.357 + assert.equal(code, null, 'Exit code is null in close handler'); 1.358 + assert.equal(signal, 'SIGTERM', 'Signal is SIGTERM in close handler'); 1.359 + } 1.360 + else { 1.361 + assert.equal(code, 0, 'Exit code is 0 in close handler'); 1.362 + assert.equal(signal, null, 'Signal is null in close handler'); 1.363 + } 1.364 + if (++count === 3) complete(); 1.365 + } 1.366 + 1.367 + function complete () { 1.368 + stderrChild.off('exit', exitHandler); 1.369 + stderrChild.off('close', closeHandler); 1.370 + done(); 1.371 + } 1.372 +}; 1.373 + 1.374 +exports.testChildExecFileKillSignal = function (assert, done) { 1.375 + getScript('wait').then(script => { 1.376 + execFile(script, { 1.377 + killSignal: 'beepbeep', 1.378 + timeout: 10 1.379 + }, function (err, stdout, stderr) { 1.380 + assert.equal(err.signal, 'beepbeep', 'correctly used custom killSignal'); 1.381 + done(); 1.382 + }); 1.383 + }); 1.384 +}; 1.385 + 1.386 +exports.testChildProperties = function (assert, done) { 1.387 + getScript('check-env').then(script => { 1.388 + let child = spawn(script, { 1.389 + env: { CHILD_PROCESS_ENV_TEST: 'my-value-test' } 1.390 + }); 1.391 + 1.392 + if (isWindows) 1.393 + assert.ok(true, 'Windows environment does not have `pid`'); 1.394 + else 1.395 + assert.ok(child.pid > 0, 'Child has a pid'); 1.396 + done(); 1.397 + }); 1.398 +}; 1.399 + 1.400 +exports.testChildStdinStreamLarge = function (assert, done) { 1.401 + let REPEAT = 2000; 1.402 + let allData = ''; 1.403 + // Use direct paths to more/cat, as we do not currently support calling non-files 1.404 + // from subprocess.jsm 1.405 + let child = spawn(CAT_PATH); 1.406 + 1.407 + child.stdout.on('data', onData); 1.408 + child.on('close', onClose); 1.409 + 1.410 + for (let i = 0; i < REPEAT; i++) 1.411 + emit(child.stdin, 'data', '12345\n'); 1.412 + 1.413 + emit(child.stdin, 'end'); 1.414 + 1.415 + function onData (data) { 1.416 + allData += data; 1.417 + } 1.418 + 1.419 + function onClose (code, signal) { 1.420 + child.stdout.off('data', onData); 1.421 + child.off('close', onClose); 1.422 + assert.equal(code, 0, 'exited succesfully'); 1.423 + assert.equal(signal, null, 'no kill signal given'); 1.424 + assert.equal(allData.replace(/\W/g, '').length, '12345'.length * REPEAT, 1.425 + 'all data processed from stdin'); 1.426 + done(); 1.427 + } 1.428 +}; 1.429 + 1.430 +exports.testChildStdinStreamSmall = function (assert, done) { 1.431 + let allData = ''; 1.432 + let child = spawn(CAT_PATH); 1.433 + child.stdout.on('data', onData); 1.434 + child.on('close', onClose); 1.435 + 1.436 + emit(child.stdin, 'data', '12345'); 1.437 + emit(child.stdin, 'end'); 1.438 + 1.439 + function onData (data) { 1.440 + allData += data; 1.441 + } 1.442 + 1.443 + function onClose (code, signal) { 1.444 + child.stdout.off('data', onData); 1.445 + child.off('close', onClose); 1.446 + assert.equal(code, 0, 'exited succesfully'); 1.447 + assert.equal(signal, null, 'no kill signal given'); 1.448 + assert.equal(allData.trim(), '12345', 'all data processed from stdin'); 1.449 + done(); 1.450 + } 1.451 +}; 1.452 +/* 1.453 + * This tests failures when an error is thrown attempting to 1.454 + * spawn the process, like an invalid command 1.455 + */ 1.456 +exports.testChildEventsSpawningError= function (assert, done) { 1.457 + let handlersCalled = 0; 1.458 + let child = execFile('i-do-not-exist', (err, stdout, stderr) => { 1.459 + assert.ok(err, 'error was passed into callback'); 1.460 + assert.equal(stdout, '', 'stdout is empty') 1.461 + assert.equal(stderr, '', 'stderr is empty'); 1.462 + if (++handlersCalled === 3) complete(); 1.463 + }); 1.464 + 1.465 + child.on('error', handleError); 1.466 + child.on('exit', handleExit); 1.467 + child.on('close', handleClose); 1.468 + 1.469 + function handleError (e) { 1.470 + assert.ok(e, 'error passed into error handler'); 1.471 + if (++handlersCalled === 3) complete(); 1.472 + } 1.473 + 1.474 + function handleClose (code, signal) { 1.475 + assert.equal(code, -1, 1.476 + 'process was never spawned, therefore exit code is -1'); 1.477 + assert.equal(signal, null, 'signal should be null'); 1.478 + if (++handlersCalled === 3) complete(); 1.479 + } 1.480 + 1.481 + function handleExit (code, signal) { 1.482 + assert.fail('Close event should not be called on init failure'); 1.483 + } 1.484 + 1.485 + function complete () { 1.486 + child.off('error', handleError); 1.487 + child.off('exit', handleExit); 1.488 + child.off('close', handleClose); 1.489 + done(); 1.490 + } 1.491 +}; 1.492 + 1.493 +exports.testSpawnOptions = function (assert, done) { 1.494 + let count = 0; 1.495 + let envStdout = ''; 1.496 + let cwdStdout = ''; 1.497 + let checkEnv, checkPwd, envChild, cwdChild; 1.498 + getScript('check-env').then(script => { 1.499 + checkEnv = script; 1.500 + return getScript('check-pwd'); 1.501 + }).then(script => { 1.502 + checkPwd = script; 1.503 + 1.504 + envChild = spawn(checkEnv, { 1.505 + env: { CHILD_PROCESS_ENV_TEST: 'my-value-test' } 1.506 + }); 1.507 + cwdChild = spawn(checkPwd, { cwd: PROFILE_DIR }); 1.508 + 1.509 + // Do these need to be unbound? 1.510 + envChild.stdout.on('data', data => envStdout += data); 1.511 + cwdChild.stdout.on('data', data => cwdStdout += data); 1.512 + 1.513 + envChild.on('close', envClose); 1.514 + cwdChild.on('close', cwdClose); 1.515 + }); 1.516 + 1.517 + function envClose () { 1.518 + assert.equal(envStdout.trim(), 'my-value-test', 'spawn correctly passed in ENV'); 1.519 + if (++count === 2) complete(); 1.520 + } 1.521 + 1.522 + function cwdClose () { 1.523 + // Check for PROFILE_DIR in the output because 1.524 + // some systems resolve symbolic links, and on OSX 1.525 + // /var -> /private/var 1.526 + let isCorrectPath = ~cwdStdout.trim().indexOf(PROFILE_DIR); 1.527 + assert.ok(isCorrectPath, 'spawn correctly passed in cwd'); 1.528 + if (++count === 2) complete(); 1.529 + } 1.530 + 1.531 + function complete () { 1.532 + envChild.off('close', envClose); 1.533 + cwdChild.off('close', cwdClose); 1.534 + done(); 1.535 + } 1.536 +}; 1.537 + 1.538 +exports.testFork = function (assert) { 1.539 + assert.throws(function () { 1.540 + fork(); 1.541 + }, /not currently supported/, 'fork() correctly throws an unsupported error'); 1.542 +}; 1.543 + 1.544 +after(exports, cleanUp); 1.545 + 1.546 +require("test").run(exports); 1.547 + 1.548 +// Test disabled because of bug 979675 1.549 +module.exports = {};