3

Testing your crate C-API

 3 years ago
source link: https://dev.to/luzero/testing-your-crate-c-api-19nc
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Testing your crate C-API

Nov 22

・2 min read

I wrote previously about cargo-c.

It is a good solution if you want to use your rust crates from C: with minimal changes you get a set of libraries, header and pkg-config that downstream application won't feel any different than a normal C library.

But an itch was left. I wanted to have proper tests and make so they won't feel much different than what we have in the cargo ecosystem.

The long-term plan was to make cargo build the C code as a stand alone Unit and link everything together in the test runner. Getting it right requires lots of time and so far I hadn't had enough.

Inline-c

Recently I stumbled upon inline-c and it felt like a great shortcut.

It let you write tests by embedding C code and then have the procedural macro invoke the compiler with the parameters pass using the usual env variables: CC, CFLAGS, LDFLAGS and so on.

#[test]
fn test_api_something() {
set_var("INLINE_C_RS_CFLAGS", "-Iinclude/something/")
set_var("INLINE_C_RS_LDFLAGS", "-Llib/path -lsomething")
(assert_c! {
        #include <something.h>

        int main() {
            SomeThing *thing = NULL;
            int ret = some_init(&thing);
            if (ret != 0)
                return -1;
            ...
            some_destroy(&thing);

            return 0;
        }
    })
    .success()
}
Enter fullscreen modeExit fullscreen mode

It does work nicely but it requires to feed it the right include and library search path.

cargo ctest

cargo-c produces the header and library in known locations so it is fairly easy to set the env variables accordingly and makes the whole experience much nicer.

All you need to do is to add inline-c to your [dev-dependencies].

[dev-dependencies]
inline-c = "0.1"
Enter fullscreen modeExit fullscreen mode

Write the test without having to care about where the header and the libraries are:

#[cfg(feature = "capi")]
mod capi {
    use inline_c::assert_c;

    #[test]
    fn test_capi() {
        (assert_c! {
        #include <example_project.h>
        #include <stdio.h>

        int main() {
            ExampleProjectOddCounter *counter = example_project_oddcounter_new(4);
            if (counter) {
                printf("Unexpected success\n");
                return 1;
            }
            counter = example_project_oddcounter_new(5);
            if (!counter) {
                printf("Error creating ExampleProjectOddCounter\n");
                return 1;
            }
            example_project_oddcounter_increment(counter);
            uint32_t result = example_project_oddcounter_get_current(counter);
            example_project_oddcounter_free(counter);
            if (result == 7) {
                return 0;
            } else {
                printf("Error: unexpected result: %d\n", result);
                return 1;
            }
        }
            })
        .success();
    }
}
Enter fullscreen modeExit fullscreen mode

And then run the tests through cargo-c:

$ cargo ctest
Enter fullscreen modeExit fullscreen mode

It is available since version 0.6.17 that I just released on crates.io.

Enjoy!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK