How to test Rust library whose public interfaces are in C?

I am working on a library that is written in Rust.

The user of this library uses C++ instead of Rust,
so the public interfaces are all extern "C" functions in Rust.

There are many C APIs in this library, over 100 APIs.

About 1000 lines of code needed to write a test application,
to make use of all of those APIs in the library.


Any suggestion how to write the test application?

  1. Use Rust language using the C APIs, but it is painful to have "unsafe" everywhere
  2. Use the corresponding Rust APIs for C API, but limits the code coverage for C API.
  3. Write the test program in C++, but I prefer pure Rust, and mixing build of Rust and C++ needs some work.
  4. Implement a safe Rust wrappper on top of C API. This ensure C API are tested, but there are too many boilterplate code.

The C API is designed in object oriented style

For example, some APIs are (simplied here):

typedef struct Version {
  int64_t handle;
} Version;

error_code_t VersionGetCString(Version version, const char** cstring);

error_code_t VersionGetCommitHash(Version version, const char** commitHash);

The corresponding Rust code is similar to

struct Version {
  cstring: &CStr,
  commitHash: &CStr,
}

// extern "C" functions omitted
1 Like

Start off by unit-testing the pure rust internals, then integration test the external C API as if your tests are normal consumers of the library.

This is generally the same approach as with rust libraries that do not expose C API.

5 Likes

Thanks. Do you know any similar open source project (on github)?

If your extern functions provide a safe API, then you can keep them safe. For example, you can write:

// foo/src/lib.rs
#[no_mangle]
pub extern "C" fn add(a: &u32, b: &u32) -> u32 {
    *a + *b
}

// foo/tests/mod.rs
#[test]
fn test_add() {
    assert_eq!(foo::add(&2, &2), 4);
}

Otherwise, I would prefer to write a proper safe Rust wrapper and write tests for it. It would allow you to test shared libraries (assuming you compile your library into one) and it will allow an easy use of the library from Rust code in future.