Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support posix-exit-codes for child and in-process modes #11

Merged
merged 1 commit into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const mochaArgs = {};
const nodeArgs = {};
const EXIT_SUCCESS = 0;
const EXIT_FAILURE = 1;
const SIGNAL_OFFSET= 128;
const SIGNAL_OFFSET = 128;
let hasInspect = false;

const opts = loadOptions(process.argv.slice(2));
Expand Down
10 changes: 8 additions & 2 deletions lib/cli/run-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const {format} = require('util');
const {createInvalidLegacyPluginError} = require('../errors');
const {requireOrImport} = require('../nodejs/esm-utils');
const PluginLoader = require('../plugin-loader');
const EXIT_FAILURE = 1;

/**
* Exits Mocha when tests + code under test has finished execution (default)
Expand All @@ -25,7 +26,10 @@ const PluginLoader = require('../plugin-loader');
*/
const exitMochaLater = code => {
process.on('exit', () => {
process.exitCode = Math.min(code, 255);
const usePosixExitCodes = process.argv.includes('--posix-exit-codes');
const exitCode =
usePosixExitCodes && code > 0 ? EXIT_FAILURE : Math.min(code, 255);
process.exitCode = exitCode;
});
};

Expand All @@ -37,7 +41,9 @@ const exitMochaLater = code => {
* @private
*/
const exitMocha = code => {
const clampedCode = Math.min(code, 255);
const usePosixExitCodes = process.argv.includes('--posix-exit-codes');
const clampedCode =
usePosixExitCodes && code > 0 ? EXIT_FAILURE : Math.min(code, 255);
let draining = 0;

// Eagerly set the process's exit code in case stream.write doesn't
Expand Down
13 changes: 13 additions & 0 deletions test/integration/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const {format} = require('util');
const path = require('path');
const Base = require('../../lib/reporters/base');
const debug = require('debug')('mocha:test:integration:helpers');
const SIGNAL_OFFSET = 128;

/**
* Path to `mocha` executable
Expand Down Expand Up @@ -357,6 +358,18 @@ function createSubprocess(args, done, opts = {}) {
});
});

/**
* Emulate node's exit code for fatal signal. Allows tests to see the same
* exit code as the mocha cli.
*/
mocha.on('exit', (code, signal) => {
if (signal) {
mocha.exitCode =
SIGNAL_OFFSET +
(typeof signal == 'string' ? os.constants.signals[signal] : signal);
}
});

return mocha;
}

Expand Down
185 changes: 169 additions & 16 deletions test/integration/options/posixExitCodes.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,96 @@ const EXIT_FAILURE = 1;
const SIGNAL_OFFSET = 128;

describe('--posix-exit-codes', function () {
if (os.platform() !== 'win32') { // SIGTERM is not supported on Windows
describe('when enabled, and with node options', function () {
var args = ['--no-warnings', '--posix-exit-codes'];
describe('when enabled', function () {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the test need coverage for exiting with success code 0?

describe('when mocha is run as a child process', () => {
// 'no-warnings' node option makes mocha run as a child process
const args = ['--no-warnings', '--posix-exit-codes'];

it('should exit with correct POSIX shell code on SIGABRT', function (done) {
var fixture = 'signals-sigabrt.fixture.js';
runMocha(fixture, args, function postmortem(err, res) {
if (err) {
return done(err);
}
expect(res.code, 'to be', SIGNAL_OFFSET + os.constants.signals.SIGABRT);
expect(
res.code,
'to be',
SIGNAL_OFFSET + os.constants.signals.SIGABRT
);
done();
});
});

it('should exit with correct POSIX shell code on SIGTERM', function (done) {
var fixture = 'signals-sigterm.fixture.js';
// SIGTERM is not supported on Windows
if (os.platform() !== 'win32') {
var fixture = 'signals-sigterm.fixture.js';
runMocha(fixture, args, function postmortem(err, res) {
if (err) {
return done(err);
}
expect(
res.code,
'to be',
SIGNAL_OFFSET + os.constants.signals.SIGTERM
);
done();
});
} else {
done();
}
});

it('should exit with code 1 if there are test failures', function (done) {
var fixture = 'failing.fixture.js';
runMocha(fixture, args, function postmortem(err, res) {
if (err) {
return done(err);
}
expect(res.code, 'to be', SIGNAL_OFFSET + os.constants.signals.SIGTERM);
expect(res.code, 'to be', EXIT_FAILURE);
done();
});
});
});

describe('when mocha is run in-process', () => {
// Without node-specific cli options, mocha runs in-process
const args = ['--posix-exit-codes'];

it('should exit with the correct POSIX shell code on SIGABRT', function (done) {
var fixture = 'signals-sigabrt.fixture.js';
runMocha(fixture, args, function postmortem(err, res) {
if (err) {
return done(err);
}
expect(
res.code,
'to be',
SIGNAL_OFFSET + os.constants.signals.SIGABRT
);
done();
});
});

it('should exit with the correct POSIX shell code on SIGTERM', function (done) {
// SIGTERM is not supported on Windows
if (os.platform() !== 'win32') {
var fixture = 'signals-sigterm.fixture.js';
runMocha(fixture, args, function postmortem(err, res) {
if (err) {
return done(err);
}
expect(
res.code,
'to be',
SIGNAL_OFFSET + os.constants.signals.SIGTERM
);
done();
});
} else {
done();
}
});

it('should exit with code 1 if there are test failures', function (done) {
var fixture = 'failing.fixture.js';
Expand All @@ -46,19 +111,107 @@ describe('--posix-exit-codes', function () {
});
});
});
}
});

describe('when not enabled, and with node options', function () {
it('should exit with the number of failed tests', function (done) {
var fixture = 'failing.fixture.js'; // contains three failing tests
var numFailures = 3;
describe('when not enabled', function () {
const fixture = 'failing.fixture.js'; // contains three failing tests
const numFailures = 3;

describe('when mocha is run as a child process', () => {
// 'no-warnings' node option makes mocha run as a child process
var args = ['--no-warnings'];
runMocha(fixture, args, function postmortem(err, res) {
if (err) {
return done(err);

it('should exit with the correct POSIX shell code on SIGABRT', function (done) {
var fixture = 'signals-sigabrt.fixture.js';
runMocha(fixture, args, function postmortem(err, res) {
if (err) {
return done(err);
}
expect(
res.code,
'to be',
SIGNAL_OFFSET + os.constants.signals.SIGABRT
);
done();
});
});

it('should exit with the correct POSIX shell code on SIGTERM', function (done) {
// SIGTERM is not supported on Windows
if (os.platform() !== 'win32') {
var fixture = 'signals-sigterm.fixture.js';
runMocha(fixture, args, function postmortem(err, res) {
if (err) {
return done(err);
}
expect(
res.code,
'to be',
SIGNAL_OFFSET + os.constants.signals.SIGTERM
);
done();
});
} else {
done();
}
});

it('should exit with the number of failed tests', function (done) {
runMocha(fixture, args, function postmortem(err, res) {
if (err) {
return done(err);
}
expect(res.code, 'to be', numFailures);
done();
});
});
});

describe('when mocha is run in-process', () => {
var args = [];
it('should exit with the correct POSIX shell code on SIGABRT', function (done) {
var fixture = 'signals-sigabrt.fixture.js';
runMocha(fixture, args, function postmortem(err, res) {
if (err) {
return done(err);
}
expect(
res.code,
'to be',
SIGNAL_OFFSET + os.constants.signals.SIGABRT
);
done();
});
});

it('should exit with the correct POSIX shell code on SIGTERM', function (done) {
// SIGTERM is not supported on Windows
if (os.platform() !== 'win32') {
var fixture = 'signals-sigterm.fixture.js';
runMocha(fixture, args, function postmortem(err, res) {
if (err) {
return done(err);
}
expect(
res.code,
'to be',
SIGNAL_OFFSET + os.constants.signals.SIGTERM
);
done();
});
} else {
done();
}
expect(res.code, 'to be', numFailures);
done();
});

it('should exit with the number of failed tests', function (done) {
runMocha(fixture, args, function postmortem(err, res) {
if (err) {
return done(err);
}
expect(res.code, 'to be', numFailures);
done();
});
});
});
});
Expand Down