Testing a change
A change is done when the workspace checks pass, a project made from the checkout builds and plays with it, and the templates it can affect still do.
The workspace builds, tests and lints without the engine:
cargo build --workspace
cargo test --workspace
cargo clippy --workspace --all-targets --no-deps -- -D warnings
That is what CI runs on every pull request; run it before pushing. Where the engine-free tests are, and what each protects, is in The CLI, Code generation and The runtime.
Anything that touches the engine is tested in a UE project of your choice,
driven by this checkout’s CLI (cargo run -p rusteal --, from the repository
root). A new project that depends on this checkout:
cargo run -p rusteal -- new Probe --dir <dir> --runtime-path .
Put it in a directory of your own, such as the one that holds your Unreal projects, not in /tmp: on Linux, UBT fails at step 1 for a project under /tmp, building the exporter’s path without its leading / (“is not an absolute path”).
After changing the runtime, the macros or a game crate:
cargo run -p rusteal -- build <dir>/Probe
After changing the UE plugins in ue_plugin/, reinstall them into the project
and build it:
cargo run -p rusteal -- setup <dir>/Probe
cargo run -p rusteal -- build <dir>/Probe --all
In the editor, each build reloads the library by itself, changed classes
included (see Hot reload); a change to the UE plugins needs an
editor restart.
A project made with --runtime-path follows that checkout: the CLI acts on it
only when built from the same checkout, so drive it with cargo run -p rusteal --
from there. After pulling, setup reinstalls the plugins at the checkout’s
version.
--runtime-path
It decides where the generated project takes Rusteal from:
| without it | --runtime-path <checkout> | |
|---|---|---|
Rust/Cargo.toml says | rusteal-runtime = "=x.y.z" | { path = "<checkout>/rusteal-runtime" } |
| Crates come from | crates.io | the local checkout |
| A change in the runtime reaches the game | on the next published version | on the next build |
| The project builds on another machine | yes | no (the path is this machine’s) |
So: without it for a real game, with it while working on Rusteal — a change in
rusteal-core shows up in the project you test with on the next build,
with nothing to publish in between.
Testing in the editor, headless
What happens in the editor is tested with editor Python, in an editor with no window:
"<engine>/Engine/Binaries/Linux/UnrealEditor" "<dir>/Probe/Probe.uproject" \
-nullrhi -unattended -nosplash -nosound -NoLiveCoding \
-ExecCmds="py <dir>/test.py" -abslog="<dir>/test.log"
(UnrealEditor-Cmd.exe under Engine/Binaries/Win64/ on Windows.) A test script:
- loads a map, places what it needs in the editor world (Python cannot spawn into the Play In Editor world), and saves;
- starts Play In Editor with
LevelEditorSubsystem.editor_request_begin_play()and drives it from a Slate tick callback (unreal.register_slate_post_tick_callback), a generator that yields the number of ticks to wait between steps; - calls the Rust classes’ functions and reads their properties by their Unreal names (
actor.call_method("SnapTurn", (True,)),get_editor_property("MoveSpeed")), since editor Python wraps a Rust class as its nearest engine class; - writes
PASS/FAILlines to a results file, ends Play, and quits withunreal.SystemLibrary.quit_editor().
A C++ helper library in the test project’s own module, never in Rusteal, reaches what Python cannot: the Message Log’s entries and tokens, Blueprint graph nodes, function calls on a token’s link. The ports in Porting an engine template are validated this way.
Before a pull request
-
A game-facing API needs no
unsafeand no raw handle from its caller, takes and returns typed references and Rust types, and its error is aRustealResult; the templates’ sources stay free ofunsafe(game_developers_never_write_unsafe_code). -
The workspace checks above pass.
-
The test project builds with
rusteal build --alland plays with noLogRustealerror or warning. -
A change to the runtime, the macros, codegen or the plugin: the templates it reaches still build and play (
rusteal newof each, with--runtime-path). Codegen and plugin changes reach all of them. -
A change the game developer sees: the game developer’s handbook says so. A decision: Decisions.