Skip to content
Open
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 PORTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Tests covering the engine-specific part of Node-API, defined in `js_native_api.h
| `test_exception` | Ported ✅ | Medium |
| `test_finalizer` | Ported ✅ | Medium |
| `test_function` | Ported ✅ | Medium |
| `test_general` | Not ported | Hard |
| `test_general` | Partial | Hard |
| `test_handle_scope` | Ported ✅ | Easy |
| `test_instance_data` | Not ported | Medium |
| `test_new_target` | Ported ✅ | Easy |
Expand Down
1 change: 1 addition & 0 deletions tests/js-native-api/test_general/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_node_api_cts_addon(test_general test_general.c)
96 changes: 96 additions & 0 deletions tests/js-native-api/test_general/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
const test_general = loadAddon('test_general');

const val1 = '1';
const val2 = 1;
const val3 = 1;

class BaseClass {
}

class ExtendedClass extends BaseClass {
}

const baseObject = new BaseClass();
const extendedObject = new ExtendedClass();

// napi_strict_equals
assert.ok(test_general.testStrictEquals(val1, val1));
assert.strictEqual(test_general.testStrictEquals(val1, val2), false);
assert.ok(test_general.testStrictEquals(val2, val3));

// napi_get_prototype
assert.strictEqual(
test_general.testGetPrototype(baseObject),
Object.getPrototypeOf(baseObject),
);
assert.strictEqual(
test_general.testGetPrototype(extendedObject),
Object.getPrototypeOf(extendedObject),
);
// Prototypes for base and extended should be different.
assert.notStrictEqual(
test_general.testGetPrototype(baseObject),
test_general.testGetPrototype(extendedObject),
);

// napi_get_version. Upstream pins this to Node.js's own Node-API version;
// portably, the addon must report whatever version the runtime declares.
assert.strictEqual(test_general.testGetVersion(), napiVersion);

// napi_typeof
[
123,
'test string',
function() {},
new Object(),
true,
undefined,
Symbol(),
].forEach((val) => {
assert.strictEqual(test_general.testNapiTypeof(val), typeof val);
});

// typeof null is 'object' in JS, so napi_null gets its own case.
assert.strictEqual(test_general.testNapiTypeof(null), 'null');

// Wrapping the same object twice fails.
const x = {};
test_general.wrap(x);
assert.throws(
() => test_general.wrap(x),
{ name: 'Error', message: 'Invalid argument' },
);
// Clean up here, otherwise derefItemWasCalled() will be polluted.
test_general.removeWrap(x);

// Wrapping twice succeeds if a removeWrap() separates the instances.
const y = {};
test_general.wrap(y);
test_general.removeWrap(y);
test_general.wrap(y);
// Clean up here, otherwise derefItemWasCalled() will be polluted.
test_general.removeWrap(y);

// napi_adjust_external_memory
const adjustedValue = test_general.testAdjustExternalMemory();
assert.strictEqual(typeof adjustedValue, 'number');
assert.ok(adjustedValue > 0);

// Garbage collecting a wrapped object calls the finalizer.
assert.strictEqual(test_general.derefItemWasCalled(), false);

(() => test_general.wrap({}))();
await gcUntil(
'deref_item() was called upon garbage collecting a wrapped object.',
() => test_general.derefItemWasCalled(),
);

// Removing a wrap and then garbage collecting does not call the finalizer.
let z = {};
test_general.testFinalizeWrap(z);
test_general.removeWrap(z);
z = null;
await gcUntil(
'finalize callback was not called upon garbage collection.',
() => !test_general.finalizeWasCalled(),
);
4 changes: 4 additions & 0 deletions tests/js-native-api/test_general/testGlobals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const test_general = loadAddon('test_general');

assert.strictEqual(test_general.getUndefined(), undefined);
assert.strictEqual(test_general.getNull(), null);
41 changes: 41 additions & 0 deletions tests/js-native-api/test_general/testInstanceOf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const test_general = loadAddon('test_general');

// napi_instanceof must agree with the `instanceof` operator, including when a
// constructor overrides Symbol.hasInstance.
function compareToNative(theObject, theConstructor) {
assert.strictEqual(
test_general.doInstanceOf(theObject, theConstructor),
theObject instanceof theConstructor,
);
}

function MyClass() {}
Object.defineProperty(MyClass, Symbol.hasInstance, {
value: function(candidate) {
return 'mark' in candidate;
},
});

function MySubClass() {}
MySubClass.prototype = new MyClass();

// MySubClass inherits MyClass's Symbol.hasInstance, so both constructors
// answer "does the candidate carry a mark?" rather than walking the prototype
// chain -- which is exactly what makes y fail against its own constructor.
let x = new MySubClass();
let y = new MySubClass();
x.mark = true;

compareToNative(x, MySubClass);
compareToNative(y, MySubClass);
compareToNative(x, MyClass);
compareToNative(y, MyClass);

x = new MyClass();
y = new MyClass();
x.mark = true;

compareToNative(x, MySubClass);
compareToNative(y, MySubClass);
compareToNative(x, MyClass);
compareToNative(y, MyClass);
7 changes: 7 additions & 0 deletions tests/js-native-api/test_general/testNapiRun.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const test_general = loadAddon('test_general');

assert.strictEqual(test_general.testNapiRun('(41.92 + 0.08);'), 42);
assert.throws(
() => test_general.testNapiRun({ abc: 'def' }),
/string was expected/,
);
10 changes: 10 additions & 0 deletions tests/js-native-api/test_general/testNapiStatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const test_general = loadAddon('test_general');

// createNapiError provokes a failing call, then checks that
// napi_get_last_error_info reports that failure. The next successful call must
// reset the recorded status back to napi_ok.
test_general.createNapiError();
assert.ok(
test_general.testNapiErrorCleanup(),
'napi_status cleaned up for second call',
);
99 changes: 99 additions & 0 deletions tests/js-native-api/test_general/testV8Instanceof.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// This test is adopted from V8's test suite.
// See deps/v8/test/mjsunit/instanceof.js in Node.js source repository.
//
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

const addon = loadAddon('test_general');

assert.ok(addon.doInstanceOf({}, Object));
assert.ok(addon.doInstanceOf([], Object));

assert.ok(!addon.doInstanceOf({}, Array));
assert.ok(addon.doInstanceOf([], Array));

function TestChains() {
const A = {};
const B = {};
const C = {};
Object.setPrototypeOf(B, A);
Object.setPrototypeOf(C, B);

function F() { }
F.prototype = A;
assert.ok(addon.doInstanceOf(C, F));
assert.ok(addon.doInstanceOf(B, F));
assert.ok(!addon.doInstanceOf(A, F));

F.prototype = B;
assert.ok(addon.doInstanceOf(C, F));
assert.ok(!addon.doInstanceOf(B, F));
assert.ok(!addon.doInstanceOf(A, F));

F.prototype = C;
assert.ok(!addon.doInstanceOf(C, F));
assert.ok(!addon.doInstanceOf(B, F));
assert.ok(!addon.doInstanceOf(A, F));
}

TestChains();

function TestExceptions() {
function F() { }
const items = [ 1, new Number(42),
true,
'string', new String('hest'),
{}, [],
F, new F(),
Object, String ];

let exceptions = 0;
let instanceofs = 0;

for (let i = 0; i < items.length; i++) {
for (let j = 0; j < items.length; j++) {
try {
if (addon.doInstanceOf(items[i], items[j])) instanceofs++;
} catch (e) {
assert.ok(addon.doInstanceOf(e, TypeError));
exceptions++;
}
}
}
assert.strictEqual(instanceofs, 10);
assert.strictEqual(exceptions, 88);

// Make sure to throw an exception if the function prototype
// isn't a proper JavaScript object.
function G() { }
G.prototype = undefined;
assert.throws(function() {
addon.doInstanceOf({}, G);
}, Error);
}

TestExceptions();
Loading
Loading