Skip to content

Commit 6b37707

Browse files
authored
docs(examples): add library-with-tests example exercising cabin test (#1353)
1 parent 5b8376d commit 6b37707

8 files changed

Lines changed: 183 additions & 0 deletions

File tree

crates/cabin/tests/cabin_examples.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,34 @@ fn zlib_usage_builds_and_runs() {
273273
"zlib-usage run: stdout = {stdout}"
274274
);
275275
}
276+
277+
#[test]
278+
fn library_with_tests_runs_tests() {
279+
if !build_tools_available() {
280+
eprintln!("test skipped: requires ninja + a C++ compiler");
281+
return;
282+
}
283+
let dir = copy_example("library-with-tests");
284+
// `cabin test` builds every `type = "test"` target and runs each,
285+
// so this single command exercises the whole example.
286+
let output = cabin()
287+
.args(["test", "--manifest-path"])
288+
.arg(dir.path().join("cabin.toml"))
289+
.arg("--build-dir")
290+
.arg(dir.path().join("build"))
291+
.assert()
292+
.success()
293+
.get_output()
294+
.clone();
295+
let stdout = String::from_utf8(output.stdout).expect("stdout is utf-8");
296+
for expected in [
297+
"test library-with-tests:calc_test ... ok",
298+
"test library-with-tests:parity_test ... ok",
299+
"test result: ok. 2 passed; 0 failed (of 2)",
300+
] {
301+
assert!(
302+
stdout.contains(expected),
303+
"library-with-tests test: missing `{expected}`; stdout = {stdout}"
304+
);
305+
}
306+
}

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Each example has its own `cabin.toml`, sources, and `README.md`.
2323
| [`hello-c/`](hello-c) | Smallest useful C project: one `executable` target with a `.c` source. |
2424
| [`hello-cpp/`](hello-cpp) | Smallest useful C++ project: one `executable` target with a `.cc` source. |
2525
| [`library-and-app/`](library-and-app) | A library target consumed by an executable target in the same package, with `include_dirs` propagation. |
26+
| [`library-with-tests/`](library-with-tests) | A library plus two `test` targets, run with `cabin test`. The example to read for unit testing. |
2627
| [`workspace-basic/`](workspace-basic) | A virtual workspace root with two members (`util` library, `cli` executable depending on `util` via a path dependency). |
2728
| [`zlib-usage/`](zlib-usage) | Consuming the curated zlib foundation port from [`crates/cabin-port/ports/zlib/`](../crates/cabin-port/ports/zlib). |
2829

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# library-with-tests
2+
3+
A single Cabin package with a `library` target and two `test` targets
4+
that exercise it. This is the example to read for `cabin test`.
5+
6+
A Cabin `test` target is an ordinary executable: it **passes when its
7+
`main()` returns `0`** and fails otherwise. There is no framework,
8+
macro, or attribute to learn. `cabin test` builds every `type = "test"`
9+
target, runs each one, and reports a per-target `... ok` / `... FAILED`
10+
line plus a summary. Tests run in a deterministic order — by package
11+
name, then target name — so `calc_test` always runs before
12+
`parity_test`.
13+
14+
Both tests depend on the `calc` library through `deps = ["calc"]` and
15+
include its public header through `calc`'s `include_dirs = ["include"]`.
16+
17+
## Run the tests
18+
19+
```sh
20+
cd examples/library-with-tests
21+
cabin test
22+
```
23+
24+
Expected output (the `Compiling …` build line goes to stderr and is
25+
omitted here):
26+
27+
```
28+
running 2 tests
29+
running test library-with-tests:calc_test
30+
running test library-with-tests:parity_test
31+
test library-with-tests:calc_test ... ok
32+
test library-with-tests:parity_test ... ok
33+
test result: ok. 2 passed; 0 failed (of 2)
34+
```
35+
36+
A passing test is silent on stdout. The `check(...)` helper in
37+
`tests/*.cc` only writes (to stderr) when an assertion fails, which
38+
also makes that target exit non-zero so `cabin test` reports it as
39+
`FAILED (exit N)` and the command exits non-zero.
40+
41+
This package has no `executable` target, so `cabin run` does not apply
42+
here. A plain `cabin build` compiles only the `calc` library; the
43+
`test` targets are dev-only, so `cabin test` is what builds *and* runs
44+
the two test binaries.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "library-with-tests"
3+
version = "0.1.0"
4+
5+
[target.calc]
6+
type = "library"
7+
sources = ["src/calc.cc"]
8+
include_dirs = ["include"]
9+
10+
[target.calc_test]
11+
type = "test"
12+
sources = ["tests/calc_test.cc"]
13+
deps = ["calc"]
14+
15+
[target.parity_test]
16+
type = "test"
17+
sources = ["tests/parity_test.cc"]
18+
deps = ["calc"]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#pragma once
2+
3+
namespace calc {
4+
5+
// Sum of two integers.
6+
int add(int a, int b);
7+
8+
// Product of the integers 1..=n for n >= 0. factorial(0) == 1.
9+
long factorial(int n);
10+
11+
// True when n is divisible by two.
12+
bool is_even(int n);
13+
14+
} // namespace calc
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include "calc/calc.hpp"
2+
3+
namespace calc {
4+
5+
int add(int a, int b) {
6+
return a + b;
7+
}
8+
9+
long factorial(int n) {
10+
long result = 1;
11+
for (int i = 2; i <= n; ++i) {
12+
result *= i;
13+
}
14+
return result;
15+
}
16+
17+
bool is_even(int n) {
18+
return n % 2 == 0;
19+
}
20+
21+
} // namespace calc
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include "calc/calc.hpp"
2+
3+
#include <cstdio>
4+
5+
// A Cabin `test` target is just an executable: it passes when `main`
6+
// returns 0 and fails otherwise. There is no framework to learn. This
7+
// tiny helper records failures and stays silent on success, so a
8+
// passing run produces no stray output.
9+
namespace {
10+
11+
int failures = 0;
12+
13+
void check(bool condition, const char* what) {
14+
if (!condition) {
15+
std::fprintf(stderr, "FAILED check: %s\n", what);
16+
++failures;
17+
}
18+
}
19+
20+
} // namespace
21+
22+
int main() {
23+
check(calc::add(2, 3) == 5, "add(2, 3) == 5");
24+
check(calc::add(-4, 4) == 0, "add(-4, 4) == 0");
25+
check(calc::factorial(0) == 1, "factorial(0) == 1");
26+
check(calc::factorial(5) == 120, "factorial(5) == 120");
27+
return failures;
28+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include "calc/calc.hpp"
2+
3+
#include <cstdio>
4+
5+
// Second test target for the same library. `cabin test` discovers and
6+
// runs both, in a deterministic order (by package, then target name),
7+
// so this one always runs after `calc_test`.
8+
namespace {
9+
10+
int failures = 0;
11+
12+
void check(bool condition, const char* what) {
13+
if (!condition) {
14+
std::fprintf(stderr, "FAILED check: %s\n", what);
15+
++failures;
16+
}
17+
}
18+
19+
} // namespace
20+
21+
int main() {
22+
check(calc::is_even(0), "is_even(0)");
23+
check(calc::is_even(4), "is_even(4)");
24+
check(!calc::is_even(7), "!is_even(7)");
25+
return failures;
26+
}

0 commit comments

Comments
 (0)