nandi/vflutter_ffipublic Fork 0
472eb493bac94bdcb79a38829ccca0c541256fef
Commits
Clone
git clone https://git.rickub.com/nandi/vflutter_ffi.git
git clone ssh://git@rickub.com/nandi/vflutter_ffi.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Replace V with Nim 472eb49 · on 472eb493bac94bdcb79a38829ccca0c541256fef · nandithebull · 4h ago
test_wasm.mjs · 67 lines · 2.6 KBJavaScript Blame HistoryRaw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// Headless check of the wasm build: same contract as the native library.
//   node tool/test_wasm.mjs [out.bin]
// With an argument, writes the raw RGBA frame there so it can be diffed
// against the native kernel (tool/test_wasm_parity.sh).
import { createRequire } from 'node:module';
import { writeFileSync } from 'node:fs';

const require = createRequire(import.meta.url);
const createNimFlutterModule = require('../example/web/nimflutter.js');
const M = await createNimFlutterModule();

let failures = 0;
const check = (name, ok, detail = '') => {
  console.log(`${ok ? 'ok  ' : 'FAIL'}  ${name}${detail ? `${detail}` : ''}`);
  if (!ok) failures++;
};

M.ccall('nf_init', null, [], []);
check('nf_add', M.ccall('nf_add', 'number', ['number', 'number'], [20, 22]) === 42);

// nf_greet returns a Nim-allocated string the caller must release.
const namePtr = M._malloc(16);
M.stringToUTF8('wasm', namePtr, 16);
const resPtr = M.ccall('nf_greet', 'number', ['number'], [namePtr]);
const greeting = M.UTF8ToString(resPtr);
M.ccall('nf_free', null, ['number'], [resPtr]);
M._free(namePtr);
check('nf_greet', greeting === 'Hello, wasm, from Nim!', greeting);

const W = 240, H = 180, ITER = 300, BYTES = W * H * 4;
const buf = M._malloc(BYTES);

const fill = (len, y0, y1) => {
  M.HEAPU8.fill(0, buf, buf + BYTES);
  M.ccall(
    'nf_mandelbrot',
    null,
    ['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'],
    [buf, len, W, H, -0.5, 0.0, 3.0, ITER, y0, y1],
  );
  return M.HEAPU8.slice(buf, buf + BYTES);
};

const frame = fill(BYTES, 0, H);
check('every pixel has alpha', frame.every((v, i) => i % 4 !== 3 || v === 255));
check('frame is a fractal, not a flat fill', new Set(frame).size > 8);

// size_t is 32-bit on wasm32 — the guard has to hold there too.
check('undersized buf_len writes nothing', fill(BYTES - 1, 0, H).every((v) => v === 0));
check('zero buf_len writes nothing', fill(0, 0, H).every((v) => v === 0));
check('inverted band writes nothing', fill(BYTES, 10, 2).every((v) => v === 0));

// Band split must reassemble into the identical frame.
const top = fill(BYTES, 0, H / 2).slice(0, (BYTES / 2));
const bottom = fill(BYTES, H / 2, H).slice(0, (BYTES / 2));
const joined = new Uint8Array(BYTES);
joined.set(top, 0);
joined.set(bottom, BYTES / 2);
check('band split matches a single-shot render', joined.every((v, i) => v === frame[i]));

M._free(buf);

if (process.argv[2]) {
  writeFileSync(process.argv[2], Buffer.from(frame));
  console.log(`wrote ${process.argv[2]} (${frame.length} bytes)`);
}
process.exit(failures === 0 ? 0 : 1);