Skip to content

Commit da7cdbf

Browse files
committed
tests
1 parent e2023fd commit da7cdbf

File tree

7 files changed

+369
-355
lines changed

7 files changed

+369
-355
lines changed

test/ffi/test-ffi-calls.js

Lines changed: 122 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -2,112 +2,120 @@
22
'use strict';
33
const common = require('../common');
44
common.skipIfFFIMissing();
5-
const { gcUntil } = require('../common/gc');
6-
const assert = require('node:assert');
5+
6+
const test = require('node:test');
77
const { spawnSync } = require('node:child_process');
88
const ffi = require('node:ffi');
99
const { cString, fixtureSymbols, libraryPath } = require('./ffi-test-common');
1010

1111
const { lib, functions: symbols } = ffi.dlopen(libraryPath, fixtureSymbols);
1212

13-
{
14-
assert.strictEqual(symbols.add_i8(120, 10), -126);
15-
assert.strictEqual(symbols.add_u8(250, 10), 4);
16-
assert.strictEqual(symbols.add_i16(10_000, -58), 9_942);
17-
assert.strictEqual(symbols.add_u16(65_530, 10), 4);
18-
assert.strictEqual(symbols.add_i32(-10, 52), 42);
19-
assert.strictEqual(symbols.add_u32(0xFFFFFFFF, 1), 0);
20-
assert.strictEqual(symbols.add_i64(20n, 22n), 42n);
21-
assert.strictEqual(symbols.add_u64(20n, 22n), 42n);
22-
23-
if (symbols.char_is_signed()) {
24-
assert.strictEqual(symbols.identity_char(-1), -1);
25-
assert.strictEqual(symbols.identity_char(-128), -128);
13+
test('character operations', (t) => {
14+
const signed = symbols.char_is_signed();
15+
16+
t.assert.strictEqual(typeof signed, 'number');
17+
if (signed) {
18+
t.assert.strictEqual(symbols.identity_char(-1), -1);
19+
t.assert.strictEqual(symbols.identity_char(-128), -128);
2620
} else {
27-
assert.strictEqual(symbols.identity_char(255), 255);
28-
assert.strictEqual(symbols.identity_char(128), 128);
21+
t.assert.strictEqual(symbols.identity_char(255), 255);
22+
t.assert.strictEqual(symbols.identity_char(128), 128);
2923
}
30-
}
31-
32-
{
33-
assert.strictEqual(symbols.add_f32(1.25, 2.75), 4);
34-
assert.strictEqual(symbols.multiply_f64(6, 7), 42);
35-
assert.strictEqual(symbols.sum_five_i32(10, 8, 7, 9, 8), 42);
36-
assert.strictEqual(symbols.sum_five_f64(10, 8, 7, 9, 8), 42);
37-
assert.strictEqual(symbols.mixed_operation(10, 2.5, 3.5, 4), 20);
38-
}
39-
40-
{
41-
assert.strictEqual(symbols.logical_and(1, 1), 1);
42-
assert.strictEqual(symbols.logical_and(1, 0), 0);
43-
assert.strictEqual(symbols.logical_or(0, 1), 1);
44-
assert.strictEqual(symbols.logical_not(0), 1);
24+
});
25+
26+
test('unsigned integer operations', (t) => {
27+
t.assert.strictEqual(symbols.add_u8(250, 10), 4);
28+
t.assert.strictEqual(symbols.add_u16(65_530, 10), 4);
29+
t.assert.strictEqual(symbols.add_u32(0xFFFFFFFF, 1), 0);
30+
t.assert.strictEqual(symbols.add_u64(20n, 22n), 42n);
31+
});
32+
33+
test('signed integer operations', (t) => {
34+
t.assert.strictEqual(symbols.add_i8(120, 10), -126);
35+
t.assert.strictEqual(symbols.add_i16(10_000, -58), 9_942);
36+
t.assert.strictEqual(symbols.add_i32(-10, 52), 42);
37+
t.assert.strictEqual(symbols.add_i64(20n, 22n), 42n);
38+
});
39+
40+
test('floating point and mixed operations', (t) => {
41+
t.assert.strictEqual(symbols.add_f32(1.25, 2.75), 4);
42+
t.assert.strictEqual(symbols.multiply_f64(6, 7), 42);
43+
t.assert.strictEqual(symbols.sum_five_i32(10, 8, 7, 9, 8), 42);
44+
t.assert.strictEqual(symbols.sum_five_f64(10, 8, 7, 9, 8), 42);
45+
t.assert.strictEqual(symbols.mixed_operation(10, 2.5, 3.5, 4), 20);
46+
});
47+
48+
test('boolean operations and bool signature validation', (t) => {
49+
t.assert.strictEqual(symbols.logical_and(1, 1), 1);
50+
t.assert.strictEqual(symbols.logical_and(1, 0), 0);
51+
t.assert.strictEqual(symbols.logical_or(0, 1), 1);
52+
t.assert.strictEqual(symbols.logical_not(0), 1);
4553

4654
const boolAdder = lib.getFunction('add_u8', {
4755
parameters: ['bool', 'bool'],
4856
result: 'bool',
4957
});
50-
assert.strictEqual(boolAdder(1, 0), 1);
51-
assert.throws(() => boolAdder(true, false), /Argument 0 must be a uint8/);
52-
}
58+
t.assert.strictEqual(boolAdder(1, 0), 1);
59+
t.assert.throws(() => boolAdder(true, false), /Argument 0 must be a uint8/);
60+
});
5361

54-
{
62+
test('pointer roundtrips and conversions', (t) => {
5563
const address = 0x1234n;
5664

57-
assert.strictEqual(symbols.identity_pointer(address), address);
58-
assert.strictEqual(symbols.pointer_to_usize(address), address);
59-
assert.strictEqual(symbols.usize_to_pointer(address), address);
60-
}
65+
t.assert.strictEqual(symbols.identity_pointer(address), address);
66+
t.assert.strictEqual(symbols.pointer_to_usize(address), address);
67+
t.assert.strictEqual(symbols.usize_to_pointer(address), address);
68+
});
6169

62-
{
63-
assert.strictEqual(symbols.string_length('hello ffi'), 9n);
64-
assert.strictEqual(symbols.string_length(cString('hello ffi')), 9n);
65-
assert.strictEqual(symbols.safe_strlen(null), -1);
70+
test('string and buffer operations', (t) => {
71+
t.assert.strictEqual(symbols.string_length('hello ffi'), 9n);
72+
t.assert.strictEqual(symbols.string_length(cString('hello ffi')), 9n);
73+
t.assert.strictEqual(symbols.safe_strlen(null), -1);
6674

6775
const concatenated = symbols.string_concat('hello ', 'ffi');
68-
assert.strictEqual(typeof concatenated, 'bigint');
69-
assert.strictEqual(ffi.toString(concatenated), 'hello ffi');
76+
t.assert.strictEqual(typeof concatenated, 'bigint');
77+
t.assert.strictEqual(ffi.toString(concatenated), 'hello ffi');
7078
symbols.free_string(concatenated);
7179

7280
const duplicated = symbols.string_duplicate('copied string');
73-
assert.strictEqual(ffi.toString(duplicated), 'copied string');
81+
t.assert.strictEqual(ffi.toString(duplicated), 'copied string');
7482
symbols.free_string(duplicated);
7583

7684
const buffer = Buffer.from([1, 2, 3, 4]);
77-
assert.strictEqual(symbols.sum_buffer(buffer, BigInt(buffer.length)), 10n);
85+
t.assert.strictEqual(symbols.sum_buffer(buffer, BigInt(buffer.length)), 10n);
7886
symbols.reverse_buffer(buffer, BigInt(buffer.length));
79-
assert.deepStrictEqual([...buffer], [4, 3, 2, 1]);
87+
t.assert.deepStrictEqual([...buffer], [4, 3, 2, 1]);
8088

8189
const typed = new Uint8Array([5, 6, 7, 8]);
82-
assert.strictEqual(symbols.sum_buffer(typed, BigInt(typed.byteLength)), 26n);
90+
t.assert.strictEqual(symbols.sum_buffer(typed, BigInt(typed.byteLength)), 26n);
8391

8492
const arrayBuffer = new Uint8Array([9, 10, 11, 12]).buffer;
85-
assert.strictEqual(symbols.sum_buffer(arrayBuffer, BigInt(arrayBuffer.byteLength)), 42n);
86-
}
93+
t.assert.strictEqual(symbols.sum_buffer(arrayBuffer, BigInt(arrayBuffer.byteLength)), 42n);
94+
});
8795

88-
{
96+
test('typed array get/set operations', (t) => {
8997
const ints = new Int32Array([10, 20, 30, 40]);
90-
assert.strictEqual(symbols.array_get_i32(ints, 2n), 30);
98+
t.assert.strictEqual(symbols.array_get_i32(ints, 2n), 30);
9199
symbols.array_set_i32(ints, 1n, 22);
92-
assert.deepStrictEqual([...ints], [10, 22, 30, 40]);
100+
t.assert.deepStrictEqual([...ints], [10, 22, 30, 40]);
93101

94102
const doubles = new Float64Array([1, 2, 3, 4]);
95-
assert.strictEqual(symbols.array_get_f64(doubles, 1n), 2);
103+
t.assert.strictEqual(symbols.array_get_f64(doubles, 1n), 2);
96104
symbols.array_set_f64(doubles, 2n, 39.5);
97-
assert.deepStrictEqual([...doubles], [1, 2, 39.5, 4]);
98-
}
105+
t.assert.deepStrictEqual([...doubles], [1, 2, 39.5, 4]);
106+
});
99107

100-
{
108+
test('counter state management', (t) => {
101109
symbols.reset_counter();
102-
assert.strictEqual(symbols.get_counter(), 0);
110+
t.assert.strictEqual(symbols.get_counter(), 0);
103111
symbols.increment_counter();
104112
symbols.increment_counter();
105-
assert.strictEqual(symbols.get_counter(), 2);
113+
t.assert.strictEqual(symbols.get_counter(), 2);
106114
symbols.reset_counter();
107-
assert.strictEqual(symbols.get_counter(), 0);
108-
}
115+
t.assert.strictEqual(symbols.get_counter(), 0);
116+
});
109117

110-
{
118+
test('register and invoke callbacks', (t) => {
111119
const seen = [];
112120
const intCallback = lib.registerCallback(
113121
{ parameters: ['i32'], result: 'i32' },
@@ -123,18 +131,18 @@ const { lib, functions: symbols } = ffi.dlopen(libraryPath, fixtureSymbols);
123131
);
124132

125133
try {
126-
assert.strictEqual(symbols.call_int_callback(intCallback, 21), 42);
134+
t.assert.strictEqual(symbols.call_int_callback(intCallback, 21), 42);
127135
symbols.call_string_callback(stringCallback, cString('hello callback'));
128-
assert.deepStrictEqual(seen, ['hello callback']);
129-
assert.strictEqual(symbols.call_binary_int_callback(binaryCallback, 19, 23), 42);
136+
t.assert.deepStrictEqual(seen, ['hello callback']);
137+
t.assert.strictEqual(symbols.call_binary_int_callback(binaryCallback, 19, 23), 42);
130138
} finally {
131139
lib.unregisterCallback(intCallback);
132140
lib.unregisterCallback(stringCallback);
133141
lib.unregisterCallback(binaryCallback);
134142
}
135-
}
143+
});
136144

137-
{
145+
test('callback ref/unref and callback validation errors', (t) => {
138146
let called = false;
139147
const values = [];
140148
const voidCallback = lib.registerCallback(() => {
@@ -154,52 +162,52 @@ const { lib, functions: symbols } = ffi.dlopen(libraryPath, fixtureSymbols);
154162
symbols.call_void_callback(voidCallback);
155163
symbols.call_callback_multiple_times(countingCallback, 5);
156164

157-
assert.strictEqual(called, true);
158-
assert.deepStrictEqual(values, [0, 1, 2, 3, 4]);
165+
t.assert.strictEqual(called, true);
166+
t.assert.deepStrictEqual(values, [0, 1, 2, 3, 4]);
159167
} finally {
160168
lib.unregisterCallback(voidCallback);
161169
lib.unregisterCallback(countingCallback);
162170
}
163171

164-
assert.throws(() => lib.refCallback(voidCallback), /Callback not found/);
165-
assert.throws(() => lib.unregisterCallback(-1n), /The first argument must be a non-negative bigint/);
166-
}
167-
168-
{
169-
assert.throws(() => symbols.add_i32(1), /Invalid argument count: expected 2, got 1/);
170-
assert.throws(() => symbols.add_i32('1', 2), /Argument 0 must be an int32/);
171-
assert.throws(() => symbols.add_i8(1.5, 1), /Argument 0 must be an int8/);
172-
assert.throws(() => symbols.add_i8(200, 1), /Argument 0 must be an int8/);
173-
assert.throws(() => symbols.add_u8(Number.NaN, 1), /Argument 0 must be a uint8/);
174-
assert.throws(() => symbols.add_u8(300, 1), /Argument 0 must be a uint8/);
175-
assert.throws(() => symbols.add_i16(1.5, 1), /Argument 0 must be an int16/);
176-
assert.throws(() => symbols.add_i16(40_000, 1), /Argument 0 must be an int16/);
177-
assert.throws(() => symbols.add_u16(Number.NaN, 1), /Argument 0 must be a uint16/);
178-
assert.throws(() => symbols.add_u16(70_000, 1), /Argument 0 must be a uint16/);
179-
assert.throws(() => symbols.add_i64(1, 2n), /Argument 0 must be an int64/);
180-
assert.throws(() => symbols.add_i64(1.5, 2n), /Argument 0 must be an int64/);
181-
assert.throws(() => symbols.add_i64(2n ** 63n, 2n), /Argument 0 must be an int64/);
182-
assert.throws(() => symbols.add_i64(-(2n ** 63n) - 1n, 2n), /Argument 0 must be an int64/);
183-
assert.throws(() => symbols.add_u64('1', 2n), /Argument 0 must be a uint64/);
184-
assert.throws(() => symbols.add_u64(1, 2n), /Argument 0 must be a uint64/);
185-
assert.throws(() => symbols.add_u64(Number.NaN, 2n), /Argument 0 must be a uint64/);
186-
assert.throws(() => symbols.add_u64(-1n, 2n), /Argument 0 must be a uint64/);
187-
assert.throws(() => symbols.add_u64(2n ** 64n, 2n), /Argument 0 must be a uint64/);
188-
assert.throws(() => symbols.identity_pointer(-1n), /Argument 0 must be a non-negative pointer bigint/);
189-
assert.throws(() => symbols.string_length(Symbol('x')), /must be a buffer, an ArrayBuffer, a string, or a bigint/);
172+
t.assert.throws(() => lib.refCallback(voidCallback), /Callback not found/);
173+
t.assert.throws(() => lib.unregisterCallback(-1n), /The first argument must be a non-negative bigint/);
174+
});
175+
176+
test('argument validation and range errors', (t) => {
177+
t.assert.throws(() => symbols.add_i32(1), /Invalid argument count: expected 2, got 1/);
178+
t.assert.throws(() => symbols.add_i32('1', 2), /Argument 0 must be an int32/);
179+
t.assert.throws(() => symbols.add_i8(1.5, 1), /Argument 0 must be an int8/);
180+
t.assert.throws(() => symbols.add_i8(200, 1), /Argument 0 must be an int8/);
181+
t.assert.throws(() => symbols.add_u8(Number.NaN, 1), /Argument 0 must be a uint8/);
182+
t.assert.throws(() => symbols.add_u8(300, 1), /Argument 0 must be a uint8/);
183+
t.assert.throws(() => symbols.add_i16(1.5, 1), /Argument 0 must be an int16/);
184+
t.assert.throws(() => symbols.add_i16(40_000, 1), /Argument 0 must be an int16/);
185+
t.assert.throws(() => symbols.add_u16(Number.NaN, 1), /Argument 0 must be a uint16/);
186+
t.assert.throws(() => symbols.add_u16(70_000, 1), /Argument 0 must be a uint16/);
187+
t.assert.throws(() => symbols.add_i64(1, 2n), /Argument 0 must be an int64/);
188+
t.assert.throws(() => symbols.add_i64(1.5, 2n), /Argument 0 must be an int64/);
189+
t.assert.throws(() => symbols.add_i64(2n ** 63n, 2n), /Argument 0 must be an int64/);
190+
t.assert.throws(() => symbols.add_i64(-(2n ** 63n) - 1n, 2n), /Argument 0 must be an int64/);
191+
t.assert.throws(() => symbols.add_u64('1', 2n), /Argument 0 must be a uint64/);
192+
t.assert.throws(() => symbols.add_u64(1, 2n), /Argument 0 must be a uint64/);
193+
t.assert.throws(() => symbols.add_u64(Number.NaN, 2n), /Argument 0 must be a uint64/);
194+
t.assert.throws(() => symbols.add_u64(-1n, 2n), /Argument 0 must be a uint64/);
195+
t.assert.throws(() => symbols.add_u64(2n ** 64n, 2n), /Argument 0 must be a uint64/);
196+
t.assert.throws(() => symbols.identity_pointer(-1n), /Argument 0 must be a non-negative pointer bigint/);
197+
t.assert.throws(() => symbols.string_length(Symbol('x')), /must be a buffer, an ArrayBuffer, a string, or a bigint/);
190198

191199
if (process.arch === 'ia32' || process.arch === 'arm') {
192-
assert.throws(() => symbols.identity_pointer(2n ** 32n), /platform pointer range|non-negative pointer bigint/);
200+
t.assert.throws(() => symbols.identity_pointer(2n ** 32n), /platform pointer range|non-negative pointer bigint/);
193201
}
194-
}
202+
});
195203

196-
{
197-
assert.strictEqual(symbols.divide_i32(84, 2), 42);
198-
assert.strictEqual(symbols.divide_i32(84, 0), 0);
199-
assert.strictEqual(symbols.safe_strlen(null), -1);
200-
}
204+
test('safe operation behavior', (t) => {
205+
t.assert.strictEqual(symbols.divide_i32(84, 2), 42);
206+
t.assert.strictEqual(symbols.divide_i32(84, 0), 0);
207+
t.assert.strictEqual(symbols.safe_strlen(null), -1);
208+
});
201209

202-
function assertInvalidCallbackReturnAborts(returnExpression) {
210+
function assertInvalidCallbackReturnAborts(t, returnExpression) {
203211
const { stderr, status, signal } = spawnSync(process.execPath, [
204212
'--experimental-ffi',
205213
'-e',
@@ -216,59 +224,15 @@ functions.call_int_callback(callback, 21);`,
216224
encoding: 'utf8',
217225
});
218226

219-
assert.ok(common.nodeProcessAborted(status, signal),
220-
`status: ${status}, signal: ${signal}\nstderr: ${stderr}`);
221-
assert.match(stderr, /Callback returned invalid value for declared FFI type/);
227+
t.assert.ok(common.nodeProcessAborted(status, signal),
228+
`status: ${status}, signal: ${signal}\nstderr: ${stderr}`);
229+
t.assert.match(stderr, /Callback returned invalid value for declared FFI type/);
222230
}
223231

224-
assertInvalidCallbackReturnAborts('1.5');
225-
assertInvalidCallbackReturnAborts('2 ** 40');
226-
227-
(async () => {
228-
{
229-
let callback = () => 1;
230-
const ref = new WeakRef(callback);
231-
const pointer = lib.registerCallback(
232-
{ parameters: ['i32'], result: 'i32' },
233-
callback,
234-
);
235-
236-
lib.unrefCallback(pointer);
237-
callback = null;
238-
239-
await gcUntil('ffi unrefCallback releases callback function', () => {
240-
return ref.deref() === undefined;
241-
});
242-
243-
assert.strictEqual(symbols.call_int_callback(pointer, 21), 0);
244-
245-
lib.unregisterCallback(pointer);
246-
}
247-
248-
{
249-
let callback = () => 1;
250-
const ref = new WeakRef(callback);
251-
const pointer = lib.registerCallback({ result: 'i32' }, callback);
232+
test('invalid callback return aborts for non-integer', (t) => {
233+
assertInvalidCallbackReturnAborts(t, '1.5');
234+
});
252235

253-
lib.unrefCallback(pointer);
254-
lib.refCallback(pointer);
255-
callback = null;
256-
257-
for (let i = 0; i < 5; i++) {
258-
await gcUntil('ffi refCallback retains callback function', () => true, 1);
259-
assert.strictEqual(typeof ref.deref(), 'function');
260-
}
261-
262-
lib.unregisterCallback(pointer);
263-
}
264-
265-
{
266-
const callback = lib.registerCallback(() => {});
267-
268-
lib.close();
269-
270-
assert.throws(() => lib.unregisterCallback(callback), /Library is closed/);
271-
assert.throws(() => lib.refCallback(callback), /Library is closed/);
272-
assert.throws(() => lib.unrefCallback(callback), /Library is closed/);
273-
}
274-
})().then(common.mustCall());
236+
test('invalid callback return aborts for out-of-range integer', (t) => {
237+
assertInvalidCallbackReturnAborts(t, '2 ** 40');
238+
});

0 commit comments

Comments
 (0)