From debda2e5ba902ea287a936c48c15e881769477e3 Mon Sep 17 00:00:00 2001 From: Jason Ginchereau Date: Fri, 19 May 2017 17:10:54 -0700 Subject: [PATCH] Make tests work on all Node versions - Make GC tests (arraybuffer, buffer, external) async, to account for different GC behavior with different versions of V8 and ChakraCore, similar to https://github.com/nodejs/node/pull/13121 - In test/index.js, use the --napi-modules and --expose-gc command-line flags automatically --- package.json | 8 ++--- test/arraybuffer.cc | 6 ++++ test/arraybuffer.js | 66 +++++++++++++++++++++++++------------- test/buffer.cc | 6 ++++ test/buffer.js | 77 ++++++++++++++++++++++++++++++--------------- test/external.cc | 3 ++ test/external.js | 53 +++++++++++++++++++------------ test/index.js | 42 ++++++++++++++----------- test/testUtil.js | 29 +++++++++++++++++ 9 files changed, 198 insertions(+), 92 deletions(-) create mode 100644 test/testUtil.js diff --git a/package.json b/package.json index 215405bea..0450eff44 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,7 @@ ], "dependencies": {}, "description": "Node.js API (N-API)", - "devDependencies": { - "node-gyp": "^3.6.0" - }, + "devDependencies": {}, "directories": {}, "homepage": "https://github.com/nodejs/node-addon-api", "license": "MIT", @@ -31,7 +29,7 @@ }, "scripts": { "pretest": "node-gyp rebuild -C test", - "test": "node --expose-gc test" + "test": "node test" }, - "version": "0.2.0" + "version": "0.2.1" } diff --git a/test/arraybuffer.cc b/test/arraybuffer.cc index 66d5da8d8..1640c3548 100644 --- a/test/arraybuffer.cc +++ b/test/arraybuffer.cc @@ -35,6 +35,8 @@ Value CreateBuffer(const CallbackInfo& info) { } Value CreateExternalBuffer(const CallbackInfo& info) { + finalizeCount = 0; + ArrayBuffer buffer = ArrayBuffer::New(info.Env(), testData, testLength); if (buffer.ByteLength() != testLength) { @@ -50,6 +52,8 @@ Value CreateExternalBuffer(const CallbackInfo& info) { } Value CreateExternalBufferWithFinalize(const CallbackInfo& info) { + finalizeCount = 0; + uint8_t* data = new uint8_t[testLength]; ArrayBuffer buffer = ArrayBuffer::New( @@ -74,6 +78,8 @@ Value CreateExternalBufferWithFinalize(const CallbackInfo& info) { } Value CreateExternalBufferWithFinalizeHint(const CallbackInfo& info) { + finalizeCount = 0; + uint8_t* data = new uint8_t[testLength]; char* hint = nullptr; diff --git a/test/arraybuffer.js b/test/arraybuffer.js index 0a4a61383..f873b0d1e 100644 --- a/test/arraybuffer.js +++ b/test/arraybuffer.js @@ -2,30 +2,52 @@ const buildType = process.config.target_defaults.default_configuration; const binding = require(`./build/${buildType}/binding.node`); const assert = require('assert'); +const testUtil = require('./testUtil'); -let test = binding.arraybuffer.createBuffer(); -binding.arraybuffer.checkBuffer(test); -assert.ok(test instanceof ArrayBuffer); +testUtil.runGCTests([ + 'Internal ArrayBuffer', + () => { + const test = binding.arraybuffer.createBuffer(); + binding.arraybuffer.checkBuffer(test); + assert.ok(test instanceof ArrayBuffer); -let test2 = test.slice(0); -binding.arraybuffer.checkBuffer(test2); + const test2 = test.slice(0); + binding.arraybuffer.checkBuffer(test2); + }, -test = binding.arraybuffer.createExternalBuffer(); -binding.arraybuffer.checkBuffer(test); -assert.ok(test instanceof ArrayBuffer); + 'External ArrayBuffer', + () => { + const test = binding.arraybuffer.createExternalBuffer(); + binding.arraybuffer.checkBuffer(test); + assert.ok(test instanceof ArrayBuffer); + assert.strictEqual(0, binding.arraybuffer.getFinalizeCount()); + }, + () => { + global.gc(); + assert.strictEqual(0, binding.arraybuffer.getFinalizeCount()); + }, -test = binding.arraybuffer.createExternalBufferWithFinalize(); -binding.arraybuffer.checkBuffer(test); -assert.ok(test instanceof ArrayBuffer); -assert.strictEqual(0, binding.arraybuffer.getFinalizeCount()); -test = null; -global.gc(); -assert.strictEqual(1, binding.arraybuffer.getFinalizeCount()); + 'External ArrayBuffer with finalizer', + () => { + const test = binding.arraybuffer.createExternalBufferWithFinalize(); + binding.arraybuffer.checkBuffer(test); + assert.ok(test instanceof ArrayBuffer); + assert.strictEqual(0, binding.arraybuffer.getFinalizeCount()); + }, + () => { + global.gc(); + assert.strictEqual(1, binding.arraybuffer.getFinalizeCount()); + }, -test = binding.arraybuffer.createExternalBufferWithFinalizeHint(); -binding.arraybuffer.checkBuffer(test); -assert.ok(test instanceof ArrayBuffer); -assert.strictEqual(1, binding.arraybuffer.getFinalizeCount()); -test = null; -global.gc(); -assert.strictEqual(2, binding.arraybuffer.getFinalizeCount()); + 'External ArrayBuffer with finalizer hint', + () => { + const test = binding.arraybuffer.createExternalBufferWithFinalizeHint(); + binding.arraybuffer.checkBuffer(test); + assert.ok(test instanceof ArrayBuffer); + assert.strictEqual(0, binding.arraybuffer.getFinalizeCount()); + }, + () => { + global.gc(); + assert.strictEqual(1, binding.arraybuffer.getFinalizeCount()); + }, +]); diff --git a/test/buffer.cc b/test/buffer.cc index 530be2faf..68ccca0e8 100644 --- a/test/buffer.cc +++ b/test/buffer.cc @@ -37,6 +37,8 @@ Value CreateBuffer(const CallbackInfo& info) { } Value CreateExternalBuffer(const CallbackInfo& info) { + finalizeCount = 0; + Buffer buffer = Buffer::New( info.Env(), testData, @@ -55,6 +57,8 @@ Value CreateExternalBuffer(const CallbackInfo& info) { } Value CreateExternalBufferWithFinalize(const CallbackInfo& info) { + finalizeCount = 0; + uint16_t* data = new uint16_t[testLength]; Buffer buffer = Buffer::New( @@ -79,6 +83,8 @@ Value CreateExternalBufferWithFinalize(const CallbackInfo& info) { } Value CreateExternalBufferWithFinalizeHint(const CallbackInfo& info) { + finalizeCount = 0; + uint16_t* data = new uint16_t[testLength]; char* hint = nullptr; diff --git a/test/buffer.js b/test/buffer.js index cb493ac2a..afdf67635 100644 --- a/test/buffer.js +++ b/test/buffer.js @@ -2,35 +2,60 @@ const buildType = process.config.target_defaults.default_configuration; const binding = require(`./build/${buildType}/binding.node`); const assert = require('assert'); +const testUtil = require('./testUtil'); -let test = binding.buffer.createBuffer(); -binding.buffer.checkBuffer(test); -assert.ok(test instanceof Buffer); +testUtil.runGCTests([ + 'Internal Buffer', + () => { + const test = binding.buffer.createBuffer(); + binding.buffer.checkBuffer(test); + assert.ok(test instanceof Buffer); -let test2 = Buffer.alloc(test.length); -test.copy(test2); -binding.buffer.checkBuffer(test2); + const test2 = Buffer.alloc(test.length); + test.copy(test2); + binding.buffer.checkBuffer(test2); + }, -test = binding.buffer.createBufferCopy(); -binding.buffer.checkBuffer(test); -assert.ok(test instanceof Buffer); + 'Buffer copy', + () => { + const test = binding.buffer.createBufferCopy(); + binding.buffer.checkBuffer(test); + assert.ok(test instanceof Buffer); + }, -test = binding.buffer.createExternalBuffer(); -binding.buffer.checkBuffer(test); -assert.ok(test instanceof Buffer); + 'External Buffer', + () => { + const test = binding.buffer.createExternalBuffer(); + binding.buffer.checkBuffer(test); + assert.ok(test instanceof Buffer); + assert.strictEqual(0, binding.buffer.getFinalizeCount()); + }, + () => { + global.gc(); + assert.strictEqual(0, binding.buffer.getFinalizeCount()); + }, -test = binding.buffer.createExternalBufferWithFinalize(); -binding.buffer.checkBuffer(test); -assert.ok(test instanceof Buffer); -assert.strictEqual(0, binding.buffer.getFinalizeCount()); -test = null; -global.gc(); -assert.strictEqual(1, binding.buffer.getFinalizeCount()); + 'External Buffer with finalizer', + () => { + const test = binding.buffer.createExternalBufferWithFinalize(); + binding.buffer.checkBuffer(test); + assert.ok(test instanceof Buffer); + assert.strictEqual(0, binding.buffer.getFinalizeCount()); + }, + () => { + global.gc(); + assert.strictEqual(1, binding.buffer.getFinalizeCount()); + }, -test = binding.buffer.createExternalBufferWithFinalizeHint(); -binding.buffer.checkBuffer(test); -assert.ok(test instanceof Buffer); -assert.strictEqual(1, binding.buffer.getFinalizeCount()); -test = null; -global.gc(); -assert.strictEqual(2, binding.buffer.getFinalizeCount()); + 'External Buffer with finalizer hint', + () => { + const test = binding.buffer.createExternalBufferWithFinalizeHint(); + binding.buffer.checkBuffer(test); + assert.ok(test instanceof Buffer); + assert.strictEqual(0, binding.buffer.getFinalizeCount()); + }, + () => { + global.gc(); + assert.strictEqual(1, binding.buffer.getFinalizeCount()); + }, +]); diff --git a/test/external.cc b/test/external.cc index 858d2cfd8..28856dc23 100644 --- a/test/external.cc +++ b/test/external.cc @@ -8,10 +8,12 @@ int testData = 1; int finalizeCount = 0; Value CreateExternal(const CallbackInfo& info) { + finalizeCount = 0; return External::New(info.Env(), &testData); } Value CreateExternalWithFinalize(const CallbackInfo& info) { + finalizeCount = 0; return External::New(info.Env(), new int(1), [](Env env, int* data) { delete data; @@ -20,6 +22,7 @@ Value CreateExternalWithFinalize(const CallbackInfo& info) { } Value CreateExternalWithFinalizeHint(const CallbackInfo& info) { + finalizeCount = 0; char* hint = nullptr; return External::New(info.Env(), new int(1), [](Env env, int* data, char* hint) { diff --git a/test/external.js b/test/external.js index 24c4310c2..d702a6a96 100644 --- a/test/external.js +++ b/test/external.js @@ -2,26 +2,39 @@ const buildType = process.config.target_defaults.default_configuration; const binding = require(`./build/${buildType}/binding.node`); const assert = require('assert'); +const testUtil = require('./testUtil'); -assert.strictEqual(0, binding.external.getFinalizeCount()); +testUtil.runGCTests([ + 'External without finalizer', + () => { + const test = binding.external.createExternal(); + assert.strictEqual(typeof test, 'object'); + binding.external.checkExternal(test); + assert.strictEqual(0, binding.external.getFinalizeCount()); + }, + () => { + assert.strictEqual(0, binding.external.getFinalizeCount()); + }, -let test = binding.external.createExternal(); -assert.strictEqual(typeof test, 'object'); -binding.external.checkExternal(test); -test = null; -global.gc(); -assert.strictEqual(0, binding.external.getFinalizeCount()); + 'External with finalizer', + () => { + const test = binding.external.createExternalWithFinalize(); + assert.strictEqual(typeof test, 'object'); + binding.external.checkExternal(test); + assert.strictEqual(0, binding.external.getFinalizeCount()); + }, + () => { + assert.strictEqual(1, binding.external.getFinalizeCount()); + }, -test = binding.external.createExternalWithFinalize(); -assert.strictEqual(typeof test, 'object'); -binding.external.checkExternal(test); -test = null; -global.gc(); -assert.strictEqual(1, binding.external.getFinalizeCount()); - -test = binding.external.createExternalWithFinalizeHint(); -assert.strictEqual(typeof test, 'object'); -binding.external.checkExternal(test); -test = null; -global.gc(); -assert.strictEqual(2, binding.external.getFinalizeCount()); + 'External with finalizer hint', + () => { + const test = binding.external.createExternalWithFinalizeHint(); + assert.strictEqual(typeof test, 'object'); + binding.external.checkExternal(test); + assert.strictEqual(0, binding.external.getFinalizeCount()); + }, + () => { + assert.strictEqual(1, binding.external.getFinalizeCount()); + }, +]); diff --git a/test/index.js b/test/index.js index 285d7ce41..38315100d 100644 --- a/test/index.js +++ b/test/index.js @@ -1,24 +1,28 @@ 'use strict'; -if (typeof global.gc !== 'function') { - throw new Error('Tests require --expose-gc flag.') -} - let testModules = [ - 'arraybuffer', - 'asyncworker', - 'buffer', - 'error', - 'external', - 'function', - 'name', + 'arraybuffer', + 'asyncworker', + 'buffer', + 'error', + 'external', + 'function', + 'name', ]; -testModules.forEach(name => { - try { - require('./' + name); - } - catch (e) { - console.error(e); - } -}); +if (typeof global.gc === 'function') { + // Requiring each module runs tests in the module. + testModules.forEach(name => { + require('./' + name); + }); +} else { + // Make it easier to run with the correct (version-dependent) command-line args. + const args = [ '--expose-gc', __filename ]; + if (require('../index').isNodeApiBuiltin) { + args.splice(0, 0, '--napi-modules'); + } + const child = require('child_process').spawnSync(process.argv[0], args, { + stdio: 'inherit', + }); + process.exitCode = child.status; +} diff --git a/test/testUtil.js b/test/testUtil.js new file mode 100644 index 000000000..db2d700c4 --- /dev/null +++ b/test/testUtil.js @@ -0,0 +1,29 @@ +// Run each test function in sequence, +// with an async delay and GC call between each. +function runGCTests(tests, i, title) { + if (!i) { + i = 0; + } + + if (tests[i]) { + if (typeof tests[i] === 'string') { + title = tests[i]; + runGCTests(tests, i + 1, title); + } else { + try { + tests[i](); + } catch (e) { + console.error('Test failed: ' + title); + throw e; + } + setImmediate(() => { + global.gc(); + runGCTests(tests, i + 1, title); + }); + } + } +} + +module.exports = { + runGCTests, +};