Skip to content

Commit 086f3ed

Browse files
committed
ci: run unit tests for every crate, not just the root package
`default-members = ["."]` means a bare `cargo test` covers only the root package. CI ran 30 of 321 unit tests, so every test in ember-core and in the platform backend was green by omission, including the thin_ls and `zfs list` parsing tests this branch added and the spec lists as its no-root coverage. `--workspace` is not the fix. It selects the other platform's backend too, and that backend does not build here: ember-macos needs `clonefile` on Linux, and on macOS ember-linux's image tests want `mkfs.ext4`. Naming the packages per platform is what actually works, matching how `build` already branches on UNAME. `check` and `clippy` get the same selection, so test-module code is compiled and linted too. That immediately caught an `items_after_test_module` failure in this branch's own accounting tests, now moved to the end of the file. `build` and `release` stay as they are: they build the root package, which pulls the platform crate and ember-core in as dependencies, so naming them would compile nothing new. CI calls `make test` rather than `cargo test`, so the lint, check, and test entry points stay consistent.
1 parent 8f88eac commit 086f3ed

3 files changed

Lines changed: 103 additions & 89 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ jobs:
3232
- name: make clippy
3333
run: make clippy
3434

35-
- name: cargo test
36-
run: cargo test
35+
- name: make test
36+
run: make test
3737

3838
- name: cargo fmt -- --check
3939
run: cargo fmt -- --check

Makefile

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@
44

55
UNAME := $(shell uname -s)
66

7+
# Which workspace members to check, lint, and test.
8+
#
9+
# `default-members = ["."]` in Cargo.toml means a bare `cargo test` only
10+
# covers the root package, so the unit tests in ember-core and the
11+
# platform crate never run. `--workspace` is not the fix: it selects the
12+
# other platform's backend too, and that backend does not compile here
13+
# (ember-macos needs `clonefile`, ember-linux's image tests need
14+
# `mkfs.ext4`). Naming the packages is what actually works.
15+
ifeq ($(UNAME),Darwin)
16+
PACKAGES := -p ember -p ember-core -p ember-macos
17+
else
18+
PACKAGES := -p ember -p ember-core -p ember-linux
19+
endif
20+
721
.PHONY: build release clean fmt check clippy test udeps
822

923
build:
@@ -32,13 +46,13 @@ fmt:
3246
cargo fmt
3347

3448
check:
35-
cargo check
49+
cargo check --all-targets $(PACKAGES)
3650

3751
clippy:
38-
cargo clippy --all-targets -- -D warnings
52+
cargo clippy --all-targets $(PACKAGES) -- -D warnings
3953

4054
test:
41-
cargo test
55+
cargo test $(PACKAGES)
4256

4357
udeps:
4458
cargo machete

crates/ember-core/src/backend.rs

Lines changed: 84 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -162,90 +162,6 @@ pub struct StorageUsage {
162162
pub images: BTreeMap<String, VolumeUsage>,
163163
}
164164

165-
#[cfg(test)]
166-
mod usage_tests {
167-
use super::*;
168-
169-
fn volume(exclusive: u64, referenced: Option<u64>, logical: Option<u64>) -> VolumeUsage {
170-
VolumeUsage {
171-
provisioned: 1024,
172-
exclusive,
173-
referenced,
174-
logical,
175-
}
176-
}
177-
178-
#[test]
179-
fn shared_is_the_gap_between_referenced_and_exclusive() {
180-
assert_eq!(volume(80, Some(100), None).shared(), Some(20));
181-
assert_eq!(volume(100, Some(100), None).shared(), Some(0));
182-
}
183-
184-
/// Backends that cannot separate shared from exclusive report
185-
/// nothing rather than claiming zero sharing.
186-
#[test]
187-
fn shared_is_unknown_without_referenced() {
188-
assert_eq!(volume(80, None, None).shared(), None);
189-
}
190-
191-
/// `exclusive` is contractually within `referenced`, but the
192-
/// saturating subtraction keeps a backend bug from producing a
193-
/// wrapped, astronomically large shared figure.
194-
#[test]
195-
fn shared_saturates_instead_of_wrapping() {
196-
assert_eq!(volume(120, Some(100), None).shared(), Some(0));
197-
}
198-
199-
#[test]
200-
fn compression_ratio_divides_logical_by_referenced() {
201-
let r = volume(80, Some(100), Some(200)).compression_ratio();
202-
assert_eq!(r, Some(2.0));
203-
}
204-
205-
/// An untouched volume would divide by zero. `None` renders as `-`
206-
/// where an infinity would render as `inf`.
207-
#[test]
208-
fn compression_ratio_guards_empty_volume() {
209-
assert_eq!(volume(0, Some(0), Some(0)).compression_ratio(), None);
210-
assert_eq!(volume(0, None, Some(200)).compression_ratio(), None);
211-
assert_eq!(volume(0, Some(100), None).compression_ratio(), None);
212-
}
213-
214-
fn pool(allocated: u64, reserved: u64, logical: Option<u64>) -> PoolUsage {
215-
PoolUsage {
216-
capacity: 1000,
217-
allocated,
218-
reserved,
219-
logical,
220-
metadata: None,
221-
}
222-
}
223-
224-
#[test]
225-
fn free_is_capacity_minus_allocated() {
226-
assert_eq!(pool(400, 0, None).free(), 600);
227-
// A pool reporting more allocated than capacity must not wrap.
228-
assert_eq!(pool(1200, 0, None).free(), 0);
229-
}
230-
231-
/// Empty reservation is charged to the pool but has no logical
232-
/// counterpart, so leaving it in the denominator understates
233-
/// compression.
234-
#[test]
235-
fn pool_ratio_excludes_reservation() {
236-
let p = pool(300, 100, Some(400));
237-
assert_eq!(p.occupied(), 200);
238-
assert_eq!(p.compression_ratio(), Some(2.0));
239-
}
240-
241-
#[test]
242-
fn pool_ratio_guards_fully_reserved_pool() {
243-
assert_eq!(pool(100, 100, Some(0)).compression_ratio(), None);
244-
assert_eq!(pool(0, 0, Some(0)).compression_ratio(), None);
245-
assert_eq!(pool(100, 0, None).compression_ratio(), None);
246-
}
247-
}
248-
249165
/// Configuration for storage backend initialization during `ember init`.
250166
///
251167
/// Carries the subset of init arguments that the storage backend needs.
@@ -690,3 +606,87 @@ pub trait Platform {
690606
/// callers are expected to soft-fail rather than block on this.
691607
fn host_ram_mib() -> anyhow::Result<u32>;
692608
}
609+
610+
#[cfg(test)]
611+
mod tests {
612+
use super::*;
613+
614+
fn volume(exclusive: u64, referenced: Option<u64>, logical: Option<u64>) -> VolumeUsage {
615+
VolumeUsage {
616+
provisioned: 1024,
617+
exclusive,
618+
referenced,
619+
logical,
620+
}
621+
}
622+
623+
#[test]
624+
fn shared_is_the_gap_between_referenced_and_exclusive() {
625+
assert_eq!(volume(80, Some(100), None).shared(), Some(20));
626+
assert_eq!(volume(100, Some(100), None).shared(), Some(0));
627+
}
628+
629+
/// Backends that cannot separate shared from exclusive report
630+
/// nothing rather than claiming zero sharing.
631+
#[test]
632+
fn shared_is_unknown_without_referenced() {
633+
assert_eq!(volume(80, None, None).shared(), None);
634+
}
635+
636+
/// `exclusive` is contractually within `referenced`, but the
637+
/// saturating subtraction keeps a backend bug from producing a
638+
/// wrapped, astronomically large shared figure.
639+
#[test]
640+
fn shared_saturates_instead_of_wrapping() {
641+
assert_eq!(volume(120, Some(100), None).shared(), Some(0));
642+
}
643+
644+
#[test]
645+
fn compression_ratio_divides_logical_by_referenced() {
646+
let r = volume(80, Some(100), Some(200)).compression_ratio();
647+
assert_eq!(r, Some(2.0));
648+
}
649+
650+
/// An untouched volume would divide by zero. `None` renders as `-`
651+
/// where an infinity would render as `inf`.
652+
#[test]
653+
fn compression_ratio_guards_empty_volume() {
654+
assert_eq!(volume(0, Some(0), Some(0)).compression_ratio(), None);
655+
assert_eq!(volume(0, None, Some(200)).compression_ratio(), None);
656+
assert_eq!(volume(0, Some(100), None).compression_ratio(), None);
657+
}
658+
659+
fn pool(allocated: u64, reserved: u64, logical: Option<u64>) -> PoolUsage {
660+
PoolUsage {
661+
capacity: 1000,
662+
allocated,
663+
reserved,
664+
logical,
665+
metadata: None,
666+
}
667+
}
668+
669+
#[test]
670+
fn free_is_capacity_minus_allocated() {
671+
assert_eq!(pool(400, 0, None).free(), 600);
672+
// A pool reporting more allocated than capacity must not wrap.
673+
assert_eq!(pool(1200, 0, None).free(), 0);
674+
}
675+
676+
/// Empty reservation is charged to the pool but has no logical
677+
/// counterpart, so leaving it in the denominator understates
678+
/// compression.
679+
#[test]
680+
fn pool_ratio_excludes_reservation() {
681+
let p = pool(300, 100, Some(400));
682+
assert_eq!(p.occupied(), 200);
683+
assert_eq!(p.compression_ratio(), Some(2.0));
684+
}
685+
686+
#[test]
687+
fn pool_ratio_guards_fully_reserved_pool() {
688+
assert_eq!(pool(100, 100, Some(0)).compression_ratio(), None);
689+
assert_eq!(pool(0, 0, Some(0)).compression_ratio(), None);
690+
assert_eq!(pool(100, 0, None).compression_ratio(), None);
691+
}
692+
}

0 commit comments

Comments
 (0)