| Replace V with Nim 472eb49 nandithebull 19h ago | 1 | // Guards the nf_mandelbrot contract at the C boundary. |
| Add a WebAssembly backend for the example 728acaa nandithebull 19h ago | 2 | // |
| Replace V with Nim 472eb49 nandithebull 19h ago | 3 | // Everything else in the suite goes through lib/nimflutter_ffi.dart, which |
| Add a WebAssembly backend for the example 728acaa nandithebull 19h ago | 4 | // always passes a correctly sized buffer. These tests deliberately bypass it |
| 5 | // and call the raw symbol the way a careless C caller would, because the |
| 6 | // point of `buf_len` is to survive exactly that. Needs the native build on |
| 7 | // the loader path — see widget_test.dart. |
| 8 | import 'dart:ffi'; |
| 9 | |
| 10 | import 'package:ffi/ffi.dart'; |
| 11 | import 'package:flutter_test/flutter_test.dart'; |
| Replace V with Nim 472eb49 nandithebull 19h ago | 12 | import 'package:nimflutter_ffi/nimflutter_ffi.dart' as v; |
| Add a WebAssembly backend for the example 728acaa nandithebull 19h ago | 13 | |
| 14 | typedef _MandelbrotNative = Void Function(Pointer<Uint8>, Size, Int32, Int32, |
| 15 | Double, Double, Double, Int32, Int32, Int32); |
| 16 | typedef _MandelbrotDart = void Function(Pointer<Uint8>, int, int, int, double, |
| 17 | double, double, int, int, int); |
| 18 | |
| 19 | void main() { |
| 20 | v.ensureInitialized(); |
| 21 | |
| Replace V with Nim 472eb49 nandithebull 19h ago | 22 | final mandelbrot = DynamicLibrary.open('libnimflutter.so') |
| 23 | .lookupFunction<_MandelbrotNative, _MandelbrotDart>('nf_mandelbrot'); |
| Add a WebAssembly backend for the example 728acaa nandithebull 19h ago | 24 | |
| 25 | const w = 16; |
| 26 | const h = 16; |
| 27 | const bytes = w * h * 4; |
| 28 | // A canary region past the band. Nothing below may touch it. |
| 29 | const canary = 1024; |
| 30 | const canaryByte = 0xAB; |
| 31 | |
| 32 | late Pointer<Uint8> buf; |
| 33 | |
| 34 | setUp(() { |
| 35 | buf = calloc<Uint8>(bytes + canary); |
| 36 | for (var i = bytes; i < bytes + canary; i++) { |
| 37 | buf[i] = canaryByte; |
| 38 | } |
| 39 | }); |
| 40 | |
| 41 | tearDown(() => calloc.free(buf)); |
| 42 | |
| 43 | bool canaryIntact() { |
| 44 | for (var i = bytes; i < bytes + canary; i++) { |
| 45 | if (buf[i] != canaryByte) return false; |
| 46 | } |
| 47 | return true; |
| 48 | } |
| 49 | |
| Replace V with Nim 472eb49 nandithebull 19h ago | 50 | /// True if Nim wrote anything into the band at all. |
| Add a WebAssembly backend for the example 728acaa nandithebull 19h ago | 51 | bool bandTouched() { |
| 52 | for (var i = 0; i < bytes; i++) { |
| 53 | if (buf[i] != 0) return true; |
| 54 | } |
| 55 | return false; |
| 56 | } |
| 57 | |
| 58 | void expectNoOp(String why) { |
| Replace V with Nim 472eb49 nandithebull 19h ago | 59 | expect(bandTouched(), isFalse, reason: '$why: Nim wrote into the band'); |
| 60 | expect(canaryIntact(), isTrue, reason: '$why: Nim wrote past the band'); |
| Add a WebAssembly backend for the example 728acaa nandithebull 19h ago | 61 | } |
| 62 | |
| 63 | test('a correctly sized call fills the whole band', () { |
| 64 | mandelbrot(buf, bytes, w, h, -0.5, 0.0, 3.0, 50, 0, h); |
| 65 | for (var i = 3; i < bytes; i += 4) { |
| 66 | expect(buf[i], 255, reason: 'alpha at pixel ${i ~/ 4}'); |
| 67 | } |
| 68 | expect(canaryIntact(), isTrue); |
| 69 | }); |
| 70 | |
| 71 | test('a buf_len one byte short is refused outright', () { |
| 72 | // The tempting bug is to fill what fits and overrun by one; this must |
| 73 | // write nothing at all. |
| 74 | mandelbrot(buf, bytes - 1, w, h, -0.5, 0.0, 3.0, 50, 0, h); |
| 75 | expectNoOp('buf_len short by one'); |
| 76 | }); |
| 77 | |
| 78 | test('a buf_len sized for a single row is refused', () { |
| 79 | mandelbrot(buf, w * 4, w, h, -0.5, 0.0, 3.0, 50, 0, h); |
| 80 | expectNoOp('buf_len sized for one row'); |
| 81 | }); |
| 82 | |
| 83 | test('a zero buf_len is refused', () { |
| 84 | mandelbrot(buf, 0, w, h, -0.5, 0.0, 3.0, 50, 0, h); |
| 85 | expectNoOp('buf_len zero'); |
| 86 | }); |
| 87 | |
| 88 | test('a null buffer is a no-op, not a crash', () { |
| 89 | mandelbrot(nullptr, bytes, w, h, -0.5, 0.0, 3.0, 50, 0, h); |
| 90 | expectNoOp('null buf'); |
| 91 | }); |
| 92 | |
| 93 | test('an inverted band is refused', () { |
| 94 | mandelbrot(buf, bytes, w, h, -0.5, 0.0, 3.0, 50, 10, 2); |
| 95 | expectNoOp('y1 < y0'); |
| 96 | }); |
| 97 | |
| 98 | test('geometry whose byte count overflows int32 is refused', () { |
| 99 | // (y1 - y0) * w * 4 = 2 * 2^30 * 4 wraps to 0 in 32-bit arithmetic, which |
| 100 | // is the case a length check alone would happily accept. |
| 101 | mandelbrot(buf, bytes, 1 << 30, h, -0.5, 0.0, 3.0, 50, 0, 2); |
| 102 | expectNoOp('w = 1 << 30'); |
| 103 | |
| 104 | mandelbrot(buf, bytes, w, h, -0.5, 0.0, 3.0, 50, 0, 0x7FFFFFFF); |
| 105 | expectNoOp('y1 = INT32_MAX'); |
| 106 | }); |
| 107 | } |