3

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.

Ryan Deschamps
  • 351
  • 4
  • 16

1 Answers1

3

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.

Ryan Deschamps
  • 351
  • 4
  • 16
  • 1
    I don't think it is a good practice, 'cause in customer's test project this lib would also open `build/test/` directory – Wesley Chang Apr 13 '21 at 09:56
  • Do you have an alternative? Maybe add a debug requirement as well? I mean it's pretty important to test that the native code is working as expected (especially if it updates). – Ryan Deschamps Apr 13 '21 at 17:17