nandi/vflutter_ffipublic Fork 0
728acaab2594667f4998fc18812e7c1e2e4cc549
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.

Add a WebAssembly backend for the example 728acaa · on 728acaab2594667f4998fc18812e7c1e2e4cc549 · nandithebull · 18h ago
abi_guard_test.dart · 107 lines · 3.4 KBDart 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
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
// Guards the vf_mandelbrot contract at the C boundary.
//
// Everything else in the suite goes through lib/vflutter_ffi.dart, which
// always passes a correctly sized buffer. These tests deliberately bypass it
// and call the raw symbol the way a careless C caller would, because the
// point of `buf_len` is to survive exactly that. Needs the native build on
// the loader path — see widget_test.dart.
import 'dart:ffi';

import 'package:ffi/ffi.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:vflutter_ffi/vflutter_ffi.dart' as v;

typedef _MandelbrotNative = Void Function(Pointer<Uint8>, Size, Int32, Int32,
    Double, Double, Double, Int32, Int32, Int32);
typedef _MandelbrotDart = void Function(Pointer<Uint8>, int, int, int, double,
    double, double, int, int, int);

void main() {
  v.ensureInitialized();

  final mandelbrot = DynamicLibrary.open('libvflutter.so')
      .lookupFunction<_MandelbrotNative, _MandelbrotDart>('vf_mandelbrot');

  const w = 16;
  const h = 16;
  const bytes = w * h * 4;
  // A canary region past the band. Nothing below may touch it.
  const canary = 1024;
  const canaryByte = 0xAB;

  late Pointer<Uint8> buf;

  setUp(() {
    buf = calloc<Uint8>(bytes + canary);
    for (var i = bytes; i < bytes + canary; i++) {
      buf[i] = canaryByte;
    }
  });

  tearDown(() => calloc.free(buf));

  bool canaryIntact() {
    for (var i = bytes; i < bytes + canary; i++) {
      if (buf[i] != canaryByte) return false;
    }
    return true;
  }

  /// True if V wrote anything into the band at all.
  bool bandTouched() {
    for (var i = 0; i < bytes; i++) {
      if (buf[i] != 0) return true;
    }
    return false;
  }

  void expectNoOp(String why) {
    expect(bandTouched(), isFalse, reason: '$why: V wrote into the band');
    expect(canaryIntact(), isTrue, reason: '$why: V wrote past the band');
  }

  test('a correctly sized call fills the whole band', () {
    mandelbrot(buf, bytes, w, h, -0.5, 0.0, 3.0, 50, 0, h);
    for (var i = 3; i < bytes; i += 4) {
      expect(buf[i], 255, reason: 'alpha at pixel ${i ~/ 4}');
    }
    expect(canaryIntact(), isTrue);
  });

  test('a buf_len one byte short is refused outright', () {
    // The tempting bug is to fill what fits and overrun by one; this must
    // write nothing at all.
    mandelbrot(buf, bytes - 1, w, h, -0.5, 0.0, 3.0, 50, 0, h);
    expectNoOp('buf_len short by one');
  });

  test('a buf_len sized for a single row is refused', () {
    mandelbrot(buf, w * 4, w, h, -0.5, 0.0, 3.0, 50, 0, h);
    expectNoOp('buf_len sized for one row');
  });

  test('a zero buf_len is refused', () {
    mandelbrot(buf, 0, w, h, -0.5, 0.0, 3.0, 50, 0, h);
    expectNoOp('buf_len zero');
  });

  test('a null buffer is a no-op, not a crash', () {
    mandelbrot(nullptr, bytes, w, h, -0.5, 0.0, 3.0, 50, 0, h);
    expectNoOp('null buf');
  });

  test('an inverted band is refused', () {
    mandelbrot(buf, bytes, w, h, -0.5, 0.0, 3.0, 50, 10, 2);
    expectNoOp('y1 < y0');
  });

  test('geometry whose byte count overflows int32 is refused', () {
    // (y1 - y0) * w * 4 = 2 * 2^30 * 4 wraps to 0 in 32-bit arithmetic, which
    // is the case a length check alone would happily accept.
    mandelbrot(buf, bytes, 1 << 30, h, -0.5, 0.0, 3.0, 50, 0, 2);
    expectNoOp('w = 1 << 30');

    mandelbrot(buf, bytes, w, h, -0.5, 0.0, 3.0, 50, 0, 0x7FFFFFFF);
    expectNoOp('y1 = INT32_MAX');
  });
}