agy-superpowers 5.1.4 → 5.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/agent/rules/debug-confirmation-policy.md +34 -0
- package/template/agent/rules/language-matching.md +32 -0
- package/template/agent/skills/rust-developer/SKILL.md +281 -0
- package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +231 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +132 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +95 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +125 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +121 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +187 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +165 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +177 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +163 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +146 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +125 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +177 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +184 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +182 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +199 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +175 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +185 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +203 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +158 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +195 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +191 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +198 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +169 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +189 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +113 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +169 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +116 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +128 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +179 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +145 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +115 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +155 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +107 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +118 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +157 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +158 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +139 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +174 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +159 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +164 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +99 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +104 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +94 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +78 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +76 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +123 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +129 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +86 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +118 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +92 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +65 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +101 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +187 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +181 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +95 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +105 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +65 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +97 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +119 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +153 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +137 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +150 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +123 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +113 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +175 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +148 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +155 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +139 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +186 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +189 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +226 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +146 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +159 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +137 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +188 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +131 -0
- package/template/agent/skills/systematic-debugging/SKILL.md +17 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# test-criterion-bench
|
|
2
|
+
|
|
3
|
+
> Use `criterion` for benchmarking
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Criterion provides statistically rigorous benchmarking with warmup, multiple iterations, outlier detection, and comparison between runs. It's far more reliable than simple timing with `Instant::now()`.
|
|
8
|
+
|
|
9
|
+
## Setup
|
|
10
|
+
|
|
11
|
+
```toml
|
|
12
|
+
# Cargo.toml
|
|
13
|
+
[dev-dependencies]
|
|
14
|
+
criterion = "0.5"
|
|
15
|
+
|
|
16
|
+
[[bench]]
|
|
17
|
+
name = "my_benchmark"
|
|
18
|
+
harness = false
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Basic Benchmark
|
|
22
|
+
|
|
23
|
+
```rust
|
|
24
|
+
// benches/my_benchmark.rs
|
|
25
|
+
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
26
|
+
|
|
27
|
+
fn fibonacci(n: u64) -> u64 {
|
|
28
|
+
match n {
|
|
29
|
+
0 => 0,
|
|
30
|
+
1 => 1,
|
|
31
|
+
n => fibonacci(n - 1) + fibonacci(n - 2),
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn bench_fibonacci(c: &mut Criterion) {
|
|
36
|
+
c.bench_function("fib 20", |b| {
|
|
37
|
+
b.iter(|| fibonacci(black_box(20)))
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
criterion_group!(benches, bench_fibonacci);
|
|
42
|
+
criterion_main!(benches);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## black_box is Critical
|
|
46
|
+
|
|
47
|
+
```rust
|
|
48
|
+
// BAD: Compiler may optimize away the computation
|
|
49
|
+
b.iter(|| fibonacci(20)); // Result unused, might be eliminated
|
|
50
|
+
|
|
51
|
+
// GOOD: black_box prevents optimization
|
|
52
|
+
b.iter(|| fibonacci(black_box(20)));
|
|
53
|
+
|
|
54
|
+
// Also wrap the result if needed
|
|
55
|
+
b.iter(|| black_box(fibonacci(black_box(20))));
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Comparing Implementations
|
|
59
|
+
|
|
60
|
+
```rust
|
|
61
|
+
fn bench_comparison(c: &mut Criterion) {
|
|
62
|
+
let mut group = c.benchmark_group("String concat");
|
|
63
|
+
|
|
64
|
+
let data = "hello";
|
|
65
|
+
|
|
66
|
+
group.bench_function("format!", |b| {
|
|
67
|
+
b.iter(|| format!("{}{}", black_box(data), " world"))
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
group.bench_function("push_str", |b| {
|
|
71
|
+
b.iter(|| {
|
|
72
|
+
let mut s = String::from(black_box(data));
|
|
73
|
+
s.push_str(" world");
|
|
74
|
+
s
|
|
75
|
+
})
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
group.bench_function("concat", |b| {
|
|
79
|
+
b.iter(|| [black_box(data), " world"].concat())
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
group.finish();
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Parameterized Benchmarks
|
|
87
|
+
|
|
88
|
+
```rust
|
|
89
|
+
fn bench_vec_push(c: &mut Criterion) {
|
|
90
|
+
let mut group = c.benchmark_group("Vec::push");
|
|
91
|
+
|
|
92
|
+
for size in [100, 1000, 10000].iter() {
|
|
93
|
+
group.bench_with_input(
|
|
94
|
+
BenchmarkId::from_parameter(size),
|
|
95
|
+
size,
|
|
96
|
+
|b, &size| {
|
|
97
|
+
b.iter(|| {
|
|
98
|
+
let mut v = Vec::new();
|
|
99
|
+
for i in 0..size {
|
|
100
|
+
v.push(black_box(i));
|
|
101
|
+
}
|
|
102
|
+
v
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
group.finish();
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Throughput Measurement
|
|
113
|
+
|
|
114
|
+
```rust
|
|
115
|
+
use criterion::Throughput;
|
|
116
|
+
|
|
117
|
+
fn bench_parse(c: &mut Criterion) {
|
|
118
|
+
let input = "a]ong string to parse...";
|
|
119
|
+
|
|
120
|
+
let mut group = c.benchmark_group("Parser");
|
|
121
|
+
group.throughput(Throughput::Bytes(input.len() as u64));
|
|
122
|
+
|
|
123
|
+
group.bench_function("parse", |b| {
|
|
124
|
+
b.iter(|| parse(black_box(input)))
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
group.finish();
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Running Benchmarks
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# Run all benchmarks
|
|
135
|
+
cargo bench
|
|
136
|
+
|
|
137
|
+
# Run specific benchmark
|
|
138
|
+
cargo bench -- fib
|
|
139
|
+
|
|
140
|
+
# Save baseline for comparison
|
|
141
|
+
cargo bench -- --save-baseline main
|
|
142
|
+
|
|
143
|
+
# Compare against baseline
|
|
144
|
+
cargo bench -- --baseline main
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Evidence from tokio
|
|
148
|
+
|
|
149
|
+
```rust
|
|
150
|
+
// https://github.com/tokio-rs/tokio/blob/master/benches/sync_mpsc.rs
|
|
151
|
+
use criterion::{criterion_group, criterion_main, Criterion};
|
|
152
|
+
|
|
153
|
+
fn send_data<T: Default, const SIZE: usize>(
|
|
154
|
+
g: &mut BenchmarkGroup<WallTime>,
|
|
155
|
+
prefix: &str
|
|
156
|
+
) {
|
|
157
|
+
let rt = rt();
|
|
158
|
+
g.bench_function(format!("{prefix}_{SIZE}"), |b| {
|
|
159
|
+
b.iter(|| {
|
|
160
|
+
let (tx, mut rx) = mpsc::channel::<T>(SIZE);
|
|
161
|
+
rt.block_on(tx.send(T::default())).unwrap();
|
|
162
|
+
rt.block_on(rx.recv()).unwrap();
|
|
163
|
+
})
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## See Also
|
|
169
|
+
|
|
170
|
+
- [perf-profile-first](perf-profile-first.md) - Profile before optimizing
|
|
171
|
+
- [perf-black-box-bench](perf-black-box-bench.md) - Use black_box in benchmarks
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# test-descriptive-names
|
|
2
|
+
|
|
3
|
+
> Use descriptive test names that explain what is being tested
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Test names appear in test output and serve as documentation. A good test name tells you what behavior is being verified without reading the test body. When a test fails, a descriptive name immediately tells you what broke.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
#[test]
|
|
13
|
+
fn test1() { ... }
|
|
14
|
+
|
|
15
|
+
#[test]
|
|
16
|
+
fn test_parse() { ... } // Parse what? What behavior?
|
|
17
|
+
|
|
18
|
+
#[test]
|
|
19
|
+
fn it_works() { ... }
|
|
20
|
+
|
|
21
|
+
#[test]
|
|
22
|
+
fn test_function() { ... }
|
|
23
|
+
|
|
24
|
+
// Failure output: "test test_parse ... FAILED"
|
|
25
|
+
// What failed? No idea.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Good
|
|
29
|
+
|
|
30
|
+
```rust
|
|
31
|
+
#[test]
|
|
32
|
+
fn parse_returns_error_for_empty_input() { ... }
|
|
33
|
+
|
|
34
|
+
#[test]
|
|
35
|
+
fn parse_handles_unicode_characters() { ... }
|
|
36
|
+
|
|
37
|
+
#[test]
|
|
38
|
+
fn user_creation_requires_valid_email() { ... }
|
|
39
|
+
|
|
40
|
+
#[test]
|
|
41
|
+
fn expired_token_is_rejected() { ... }
|
|
42
|
+
|
|
43
|
+
// Failure output: "test parse_returns_error_for_empty_input ... FAILED"
|
|
44
|
+
// Immediately know what broke!
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Naming Patterns
|
|
48
|
+
|
|
49
|
+
```rust
|
|
50
|
+
// Pattern: function_condition_expected_result
|
|
51
|
+
#[test]
|
|
52
|
+
fn parse_valid_json_returns_document() { ... }
|
|
53
|
+
|
|
54
|
+
#[test]
|
|
55
|
+
fn parse_invalid_json_returns_syntax_error() { ... }
|
|
56
|
+
|
|
57
|
+
// Pattern: scenario_expectation
|
|
58
|
+
#[test]
|
|
59
|
+
fn empty_cart_has_zero_total() { ... }
|
|
60
|
+
|
|
61
|
+
#[test]
|
|
62
|
+
fn adding_item_increases_cart_total() { ... }
|
|
63
|
+
|
|
64
|
+
// Pattern: when_given_then (BDD-style)
|
|
65
|
+
#[test]
|
|
66
|
+
fn when_user_not_found_then_returns_404() { ... }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Edge Cases
|
|
70
|
+
|
|
71
|
+
```rust
|
|
72
|
+
#[test]
|
|
73
|
+
fn handles_empty_string() { ... }
|
|
74
|
+
|
|
75
|
+
#[test]
|
|
76
|
+
fn handles_max_length_input() { ... }
|
|
77
|
+
|
|
78
|
+
#[test]
|
|
79
|
+
fn handles_unicode_emoji() { ... }
|
|
80
|
+
|
|
81
|
+
#[test]
|
|
82
|
+
fn handles_null_bytes() { ... }
|
|
83
|
+
|
|
84
|
+
#[test]
|
|
85
|
+
fn handles_concurrent_access() { ... }
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Error Cases
|
|
89
|
+
|
|
90
|
+
```rust
|
|
91
|
+
#[test]
|
|
92
|
+
fn rejects_negative_quantity() { ... }
|
|
93
|
+
|
|
94
|
+
#[test]
|
|
95
|
+
fn returns_error_for_invalid_email_format() { ... }
|
|
96
|
+
|
|
97
|
+
#[test]
|
|
98
|
+
fn panics_on_double_initialization() { ... }
|
|
99
|
+
|
|
100
|
+
#[test]
|
|
101
|
+
fn timeout_returns_timeout_error() { ... }
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Module Organization
|
|
105
|
+
|
|
106
|
+
```rust
|
|
107
|
+
#[cfg(test)]
|
|
108
|
+
mod tests {
|
|
109
|
+
use super::*;
|
|
110
|
+
|
|
111
|
+
mod parsing {
|
|
112
|
+
use super::*;
|
|
113
|
+
|
|
114
|
+
#[test]
|
|
115
|
+
fn accepts_valid_json() { ... }
|
|
116
|
+
|
|
117
|
+
#[test]
|
|
118
|
+
fn rejects_trailing_comma() { ... }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
mod validation {
|
|
122
|
+
use super::*;
|
|
123
|
+
|
|
124
|
+
#[test]
|
|
125
|
+
fn requires_name_field() { ... }
|
|
126
|
+
|
|
127
|
+
#[test]
|
|
128
|
+
fn email_must_contain_at_symbol() { ... }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Test output:
|
|
133
|
+
// tests::parsing::accepts_valid_json
|
|
134
|
+
// tests::parsing::rejects_trailing_comma
|
|
135
|
+
// tests::validation::requires_name_field
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## See Also
|
|
139
|
+
|
|
140
|
+
- [test-arrange-act-assert](./test-arrange-act-assert.md) - Test structure
|
|
141
|
+
- [test-cfg-test-module](./test-cfg-test-module.md) - Test module organization
|
|
142
|
+
- [doc-examples-section](./doc-examples-section.md) - Documentation tests
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# test-doctest-examples
|
|
2
|
+
|
|
3
|
+
> Keep documentation examples as executable doctests
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Doctests are examples in documentation that are automatically tested. They serve dual purposes: demonstrating usage to readers and verifying the examples compile and work. When your API changes, failing doctests catch outdated documentation.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
/// Parses a number from a string.
|
|
13
|
+
///
|
|
14
|
+
/// Example:
|
|
15
|
+
/// let n = parse("42"); // Not tested!
|
|
16
|
+
/// assert_eq!(n, 42);
|
|
17
|
+
pub fn parse(s: &str) -> i32 {
|
|
18
|
+
s.parse().unwrap()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Documentation can become outdated:
|
|
22
|
+
/// Adds two numbers.
|
|
23
|
+
///
|
|
24
|
+
/// ```
|
|
25
|
+
/// let sum = add(1, 2, 3); // Wrong number of args - not caught!
|
|
26
|
+
/// ```
|
|
27
|
+
pub fn add(a: i32, b: i32) -> i32 {
|
|
28
|
+
a + b
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Good
|
|
33
|
+
|
|
34
|
+
```rust
|
|
35
|
+
/// Parses a number from a string.
|
|
36
|
+
///
|
|
37
|
+
/// # Examples
|
|
38
|
+
///
|
|
39
|
+
/// ```
|
|
40
|
+
/// use my_crate::parse;
|
|
41
|
+
///
|
|
42
|
+
/// let n = parse("42");
|
|
43
|
+
/// assert_eq!(n, 42);
|
|
44
|
+
/// ```
|
|
45
|
+
pub fn parse(s: &str) -> i32 {
|
|
46
|
+
s.parse().unwrap()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// Adds two numbers.
|
|
50
|
+
///
|
|
51
|
+
/// # Examples
|
|
52
|
+
///
|
|
53
|
+
/// ```
|
|
54
|
+
/// use my_crate::add;
|
|
55
|
+
///
|
|
56
|
+
/// let sum = add(1, 2);
|
|
57
|
+
/// assert_eq!(sum, 3);
|
|
58
|
+
/// ```
|
|
59
|
+
pub fn add(a: i32, b: i32) -> i32 {
|
|
60
|
+
a + b
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Hiding Setup Code
|
|
65
|
+
|
|
66
|
+
```rust
|
|
67
|
+
/// Processes data from a file.
|
|
68
|
+
///
|
|
69
|
+
/// # Examples
|
|
70
|
+
///
|
|
71
|
+
/// ```
|
|
72
|
+
/// # use std::io::Write;
|
|
73
|
+
/// # let mut file = tempfile::NamedTempFile::new().unwrap();
|
|
74
|
+
/// # writeln!(file, "test data").unwrap();
|
|
75
|
+
/// # let path = file.path();
|
|
76
|
+
/// use my_crate::process_file;
|
|
77
|
+
///
|
|
78
|
+
/// let result = process_file(path)?;
|
|
79
|
+
/// assert!(!result.is_empty());
|
|
80
|
+
/// # Ok::<(), Box<dyn std::error::Error>>(())
|
|
81
|
+
/// ```
|
|
82
|
+
pub fn process_file(path: &Path) -> Result<String, Error> {
|
|
83
|
+
std::fs::read_to_string(path).map_err(Error::from)
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Showing Error Handling
|
|
88
|
+
|
|
89
|
+
```rust
|
|
90
|
+
/// Parses and validates an email address.
|
|
91
|
+
///
|
|
92
|
+
/// # Examples
|
|
93
|
+
///
|
|
94
|
+
/// ```
|
|
95
|
+
/// use my_crate::Email;
|
|
96
|
+
///
|
|
97
|
+
/// let email = Email::parse("user@example.com")?;
|
|
98
|
+
/// assert_eq!(email.domain(), "example.com");
|
|
99
|
+
/// # Ok::<(), my_crate::EmailError>(())
|
|
100
|
+
/// ```
|
|
101
|
+
///
|
|
102
|
+
/// # Errors
|
|
103
|
+
///
|
|
104
|
+
/// Returns error for invalid format:
|
|
105
|
+
///
|
|
106
|
+
/// ```
|
|
107
|
+
/// use my_crate::Email;
|
|
108
|
+
///
|
|
109
|
+
/// assert!(Email::parse("not-an-email").is_err());
|
|
110
|
+
/// ```
|
|
111
|
+
pub fn parse(s: &str) -> Result<Email, EmailError> {
|
|
112
|
+
// ...
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## no_run and ignore
|
|
117
|
+
|
|
118
|
+
```rust
|
|
119
|
+
/// Starts the server.
|
|
120
|
+
///
|
|
121
|
+
/// ```no_run
|
|
122
|
+
/// use my_crate::Server;
|
|
123
|
+
///
|
|
124
|
+
/// // This compiles but doesn't run (would block forever)
|
|
125
|
+
/// Server::new().run();
|
|
126
|
+
/// ```
|
|
127
|
+
pub fn run(&self) { ... }
|
|
128
|
+
|
|
129
|
+
/// Platform-specific example.
|
|
130
|
+
///
|
|
131
|
+
/// ```ignore
|
|
132
|
+
/// // This might not compile on all platforms
|
|
133
|
+
/// use windows_specific::Feature;
|
|
134
|
+
/// ```
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## compile_fail
|
|
138
|
+
|
|
139
|
+
```rust
|
|
140
|
+
/// This type is not Clone.
|
|
141
|
+
///
|
|
142
|
+
/// ```compile_fail
|
|
143
|
+
/// use my_crate::UniqueHandle;
|
|
144
|
+
///
|
|
145
|
+
/// let a = UniqueHandle::new();
|
|
146
|
+
/// let b = a.clone(); // Error: Clone not implemented
|
|
147
|
+
/// ```
|
|
148
|
+
pub struct UniqueHandle { ... }
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Running Doctests
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Run all tests including doctests
|
|
155
|
+
cargo test
|
|
156
|
+
|
|
157
|
+
# Run only doctests
|
|
158
|
+
cargo test --doc
|
|
159
|
+
|
|
160
|
+
# Run doctests for specific item
|
|
161
|
+
cargo test --doc my_function
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## See Also
|
|
165
|
+
|
|
166
|
+
- [doc-examples-section](./doc-examples-section.md) - Documentation structure
|
|
167
|
+
- [doc-hidden-setup](./doc-hidden-setup.md) - Hiding setup code
|
|
168
|
+
- [doc-question-mark](./doc-question-mark.md) - Error handling in examples
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# test-fixture-raii
|
|
2
|
+
|
|
3
|
+
> Use RAII pattern (Drop trait) for automatic test cleanup
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Tests often need setup and teardown—creating temp files, starting servers, setting environment variables. Using RAII (Resource Acquisition Is Initialization) with Drop ensures cleanup happens automatically, even if the test panics. This prevents test pollution and resource leaks.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
#[test]
|
|
13
|
+
fn test_with_temp_file() {
|
|
14
|
+
let path = "/tmp/test_file.txt";
|
|
15
|
+
std::fs::write(path, "test data").unwrap();
|
|
16
|
+
|
|
17
|
+
let result = process_file(path);
|
|
18
|
+
|
|
19
|
+
std::fs::remove_file(path).unwrap(); // Might not run if test panics!
|
|
20
|
+
assert!(result.is_ok());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#[test]
|
|
24
|
+
fn test_with_env_var() {
|
|
25
|
+
std::env::set_var("MY_VAR", "test_value");
|
|
26
|
+
|
|
27
|
+
let result = read_config();
|
|
28
|
+
|
|
29
|
+
std::env::remove_var("MY_VAR"); // Might not run if test panics!
|
|
30
|
+
assert!(result.is_ok());
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Good
|
|
35
|
+
|
|
36
|
+
```rust
|
|
37
|
+
use tempfile::NamedTempFile;
|
|
38
|
+
|
|
39
|
+
#[test]
|
|
40
|
+
fn test_with_temp_file() {
|
|
41
|
+
// Arrange - file deleted automatically when `file` drops
|
|
42
|
+
let file = NamedTempFile::new().unwrap();
|
|
43
|
+
std::fs::write(file.path(), "test data").unwrap();
|
|
44
|
+
|
|
45
|
+
// Act
|
|
46
|
+
let result = process_file(file.path());
|
|
47
|
+
|
|
48
|
+
// Assert - file cleaned up even if assertion panics
|
|
49
|
+
assert!(result.is_ok());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Custom RAII guard for environment variables
|
|
53
|
+
struct EnvGuard {
|
|
54
|
+
key: String,
|
|
55
|
+
original: Option<String>,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
impl EnvGuard {
|
|
59
|
+
fn set(key: &str, value: &str) -> Self {
|
|
60
|
+
let original = std::env::var(key).ok();
|
|
61
|
+
std::env::set_var(key, value);
|
|
62
|
+
EnvGuard {
|
|
63
|
+
key: key.to_string(),
|
|
64
|
+
original,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
impl Drop for EnvGuard {
|
|
70
|
+
fn drop(&mut self) {
|
|
71
|
+
match &self.original {
|
|
72
|
+
Some(v) => std::env::set_var(&self.key, v),
|
|
73
|
+
None => std::env::remove_var(&self.key),
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#[test]
|
|
79
|
+
fn test_with_env_var() {
|
|
80
|
+
let _guard = EnvGuard::set("MY_VAR", "test_value");
|
|
81
|
+
|
|
82
|
+
let result = read_config();
|
|
83
|
+
|
|
84
|
+
assert!(result.is_ok());
|
|
85
|
+
} // MY_VAR automatically restored
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Common RAII Patterns
|
|
89
|
+
|
|
90
|
+
```rust
|
|
91
|
+
// Temporary directory
|
|
92
|
+
use tempfile::TempDir;
|
|
93
|
+
|
|
94
|
+
#[test]
|
|
95
|
+
fn test_with_temp_dir() {
|
|
96
|
+
let dir = TempDir::new().unwrap();
|
|
97
|
+
let file_path = dir.path().join("test.txt");
|
|
98
|
+
std::fs::write(&file_path, "data").unwrap();
|
|
99
|
+
|
|
100
|
+
// dir and all contents deleted on drop
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Server guard
|
|
104
|
+
struct TestServer {
|
|
105
|
+
handle: std::thread::JoinHandle<()>,
|
|
106
|
+
shutdown: std::sync::mpsc::Sender<()>,
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
impl Drop for TestServer {
|
|
110
|
+
fn drop(&mut self) {
|
|
111
|
+
let _ = self.shutdown.send(());
|
|
112
|
+
// Wait for server to stop
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Database transaction rollback
|
|
117
|
+
struct TestTransaction<'a> {
|
|
118
|
+
conn: &'a mut Connection,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
impl Drop for TestTransaction<'_> {
|
|
122
|
+
fn drop(&mut self) {
|
|
123
|
+
self.conn.execute("ROLLBACK").unwrap();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## scopeguard Crate
|
|
129
|
+
|
|
130
|
+
```rust
|
|
131
|
+
use scopeguard::defer;
|
|
132
|
+
|
|
133
|
+
#[test]
|
|
134
|
+
fn test_with_defer() {
|
|
135
|
+
let path = "/tmp/test_file.txt";
|
|
136
|
+
std::fs::write(path, "data").unwrap();
|
|
137
|
+
|
|
138
|
+
defer! {
|
|
139
|
+
std::fs::remove_file(path).ok();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Test logic here
|
|
143
|
+
// File removed when scope exits
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## See Also
|
|
148
|
+
|
|
149
|
+
- [test-arrange-act-assert](./test-arrange-act-assert.md) - Test structure
|
|
150
|
+
- [test-tokio-async](./test-tokio-async.md) - Async test cleanup
|
|
151
|
+
- [test-mock-traits](./test-mock-traits.md) - Mocking with RAII
|