Testing Library functions in a flutter package with Native Functions using Dart::ffi

Issue

I am writing a Dart library to access a C shared object using Flutter.

So far, the example app I created is working (somewhat) as expected, but i want to include Unit testing too keep the library somewhat stable.

Details:

  • Shared Objects are in APP/android/src/main/jniLibs/{abi}/liboqs.so
  • The example App in example/lib/main.dart and works as expected
  • Library is in APP/lib/liboqs_flutter.dart

The library uses

  static final ffi.DynamicLibrary _liboqs = Platform.isAndroid
      ? ffi.DynamicLibrary.open("liboqs.so")
      : ffi.DynamicLibrary.process();

To set up the library.

But when I attempt to access this library using flutter_test (e.g. to run the init function LiboqsFlutter.init() which runs a static final C native function OQS_init, I get:

Failed to load "/{HOME DIRECTOR}/liboqs_flutter/test/liboqs_flutter_test.dart": Invalid argument(s): Failed to lookup symbol (dlsym(RTLD_DEFAULT, OQS_init): symbol not found)

I understand that flutter test does not have access to the ‘main’ assets, but how can I force through that, so I can properly test my code?

This issue offers two solutions:

but I do not fully understand exactly how to make these work for me. How does flutter driver help me here? Should the #2 code be added to the test or the original library and called in the test? If I already have prebuilt shared objects in main/jniLibs folder, do I really need to rebuild the project when I test it? That seems like a lot of wasted time just to test the app.

Solution

Testing in Android studio can happen in one of two ways. 1) on the phone or emulator environment or 2) on the desktop. I was building a Flutter package so I had an example that tested on the phone, and a Dart library which was tested on my development environment (desktop).

The configuration I was looking for was on the desktop. Therefore, I needed to compile the shared object (.dylib) for my (Mac) environment, include that in a build/test folder, and call it like so:

static ffi.DynamicLibrary _open() {
    if (Platform.environment.containsKey('FLUTTER_TEST')) {
      return ffi.DynamicLibrary.open('build/test/{clibrary}.dylib');
    } else {
      return Platform.isAndroid
          ? ffi.DynamicLibrary.open("liboqs.so")
          : ffi.DynamicLibrary.process();
    }
  }

For completeness it will make sense to detect the OS first and then provide the appropriate shared object file.

This will ensure that you have the shared object available for your unit tests.

The flutter drive option uses integration testing for the same purpose. I think adding a bit of hacky code is a better option than adding another dependency just for testing.

Answered By – Ryan Deschamps

Answer Checked By – Katrina (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *