Skip to content

Commit 3088704

Browse files
lal12Luca Lindhorst
and
Luca Lindhorst
authored
test: move most of test-ffi to own testlib
this also fixes issues with ffi test on windows and macos arm64 Co-authored-by: Luca Lindhorst <l.lindhorst@wut.de>
1 parent b3453ab commit 3088704

File tree

2 files changed

+158
-111
lines changed

2 files changed

+158
-111
lines changed

tests/fixtures/ffi-test-lib.c

+94
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,104 @@
11
#include <string.h>
22
#include <stdlib.h>
33
#include <time.h>
4+
#include <stdio.h>
5+
#include <stdarg.h>
6+
#include <stdint.h>
47

58
int test_int = 123;
69
int *test_int_ptr = &test_int;
710

811
int call_callback(int (*fun)(int), int a){
912
return fun(a);
1013
}
14+
15+
int simple_func1(int a){
16+
return a+1;
17+
}
18+
19+
float simple_func2(float a){
20+
return a+1;
21+
}
22+
23+
double simple_func3(double a){
24+
return a+1;
25+
}
26+
27+
int parse_int(char* str){
28+
return atoi(str);
29+
}
30+
31+
char* int_to_string(int a){
32+
static char str[255];
33+
if(snprintf(str, sizeof(str), "%d", a) < 0){
34+
return NULL;
35+
}
36+
return str;
37+
}
38+
39+
int test_sprintf(char *str, const char *format, ...){
40+
va_list argp;
41+
va_start(argp, format);
42+
int ret = vsprintf(str, format, argp);
43+
va_end(argp);
44+
return ret;
45+
}
46+
47+
char* test_strcat(char* a, char* b){
48+
return strcat(a, b);
49+
}
50+
51+
struct test{
52+
int a;
53+
char b;
54+
uint64_t c;
55+
};
56+
57+
struct test return_struct_test(int a){
58+
struct test st;
59+
st.a = a;
60+
st.b = 'b';
61+
st.c = 123;
62+
return st;
63+
}
64+
65+
char* sprint_struct_test(struct test* t){
66+
static char str[255];
67+
snprintf(str, 255, "a: %d, b: %u, c: %llu", t->a, t->b, t->c);
68+
return str;
69+
}
70+
71+
struct test_handle_entry{
72+
int a;
73+
};
74+
struct test_handle{
75+
unsigned count;
76+
unsigned max;
77+
struct test_handle_entry* entry;
78+
};
79+
struct test_handle* open_test_handle(unsigned count){
80+
struct test_handle* th = malloc(sizeof(struct test_handle));
81+
th->count = 0;
82+
th->max = count;
83+
th->entry = NULL;
84+
return th;
85+
}
86+
void close_test_handle(struct test_handle* th){
87+
if(th->entry){
88+
free(th->entry);
89+
th->entry = NULL;
90+
}
91+
free(th);
92+
}
93+
struct test_handle_entry* get_next_entry(struct test_handle* th){
94+
if(th->entry){
95+
free(th->entry);
96+
th->entry = NULL;
97+
}
98+
if(th->count < th->max){
99+
th->count++;
100+
th->entry = malloc(sizeof(struct test_handle_entry));
101+
th->entry->a = th->count;
102+
}
103+
return th->entry;
104+
}

tests/test-ffi.js

+64-111
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,7 @@ import assert from 'tjs:assert';
22
import FFI from 'tjs:ffi';
33
import initCParser from '../src/js/stdlib/ffi/ffiutils.js';
44

5-
const isMacArm = tjs.platform === 'darwin' && tjs.uname().machine === 'arm64';
6-
const isWindows = tjs.platform === 'windows';
7-
85
(function(){
9-
const libm = new FFI.Lib(FFI.Lib.LIBM_NAME);
10-
const libc = new FFI.Lib(FFI.Lib.LIBC_NAME);
11-
126
let sopath = './build/libffi-test.so';
137
switch(tjs.platform){
148
case 'linux':
@@ -21,42 +15,37 @@ const isWindows = tjs.platform === 'windows';
2115
sopath = './build/libffi-test.dll';
2216
break;
2317
}
18+
const testlib = new FFI.Lib(sopath);
2419

2520
function testSimpleCalls(){
26-
27-
const absF = new FFI.CFunction(libm.symbol('abs'), FFI.types.sint, [FFI.types.sint]);
28-
assert.eq(absF.call(-9), 9);
21+
const simple_func1 = new FFI.CFunction(testlib.symbol('simple_func1'), FFI.types.sint, [FFI.types.sint]);
22+
assert.eq(simple_func1.call(-9), -8);
2923

30-
if(!isWindows){ // for some reason, windows (mingw) does not find this function
31-
const fabsfF = new FFI.CFunction(libm.symbol('fabsf'), FFI.types.float, [FFI.types.float]);
32-
assert.ok(Math.abs(fabsfF.call(-3.45) - 3.45) < 0.00001);
33-
assert.eq(fabsfF.call(-4), 4);
34-
}
24+
const simple_func2 = new FFI.CFunction(testlib.symbol('simple_func2'), FFI.types.float, [FFI.types.float]);
25+
assert.ok(Math.abs(simple_func2.call(98.9) - 99.9) < 0.00001);
3526

36-
const atoiF = new FFI.CFunction(libc.symbol('atoi'), FFI.types.sint, [FFI.types.string]);
27+
const simple_func3 = new FFI.CFunction(testlib.symbol('simple_func3'), FFI.types.double, [FFI.types.double]);
28+
assert.ok(Math.abs(simple_func3.call(98.9) - 99.9) < 0.00001);
29+
30+
const atoiF = new FFI.CFunction(testlib.symbol('parse_int'), FFI.types.sint, [FFI.types.string]);
3731
assert.eq(atoiF.call("1234"), 1234);
3832

39-
const strerrorF = new FFI.CFunction(libc.symbol('strerror'), FFI.types.string, [FFI.types.sint]);
40-
assert.eq(strerrorF.call(1 /* EPERM */), "Operation not permitted");
33+
const strerrorF = new FFI.CFunction(testlib.symbol('int_to_string'), FFI.types.string, [FFI.types.sint]);
34+
assert.eq(strerrorF.call(345), "345");
4135

42-
// Not sure what's wrong on macOS + arm64
43-
if (!isMacArm) {
44-
const sprintfF3 = new FFI.CFunction(libc.symbol('sprintf'), FFI.types.sint, [FFI.types.buffer, FFI.types.string, FFI.types.sint], 1);
45-
const strbuf = new Uint8Array(15); // 14 byte string + null byte
46-
assert.eq(sprintfF3.call(strbuf, 'printf test %d\n', 5), 14);
47-
assert.eq(FFI.bufferToString(strbuf), 'printf test 5\n');
48-
}
36+
const sprintfF3 = new FFI.CFunction(testlib.symbol('test_sprintf'), FFI.types.sint, [FFI.types.buffer, FFI.types.string, FFI.types.sint], 2);
37+
const strbuf = new Uint8Array(15); // 14 byte string + null byte
38+
assert.eq(sprintfF3.call(strbuf, 'printf test %d\n', 5), 14);
39+
assert.eq(FFI.bufferToString(strbuf), 'printf test 5\n');
4940

50-
const strcatF = new FFI.CFunction(libc.symbol('strcat'), FFI.types.string, [FFI.types.buffer, FFI.types.string]);
41+
const strcatF = new FFI.CFunction(testlib.symbol('test_strcat'), FFI.types.string, [FFI.types.buffer, FFI.types.string]);
5142
const strbuf2 = new Uint8Array(12);
5243
strbuf2.set((new TextEncoder()).encode('part1:'));
5344
assert.eq(strcatF.call(strbuf2, "part2"), "part1:part2");
5445
assert.eq(FFI.bufferToString(strbuf2), "part1:part2");
5546
}
5647

5748
function testSimpleVariables() {
58-
const testlib = new FFI.Lib(sopath);
59-
6049
const testIntSymbol = testlib.symbol('test_int');
6150
const testIntPointer = new FFI.Pointer(testIntSymbol.addr, 1, FFI.types.sint);
6251
assert.eq(testIntPointer.deref(), 123);
@@ -69,59 +58,36 @@ const isWindows = tjs.platform === 'windows';
6958
}
7059

7160
function testStructs(){
72-
const divT = new FFI.StructType([['quot', FFI.types.sint], ['rem', FFI.types.sint]], 'div_t');
73-
const divF = new FFI.CFunction(libc.symbol('div'), divT, [FFI.types.sint, FFI.types.sint]);
74-
assert.equal(divF.call(10, 3), {quot:3, rem:1});
61+
const test_t = new FFI.StructType([['a', FFI.types.sint], ['b', FFI.types.uchar], ['c', FFI.types.uint64]], 'test_struct');
62+
const return_struct_test = new FFI.CFunction(testlib.symbol('return_struct_test'), test_t, [FFI.types.sint]);
63+
assert.equal(return_struct_test.call(10), {a:10, b: "b".charCodeAt(0), c: 123});
7564
}
7665

7766
function testPointersAndStructsOpendir(){
78-
// for some reason, windows (mingw) does not find this function
79-
// for some (other) reason, macOS on arm segfaults
80-
if(isMacArm || isWindows) {
81-
return;
82-
}
83-
const opendirF = new FFI.CFunction(libc.symbol('opendir'), FFI.types.pointer, [FFI.types.string]);
84-
let direntSt;
85-
if(tjs.platform == 'darwin'){ // macos has another dirent definition
86-
direntSt = new FFI.StructType([
87-
['fileno', FFI.types.uint32],
88-
['reclen', FFI.types.uint16],
89-
['type', FFI.types.uint8],
90-
['namelen', FFI.types.uint8],
91-
['name', new FFI.StaticStringType(255, 'char255') ],
92-
], 'dirent');
93-
}else{
94-
direntSt = new FFI.StructType([
95-
['ino', FFI.types.size],
96-
['type', FFI.types.size],
97-
['reclen', FFI.types.uint16],
98-
['type', FFI.types.uint8],
99-
['name', new FFI.StaticStringType(255, 'char255') ],
100-
], 'dirent');
101-
}
102-
const direntPtrT = new FFI.PointerType(direntSt, 1);
103-
const readdirF = new FFI.CFunction(libc.symbol('readdir'), direntPtrT, [FFI.types.pointer]);
104-
const closedirF = new FFI.CFunction(libc.symbol('closedir'), FFI.types.sint, [FFI.types.pointer]);
67+
const open_test_handle = new FFI.CFunction(testlib.symbol('open_test_handle'), FFI.types.pointer, [FFI.types.sint]);
68+
const entry_t = new FFI.StructType([['a', FFI.types.sint]]);
69+
const entry_ptr_t = new FFI.PointerType(entry_t, 1);
70+
const get_next_entry = new FFI.CFunction(testlib.symbol('get_next_entry'), entry_ptr_t, [FFI.types.pointer]);
71+
const close_test_handle = new FFI.CFunction(testlib.symbol('close_test_handle'), FFI.types.void, [FFI.types.pointer]);
10572

106-
const dirH = opendirF.call(import.meta.dirname);
107-
assert.ok(dirH !== null);
108-
const fileList = [];
109-
let direntPtr;
73+
const handle = open_test_handle.call(5);
74+
let i = 0;
75+
let entry;
11076
do{
111-
direntPtr = readdirF.call(dirH);
112-
if(!direntPtr.isNull){
113-
const obj = direntPtr.deref();
77+
entry = get_next_entry.call(handle);
78+
if(!entry.isNull){
79+
i++;
80+
const obj = entry.deref();
11481
assert.eq(typeof obj, 'object');
115-
fileList.push(obj);
116-
}else{
117-
assert.eq(direntPtr.addr, 0n);
82+
assert.eq(obj.a, i);
11883
}
119-
}while(!direntPtr.isNull);
120-
assert.ok(fileList.some(e=>e.name == 'test-ffi.js'));
121-
assert.eq(closedirF.call(dirH), 0);
84+
}while(!entry.isNull);
85+
close_test_handle.call(handle);
86+
assert.eq(i, 5);
12287
}
12388

12489
function testPointersAndStructsTime(){
90+
const libc = new FFI.Lib(FFI.Lib.LIBC_NAME);
12591
const tmT = new FFI.StructType([
12692
['sec', FFI.types.sint],
12793
['min', FFI.types.sint],
@@ -618,51 +584,39 @@ const isWindows = tjs.platform === 'windows';
618584
}
619585

620586
function testLibFromCProto() {
621-
const libc = new FFI.Lib(FFI.Lib.LIBC_NAME);
622-
libc.parseCProto(`
623-
typedef long int time_t;
624-
typedef long clock_t;
625-
626-
struct tm
627-
{
628-
int sec;
629-
int min;
630-
int hour;
631-
int mday;
632-
int mon;
633-
int year;
634-
int wday;
635-
int yday;
636-
int isdst;
637-
long int gmtoff;
638-
const char *tm_zone;
587+
const testlib = new FFI.Lib(sopath);
588+
testlib.parseCProto(`
589+
char* test_strcat(char* a, char* b);
590+
struct test{
591+
int a;
592+
char b;
593+
uint64_t c;
639594
};
640-
641-
clock_t clock();
642-
time_t time (time_t *__timer);
643-
double difftime (time_t __time1, time_t __time0);
644-
char* asctime (struct tm *__tp);
595+
typedef struct test s_test;
596+
s_test return_struct_test(int a);
597+
char* sprint_struct_test(s_test* t);
645598
`);
646599

647-
const clockVal = libc.call('clock');
648-
assert.ok(typeof clockVal == 'number' && clockVal > 0);
649-
assert.ok(libc.call('time', [null]) - Date.now()/1000 + 1 < 2 );
650-
assert.eq(libc.call('difftime', 100, 50), 50);
651-
const structTmT = libc.getType('struct tm');
652-
const tmData = {
653-
sec: 0, min: 0, hour: 0,
654-
year: 122, mon: 6, mday: 1,
655-
isdst: 0, gmtoff: 0, tm_zone: 'UTC'
600+
const strcatF = new FFI.CFunction(testlib.symbol('test_strcat'), FFI.types.string, [FFI.types.buffer, FFI.types.string]);
601+
const strbuf2 = new Uint8Array(12);
602+
strbuf2.set((new TextEncoder()).encode('part1:'));
603+
assert.eq(strcatF.call(strbuf2, "part2"), "part1:part2");
604+
assert.eq(FFI.bufferToString(strbuf2), "part1:part2");
605+
606+
const structTest = testlib.getType('struct test');
607+
assert.eq(structTest, testlib.getType('s_test'));
608+
const structData = {
609+
a: 1, b: 2, c: 3
656610
};
657-
const tmBuf = structTmT.toBuffer(tmData);
658-
const regex = /^Sun Jul [0 ]1 00:00:00 2022\n$/;
659-
assert.truthy(libc.call('asctime', FFI.Pointer.createRefFromBuf(structTmT, tmBuf)).match(regex));
660-
assert.truthy(libc.call('asctime', FFI.Pointer.createRef(structTmT, tmData)).match(regex));
611+
const tmBuf = structTest.toBuffer(structData);
612+
const expect = 'a: 1, b: 2, c: 3';
613+
assert.eq(testlib.call('sprint_struct_test', FFI.Pointer.createRefFromBuf(structTest, tmBuf)), expect);
614+
assert.eq(testlib.call('sprint_struct_test', FFI.Pointer.createRef(structTest, structData)), expect);
661615
}
662616

663617
function testCProtoPtrInStruct(){
664-
const libc = new FFI.Lib(FFI.Lib.LIBC_NAME);
665-
libc.parseCProto(`
618+
const testlib = new FFI.Lib(sopath);
619+
testlib.parseCProto(`
666620
struct a{
667621
int a;
668622
int b;
@@ -678,8 +632,8 @@ const isWindows = tjs.platform === 'windows';
678632
int f;
679633
}* asdasd2;
680634
`);
681-
assert.eq(libc.getType('asdasd').size, 2*FFI.types.pointer.size);
682-
assert.eq(libc.getType('asdasd2').size, FFI.types.pointer.size);
635+
assert.eq(testlib.getType('asdasd').size, 2*FFI.types.pointer.size);
636+
assert.eq(testlib.getType('asdasd2').size, FFI.types.pointer.size);
683637
}
684638

685639
testSimpleCalls();
@@ -691,5 +645,4 @@ const isWindows = tjs.platform === 'windows';
691645
testCProtoParser();
692646
testLibFromCProto();
693647
testCProtoPtrInStruct();
694-
695648
})();

0 commit comments

Comments
 (0)