Skip to content

Draft: Supply mock versions of Emitters and Models

First things first: This is currently based on the other MR I've made (!2). If only this MR should be merged, the two commits (soulsource/rust-qt-binding-generator@be1216bd, soulsource/rust-qt-binding-generator@69ed34f4) should rather be cherry-picked in a new branch.

This MR offers an easy way to generate mock versions of auto-generated Emitters, so testing of models works without the need to decouple the model's logic from the model's struct. When generate_automocks is enabled in the bindings.json file, mockall automocks are generated for each emitter. In addition, some auto-generated function signatures are duplicated with conditional compilation, so that if the test harness is being built, the mock types are used instead of the actual ones.

I've experimented with adding traits for the emitters and implementing models as generic structs that just care about the emitter's trait instead of a concrete emitter type, but in the end decided against this, after realizing that nesting objects would also create nested generics, with the number of type parameters quickly getting unmanageable.

Instead, the mocks are now directly generated from the structs' implementation blocks.

Without generate_automocks enabled the generated code should be identical to the one created by !2, and if generate_automocks is enabled, non-test code should still build/run without any modifications.

However, if generate_automocks is enabled, one can use mockall_double to automatically import the mock emitter instead of the actual one, and write tests that check if the right change-notifications are emitted at the right moment.

The motivation for this change was that now, with the setters receiving an immutable reference to self, the complexity of setter code has significantly increased. Furthermore, interior mutability bugs cannot be caught by the compiler, but rather cause runtime exceptions or deadlocks. Automated tests can help uncover such issues. For instance, one can write an automated test that fails if a Signal gets emitted while a Mutex is still locked, detecting changes that would potentially cause deadlocks.

Known issues: If the test harness is built and generate_automocks is true, the interaction with C++ is disabled (the *_new() function is not compiled). While this does not matter if tests are purely Rust, projects that have tests partially implemented in C++ and Rust cannot use the generate_automocks feature as it currently stands. As the generated code with generate_automocks==false is the same as before, this does not affect already existing code.

Draft status: I haven't actually implemented any tests yet, just made sure that everything compiles. I'll remove the Draft status as soon as I have had time to work with these changes and am reasonably confident they are OK.

Merge request reports