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
|
#!/usr/bin/env bash
# The same Mandelbrot frame from the native kernel and the wasm kernel,
# compared byte for byte.
#
# The point is not that floating point is deterministic in the abstract — it
# is that these two builds go through different compilers, different libm
# implementations (glibc vs emscripten's musl) and different word sizes
# (64-bit vs wasm32), and still have to agree, or "the same Nim code everywhere"
# is a slogan rather than a fact.
set -euo pipefail
cd "$(dirname "$0")/.."
[ -f build/libnimflutter.so ] || { echo "run ./tool/build.sh first"; exit 1; }
[ -f example/web/nimflutter.js ] || { echo "run ./tool/build_wasm.sh first"; exit 1; }
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
cat > "$work/native_frame.c" <<'EOF'
#include "nimflutter.h"
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
(void)argc;
const int W = 240, H = 180, ITER = 300;
size_t n = (size_t)W * H * 4;
unsigned char *b = calloc(n, 1);
nf_init();
nf_mandelbrot(b, n, W, H, -0.5, 0.0, 3.0, ITER, 0, H);
FILE *f = fopen(argv[1], "wb");
fwrite(b, 1, n, f);
fclose(f);
return 0;
}
EOF
cc -O2 -I src -o "$work/native_frame" "$work/native_frame.c" -L build -lnimflutter
LD_LIBRARY_PATH=build "$work/native_frame" "$work/native.bin"
node tool/test_wasm.mjs "$work/wasm.bin" > /dev/null
python3 - "$work/native.bin" "$work/wasm.bin" <<'EOF'
import sys
a = open(sys.argv[1], 'rb').read()
b = open(sys.argv[2], 'rb').read()
if len(a) != len(b):
sys.exit(f"FAIL size {len(a)} != {len(b)}")
bad = [(i, x, y) for i, (x, y) in enumerate(zip(a, b)) if x != y]
if bad:
worst = max(abs(x - y) for _, x, y in bad)
print(f"FAIL {len(bad)} of {len(a)} bytes differ (max delta {worst})")
for i, x, y in bad[:5]:
print(f" byte {i} (px {i//4}, ch {i%4}): native={x} wasm={y}")
sys.exit(1)
print(f"ok native and wasm kernels agree on all {len(a)} bytes")
EOF
|