Dev tools and testing
The library comes with a handful of tools and tricks to ease development. This page goes into different aspects of the contributing experience.
Local development
The script check.sh
in the project root can be used to mimic a minimal version of CI locally.
It's useful to run this before you commit, push or create a pull request:
./check.sh
At the time of writing, this will run formatting, clippy, unit tests and integration tests. More checks may be added in the future.
Run ./check.sh --help
to see all available options.
If you like, you can set this as a pre-commit hook in your local clone of the repository:
ln -sf check.sh .git/hooks/pre-commit
API Docs
Besides published docs, API documentation can also be generated locally using ./check.sh doc
.
Use dok
instead of doc
to open the page in the browser.
Unit tests
Because most of gdext interacts with the Godot engine, which is not available from the test executable, unit tests
(using cargo test
and the #[test]
attribute) are pretty limited in scope. They are primarily used for Rust-only logic.
Unit tests also include doctests, which are Rust code snippets embedded in the documentation.
As additional flags might be needed, the preferred way to run unit tests is through the check.sh
script:
./check.sh test
Integration tests
The itest
directory contains a suite of integration tests. It is split into two directories:
rust
, containing the Rust code for the GDExtension library, and godot
with the Godot project and GDScript tests.
Similar to #[test]
, the function annotated by #[itest]
contains one integration test. There are multiple syntax variations:
#![allow(unused)] fn main() { // Use a Godot API and verify the results using assertions. #[itest] fn variant_nil() { let variant = Variant::nil(); assert!(variant.is_nil()); } // TestContext parameter gives access to a node in the scene tree. #[itest] fn do_sth_with_the_tree(ctx: &TestContext) { let tree: Gd<Node> = ctx.scene_tree.share(); // If you don't need the scene, you can also construct free-standing nodes: let node: Gd<Node3D> = Node3D::new_alloc(); // ... node.free(); // don't forget to free everything created by new_alloc(). } // Skip a test that's not yet ready. #[itest(skip)] fn not_executed() { // ... } // Focus on a one or a few tests. // As soon as there is at least one #[itest(focus)], only focused tests are run. #[itest(focus)] fn i_need_to_debug_this() { // ... } }
You can run the integration tests like this:
./check.sh itest
Just like when compiling the crate, the GODOT4_BIN
environment variable can be used to supply the path and filename of your Godot executable.
Otherwise, a binary named godot4
in your PATH is used.
Formatting
rustfmt
is used to format code. check.sh
only warns about formatting issues, but does not fix them. To do that, run:
cargo fmt
Clippy
clippy
is used for additional lint warnings not implemented in rustc
. This, too, is best run through check.sh
:
./check.sh clippy
Continuous Integration
If you want to have the full CI experience, you can experiment as much as you like on your own gdext fork, before submitting a pull request.
For this, navigate to the file .github/workflows/full-ci.yml
and change the following lines:
on:
push:
branches:
- staging
- trying
to:
on:
push:
This runs the entire CI pipeline to run on every push. You can then see the results in the Actions tab in your repository.
Don't forget to undo this before opening a PR! You may want to keep it in a separate commit named "UNDO" or similar.
Build configurations
real
type
Certain types in Godot use either a single or double-precision float internally, such as Vector2
.
When working with these types, we use the real
type instead of choosing either f32
or f64
.
As a result, our code is portable between Godot binaries compiled with precision=single
and precision=double
.
To run the testing suite with double-precision
enabled you may add --double
to a check.sh
invocation:
./check.sh --double