Skip to content

Commit

Permalink
run specific test cases with filter conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
Deepak Rajamohan committed Oct 15, 2021
1 parent cb22841 commit 2d70c10
Show file tree
Hide file tree
Showing 15 changed files with 597 additions and 44 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,9 @@
"dev:incremental": "node test",
"doc": "doxygen doc/Doxyfile",
"lint": "eslint $(git diff --name-only refs/remotes/origin/main '**/*.js' | xargs) && node tools/clang-format",
"lint:fix": "node tools/clang-format --fix && eslint --fix $(git diff --cached --name-only '**/*.js' | xargs && git diff --name-only '**/*.js' | xargs)"
"lint:fix": "node tools/clang-format --fix && eslint --fix $(git diff --name-only refs/remotes/origin/main '**/*.js' | xargs && git diff --name-only refs/remotes/origin/main '**/*.js' | xargs)",
"preunit": "filter=\"$npm_config_filter\" node-gyp rebuild -C unit-test",
"unit": "filter=\"$npm_config_filter\" node unit-test/test"
},
"pre-commit": "lint",
"version": "4.2.0",
Expand Down
47 changes: 23 additions & 24 deletions test/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ const noop = () => {};

const mustCallChecks = [];

function runCallChecks(exitCode) {
function runCallChecks (exitCode) {
if (exitCode !== 0) return;

const failed = mustCallChecks.filter(function(context) {
const failed = mustCallChecks.filter(function (context) {
if ('minimum' in context) {
context.messageSegment = `at least ${context.minimum}`;
return context.actual < context.minimum;
Expand All @@ -20,25 +20,25 @@ function runCallChecks(exitCode) {
}
});

failed.forEach(function(context) {
failed.forEach(function (context) {
console.log('Mismatched %s function calls. Expected %s, actual %d.',
context.name,
context.messageSegment,
context.actual);
context.name,
context.messageSegment,
context.actual);
console.log(context.stack.split('\n').slice(2).join('\n'));
});

if (failed.length) process.exit(1);
}

exports.mustCall = function(fn, exact) {
exports.mustCall = function (fn, exact) {
return _mustCallInner(fn, exact, 'exact');
};
exports.mustCallAtLeast = function(fn, minimum) {
exports.mustCallAtLeast = function (fn, minimum) {
return _mustCallInner(fn, minimum, 'minimum');
};

function _mustCallInner(fn, criteria, field) {
function _mustCallInner (fn, criteria, field) {
if (typeof fn === 'number') {
criteria = fn;
fn = noop;
Expand All @@ -49,8 +49,7 @@ function _mustCallInner(fn, criteria, field) {
criteria = 1;
}

if (typeof criteria !== 'number')
throw new TypeError(`Invalid ${field} value: ${criteria}`);
if (typeof criteria !== 'number') { throw new TypeError(`Invalid ${field} value: ${criteria}`); }

const context = {
[field]: criteria,
Expand All @@ -64,50 +63,50 @@ function _mustCallInner(fn, criteria, field) {

mustCallChecks.push(context);

return function() {
return function () {
context.actual++;
return fn.apply(this, arguments);
};
}

exports.mustNotCall = function(msg) {
return function mustNotCall() {
exports.mustNotCall = function (msg) {
return function mustNotCall () {
assert.fail(msg || 'function should not have been called');
};
};

exports.runTest = async function(test, buildType, buildPathRoot = process.env.REL_BUILD_PATH || '') {
exports.runTest = async function (test, buildType, buildPathRoot = process.env.BUILD_PATH || '') {
buildType = buildType || process.config.target_defaults.default_configuration || 'Release';

const bindings = [
path.join(buildPathRoot, `../build/${buildType}/binding.node`),
path.join(buildPathRoot, `../build/${buildType}/binding_noexcept.node`),
path.join(buildPathRoot, `../build/${buildType}/binding_noexcept_maybe.node`),
path.join(buildPathRoot, `../build/${buildType}/binding_noexcept_maybe.node`)
].map(it => require.resolve(it));

for (const item of bindings) {
await Promise.resolve(test(require(item)))
.finally(exports.mustCall());
}
}
};

exports.runTestWithBindingPath = async function(test, buildType, buildPathRoot = process.env.REL_BUILD_PATH || '') {
exports.runTestWithBindingPath = async function (test, buildType, buildPathRoot = process.env.BUILD_PATH || '') {
buildType = buildType || process.config.target_defaults.default_configuration || 'Release';

const bindings = [
path.join(buildPathRoot, `../build/${buildType}/binding.node`),
path.join(buildPathRoot, `../build/${buildType}/binding_noexcept.node`),
path.join(buildPathRoot, `../build/${buildType}/binding_noexcept_maybe.node`),
path.join(buildPathRoot, `../build/${buildType}/binding_noexcept_maybe.node`)
].map(it => require.resolve(it));

for (const item of bindings) {
await test(item);
}
}
};

exports.runTestWithBuildType = async function(test, buildType) {
exports.runTestWithBuildType = async function (test, buildType) {
buildType = buildType || process.config.target_defaults.default_configuration || 'Release';

await Promise.resolve(test(buildType))
.finally(exports.mustCall());
}
await Promise.resolve(test(buildType))
.finally(exports.mustCall());
};
55 changes: 38 additions & 17 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const majorNodeVersion = process.versions.node.split('.')[0];

if (typeof global.gc !== 'function') {
// Construct the correct (version-dependent) command-line args.
let args = ['--expose-gc'];
const args = ['--expose-gc'];
const majorV8Version = process.versions.v8.split('.')[0];
if (majorV8Version < 9) {
args.push('--no-concurrent-array-buffer-freeing');
Expand All @@ -15,7 +15,7 @@ if (typeof global.gc !== 'function') {
args.push(__filename);

const child = require('./napi_child').spawnSync(process.argv[0], args, {
stdio: 'inherit',
stdio: 'inherit'
});

if (child.signal) {
Expand All @@ -27,17 +27,34 @@ if (typeof global.gc !== 'function') {
process.exit(process.exitCode);
}

const testModules = [];

const fs = require('fs');
const path = require('path');

let testModules = [];
let filterCondition = process.env.filter || '';
let filterConditionFiles = [];

if (process.env.filter !== null && process.env.filter !== undefined && process.env.filter.trim() !== '') {
filterCondition = require('../unit-test/matchModules').matchWildCards(process.env.filter);
filterConditionFiles = filterCondition.split(' ').length ? filterCondition.split(' ') : [filterCondition];
}

function checkFilterCondition (fileName, parsedFilepath) {
let result = false;

if (!filterConditionFiles.length) result = true;
if (filterConditionFiles.includes(parsedFilepath)) result = true;
if (filterConditionFiles.includes(fileName)) result = true;
return result;
}

// TODO(RaisinTen): Update this when the test filenames
// are changed into test_*.js.
function loadTestModules(currentDirectory = __dirname, pre = '') {
function loadTestModules (currentDirectory = __dirname, pre = '') {
fs.readdirSync(currentDirectory).forEach((file) => {
if (currentDirectory === __dirname && (
file === 'binding.cc' ||
file === 'binding.cc' ||
file === 'binding.gyp' ||
file === 'build' ||
file === 'common' ||
Expand All @@ -50,15 +67,19 @@ function loadTestModules(currentDirectory = __dirname, pre = '') {
return;
}
const absoluteFilepath = path.join(currentDirectory, file);
const parsedFilepath = path.parse(file);
const parsedPath = path.parse(currentDirectory);

if (fs.statSync(absoluteFilepath).isDirectory()) {
if (fs.existsSync(absoluteFilepath + '/index.js')) {
testModules.push(pre + file);
if (checkFilterCondition(parsedFilepath.name, parsedPath.base)) {
testModules.push(pre + file);
}
} else {
loadTestModules(absoluteFilepath, pre + file + '/');
}
} else {
const parsedFilepath = path.parse(file);
if (parsedFilepath.ext === '.js') {
if (parsedFilepath.ext === '.js' && checkFilterCondition(parsedFilepath.name, parsedPath.base)) {
testModules.push(pre + parsedFilepath.name);
}
}
Expand All @@ -69,7 +90,7 @@ loadTestModules();

process.config.target_defaults.default_configuration =
fs
.readdirSync(path.join(__dirname, 'build'))
.readdirSync(path.join(__dirname, process.env.REL_BUILD_PATH || '', 'build'))
.filter((item) => (item === 'Debug' || item === 'Release'))[0];

let napiVersion = Number(process.versions.napi);
Expand All @@ -87,7 +108,7 @@ if (napiVersion < 3) {
testModules.splice(testModules.indexOf('version_management'), 1);
}

if (napiVersion < 4) {
if (napiVersion < 4 && !filterConditionFiles.length) {
testModules.splice(testModules.indexOf('asyncprogressqueueworker'), 1);
testModules.splice(testModules.indexOf('asyncprogressworker'), 1);
testModules.splice(testModules.indexOf('threadsafe_function/threadsafe_function_ctx'), 1);
Expand All @@ -98,36 +119,36 @@ if (napiVersion < 4) {
testModules.splice(testModules.indexOf('threadsafe_function/threadsafe_function'), 1);
}

if (napiVersion < 5) {
if (napiVersion < 5 && !filterConditionFiles.length) {
testModules.splice(testModules.indexOf('date'), 1);
}

if (napiVersion < 6) {
if (napiVersion < 6 && !filterConditionFiles.length) {
testModules.splice(testModules.indexOf('addon'), 1);
testModules.splice(testModules.indexOf('addon_data'), 1);
testModules.splice(testModules.indexOf('bigint'), 1);
testModules.splice(testModules.indexOf('typedarray-bigint'), 1);
}

if (majorNodeVersion < 12) {
if (majorNodeVersion < 12 && !filterConditionFiles.length) {
testModules.splice(testModules.indexOf('objectwrap_worker_thread'), 1);
testModules.splice(testModules.indexOf('error_terminating_environment'), 1);
}

if (napiVersion < 8) {
if (napiVersion < 8 && !filterConditionFiles.length) {
testModules.splice(testModules.indexOf('object/object_freeze_seal'), 1);
}

(async function() {
(async function () {
console.log(`Testing with Node-API Version '${napiVersion}'.`);

console.log('Starting test suite\n');
if (filterConditionFiles.length) { console.log('Starting test suite\n', testModules); } else { console.log('Starting test suite\n'); }

// Requiring each module runs tests in the module.
for (const name of testModules) {
console.log(`Running test '${name}'`);
await require('./' + name);
};
}

console.log('\nAll tests passed!');
})().catch((error) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ const assert = require('assert');

module.exports = require('../common').runTest(test);

async function test(binding) {
async function test (binding) {
const ctx = { };
const tsfn = new binding.threadsafe_function_ctx.TSFNWrap(ctx);
const tsfn = new binding.typed_threadsafe_function_ctx.TSFNWrap(ctx);
assert(tsfn.getContext() === ctx);
await tsfn.release();
}
3 changes: 3 additions & 0 deletions unit-test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/node_modules
/build
/generated
28 changes: 28 additions & 0 deletions unit-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

# Enable running tests with specific filter conditions:

### Example:

- compile ad run only tests on objectwrap.cc and objectwrap.js
```
npm run unit --filter=objectwrap
```


# Wildcards are also possible:

### Example:

- compile and run all tests files ending with reference -> function_reference.cc object_reference.cc reference.cc
```
npm run unit --filter=*reference
```

# Multiple filter conditions are also allowed

### Example:

- compile and run all tests under folders threadsafe_function and typed_threadsafe_function and also the objectwrap.cc file
```
npm run unit --filter='*function objectwrap'
```
29 changes: 29 additions & 0 deletions unit-test/binding-file-template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* @param bindingConfigurations
* This method acts as a template to generate the content of binding.cc file
*/
module.exports.generateFileContent = function (bindingConfigurations) {
const content = [];
const inits = [];
const exports = [];

for (const config of bindingConfigurations) {
inits.push(`Object Init${config.objectName}(Env env);`);
exports.push(`exports.Set("${config.propertyName}", Init${config.objectName}(env));`);
}

content.push('#include "napi.h"');
content.push('using namespace Napi;');

inits.forEach(init => content.push(init));

content.push('Object Init(Env env, Object exports) {');

exports.forEach(exp => content.push(exp));

content.push('return exports;');
content.push('}');
content.push('NODE_API_MODULE(addon, Init);');

return Promise.resolve(content.join('\r\n'));
};
Loading

0 comments on commit 2d70c10

Please sign in to comment.