用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/genlayerlabs/genvm-manager --skill rust-test-style命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
| name | rust-test-style |
| description | Always read before writing, adding, or modifying Rust tests — where to put and how to run them. |
Standard library testing only. Do not add rstest, proptest, insta, or
pretty_assertions — they are not used anywhere in the repo. Plain #[test],
#[cfg(test)] mod tests, and std assertions are the whole toolkit. tokio is
available with the macros feature, so use #[tokio::test] when a test must be
async (rare; no async tests exist today).
To run these tests, see the /test skill (--filter-tag rust).
Decision rule — default to a separate file. If the item under test is
reachable from the crate's public API (a pub fn/pub type, even via a
re-export), its test goes in its own tests/<concern>.rs file. Use an inline
#[cfg(test)] mod tests only when the test must reach a private item that
cannot be exercised through the public API. A public function tested inline is a
review finding — move it to tests/.
Two locations, both in use:
tests/ dir.
This is the default for anything testable through the public API. Each
tests/*.rs file is its own compilation unit (a separate crate) and gets its
own runner case.
executor/crates/calldata/tests/derive_decode.rs,
executor/crates/calldata/tests/address_checksum.rs,
executor/crates/common/tests/expr.rs,
modules/implementation/tests/test_rat.rs[dev-dependencies] — not the library's regular [dependencies]. If a
test needs a util the crate already depends on (e.g. hex), add it to
[dev-dependencies] too.#[cfg(test)] mod tests { ... } at the bottom of a
src/*.rs file, reserved for testing private items. e.g.
executor/crates/calldata/src/lib.rs, executor/crates/common/src/logger/mod.rs.#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nested_value_in_struct() { /* ... */ }
}
There is no fixture framework. Factor repeated setup into small free functions
at the top of the test file, then keep each #[test] short. This is the
dominant pattern.
// executor/crates/common/tests/expr.rs:5
fn eval(input: &str) -> Value {
Expr::parse(input).unwrap().evaluate().unwrap()
}
fn assert_rational(val: Value, n: i64, d: i64) {
let r = val.into_rational().unwrap();
assert_eq!(r, BigRational::new(BigInt::from(n), BigInt::from(d)));
}
// modules/implementation/tests/test_rat.rs — construct the system under test
fn make_vm() -> Lua {
let vm = Lua::new();
rat::register_rat_global(&vm).unwrap();
vm
}
fn eval<T: mlua::FromLua>(vm: &Lua, code: &str) -> T {
vm.load(code).eval::<T>().unwrap()
}
Define throwaway #[derive(...)] types as fixtures right in the test file:
// executor/crates/calldata/tests/derive_decode_excess_fields.rs:11
#[derive(Debug, PartialEq, Decode)]
struct Named { x: i32, y: String }
std only: assert!, assert_eq!, assert_ne!. When asserting a boolean
condition, add a format-string message that prints the actual value so a
failure is debuggable:
let msg = err.to_string();
assert!(msg.contains("unknown field"), "unexpected error: {msg}");
assert!(msg.contains("`z`"), "should mention field name: {msg}");
For value equality use assert_eq! against a fully-constructed expected value
(derive Debug, PartialEq on the type).
Use a helper that returns Result, call .unwrap_err(), and assert on
substrings of the Display message — don't match on error enum internals:
// executor/crates/calldata/tests/derive_decode_excess_fields.rs:5
fn try_decode_from_value<T: codec::Decode>(val: Value) -> Result<T, codec::DecodeError> {
T::decode(codec::ValueDeserializer(val))
}
#[test]
fn tuple_struct_rejects_too_many_elements() {
let err = try_decode_from_value::<Tuple>(/* 3 elems */).unwrap_err();
assert!(err.to_string().contains("expected 2"), "unexpected error: {err}");
}
The happy path is liberal with .unwrap() — a panic is the test failure.
For codecs, encode → decode → compare in one helper:
// executor/crates/calldata/tests/derive_decode.rs:5
fn roundtrip_via_value<T>(val: &T) -> Value
where T: for<'a> codec::Encode<&'a mut Vec<u8>, Error = std::convert::Infallible> {
let mut buf = Vec::new();
codec::Encode::encode(val, &mut Encoder::new(&mut buf)).unwrap();
genlayer_calldata::decode(&buf).unwrap()
}
snake_case that name the scenario, e.g.
named_struct_rejects_unknown_field, string_literals_and_escapes.test_. The #[test] attribute already says it is a
test; the prefix is noise. Drop it from new tests and from any you touch.// ── Tuple struct: wrong sequence length ─────────────────────────────
#[test] functions.genvm-tool test discovers Rust testsDiscovery lives in tests/plugins/genvm_tool_plugins/cargo.py. For each
registered Rust crate root it creates cases via plain cargo test:
| Source | Command | Tags |
|---|---|---|
each tests/*.rs file | cargo test --test <file> | rust, unit |
crate has src/lib.rs (inline #[cfg(test)]) | cargo test --lib | rust, unit |
each binary (src/main.rs / [[bin]]) | cargo test --bin <name> | rust, unit |
each [[example]] | cargo check --example <name> (compile-only) | rust, example |
Consequences when adding tests:
tests/ is auto-discovered — no registration needed, as long
as the crate root itself is already scanned.#[cfg(test)] tests ride along on the existing --lib case;
nothing to register..ya-test-config.json
(e.g. executor/.ya-test-config.json, executor/crates/calldata/.ya-test-config.json).
That file controls per-crate cargo_test_flags, keep_env, and a skip map
(skip examples/specific cases) — add a new crate's config there if introducing
a new root.rust preset is (rust | integration) & !bench & !fuzz
(tests/presets/rust.txt).If a function's correctness is really about holding over a space of inputs
(parsers, codecs, encode/decode roundtrips, anything taking arbitrary bytes or
structured input), write a fuzz target rather than a handful of hand-picked
#[test] cases. A few cherry-picked examples give false confidence; a fuzzer
explores the space. Don't settle for an under-tested #[test] when the property
is fuzz-shaped — reach for fuzz, or do both (fuzz for coverage, a couple of
#[test]s pinning known edge cases / regressions).
Fuzz targets live under a crate's fuzz/ (afl, behind the arbitrary feature,
tagged fuzz, built with cargo_afl_build_flags; see cargo_fuzz in
tests/plugins/genvm_tool_plugins/cargo.py). They are a distinct case type —
don't mix a fuzz harness into tests/ or mod tests, and they're excluded from
the rust preset (!fuzz).