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 createVFlutterModule = require('../example/web/vflutter.js');
const M = await createVFlutterModule();
let failures = 0;
const check = (name, ok, detail = '') => {
console.log(`${ok ? 'ok ' : 'FAIL'} ${name}${detail ? ` — ${detail}` : ''}`);
if (!ok) failures++;
};
M.ccall('vf_init', null, [], []);
check('vf_add', M.ccall('vf_add', 'number', ['number', 'number'], [20, 22]) === 42);
// vf_greet returns a V-allocated string the caller must release.
const namePtr = M._malloc(16);
M.stringToUTF8('wasm', namePtr, 16);
const resPtr = M.ccall('vf_greet', 'number', ['number'], [namePtr]);
const greeting = M.UTF8ToString(resPtr);
M.ccall('vf_free', null, ['number'], [resPtr]);
M._free(namePtr);
check('vf_greet', greeting === 'Hello, wasm, from V!', 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(
'vf_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);
|