Testing strategy

Unit tests

Normally one writes Rust unit tests with functions marked with the #[test] attribute. This makes cargo test create little programs with the test functions.

However, gnome-class is not a normal library. It defines a procedural macro that runs at compilation time of the user's program. We need to create tests that are run at compilation time.

To do this, we have a second procedural macro called testme!(). It gets called from tests/lib-unit-tests.rs as a normal procedural macro. The macro implementation, in src/lib.rs, calls functions from the various modules in gnome-class. These functions work like normal unit tests.

Adding a new unit test

Create a test function in one of the files under src/, and make sure that it ultimately gets called by testme() in src/lib.rs. Your test will run as part of the tests/lib-unit-tests program. Unfortunately we cannot run individual unit tests with this scheme; all the tests must be run in a single shot.

Integration tests

Our integration tests consist of full invocations of the gobject_gen! macro; they live in the tests/ directory.

Things to test for:

  • Can the resulting GObjects be instantiated?

  • Do internal fields get dropped when an object is finalized?

  • Can one call methods? Do they come with the correct arguments and argument types?

  • Can one override virtual methods?

  • Can signals be connected and emitted? Do they have the correct arguments?

  • Can properties be read/written? Do they have the correct argument types?

  • Can we declare interfaces? Can we implement classes with those interfaces?

  • Can we inherit from an existing class? Can we implement an existing interface?

  • Do class/interface structs have the correct size?

  • Does the generated C-callable API work?

Mixed tests

Mixed tests are meant to test the integration between rust objects and c-code. It is a subset of the integration tests and consist of three part:

  • The rust code (defined in tests/testname.rs)
  • The c code (defined in mixed_tests/testname.c)
  • Glue crate (mixed_tests/Cargo.toml with crate-name gobject_gen_test)
  • generated code by gobject_gen (mixed_tests/rustgen/testname.rs).

Rust code

The rust code is the place where the unit-test begins. It contains rust-defined gobjects and ways to test it. Furthermore, it can contains call to c-code (not related to gobject). Those methods are responsible to execute the c-parts of the test.

C-code

This code is responsible for running the c-side of the test. It can check if it can create a type defined in the rust-part, it can try if it can use a type defined in rust or it can try to subclass a type implemented in rust. It can also check if the generated header-file fulfills its needs.

Glue-crate

The crate gobject-gen-test, defined in mixed_tests is used to contain the c-functions. It is build all the c-code files in build.rs. The compiled crate exposed the entry-points of the c-code. It is linked only the tests-crate and not to the main crates. That keeps the test-c-code out of the final shared object.

Generated code

To use a rust-defined gobject in c, its needs a basic definition defined in a header file. That definition will only be available after the procedural macro is run, which is too late for our case. To these files pre-exists in mixed-tests/rustgen. The rust-side of the unittest can compare the pre-existing header file with the header file it would generate, and fail the test if those files do not match.

Compatibility

The mixed tests current depends on pkg-config, so I don't think it works on Windows.