1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! E2E integration test: real kernel build with `--extra-kconfig`
//! overriding a baked-in symbol must fail at `validate_kernel_config`
//! and skip the cache store.
//!
//! # What this test pins
//!
//! 1. The merge path (`merge_kconfig_fragments`) gives last-wins
//! precedence to the user fragment, so `# CONFIG_BPF_SYSCALL is
//! not set` defeats the baked-in `CONFIG_BPF_SYSCALL=y`.
//! 2. `validate_kernel_config` runs AFTER the build and bails with
//! an actionable error naming `CONFIG_BPF_SYSCALL`. The error is
//! wrapped with a hint pointing at `--extra-kconfig` because the
//! extras flag was passed.
//! 3. The cache store is skipped on this failure path: the build
//! pipeline returns `Err` before reaching `cache.store(...)`, so
//! no entry lands under the cache key.
//!
//! # Why `#[ignore]`
//!
//! A real kernel build takes 2–10 minutes on a typical workstation
//! and requires `make`, `gcc`, `pahole`, `flex`, `bison`, plus a
//! linux source tree at `../linux`. Running this test in the
//! standard nextest pass would balloon CI time and gate every PR
//! on the build toolchain. The test is therefore
//! `#[ignore = "..."]` and only invoked via `cargo ktstr test`'s
//! gauntlet level (or explicit `cargo nextest run --run-ignored
//! all extra_kconfig_e2e_validate_rejects_disabled_bpf_syscall`).
//!
//! # Prerequisites
//!
//! - `../linux` exists and is a configured kernel source tree
//! (any version that ktstr's baked-in `ktstr.kconfig` targets).
//! - Build toolchain installed: `make`, `gcc`, `binutils`,
//! `pahole >= 1.16` (`dwarves` package), `flex`, `bison`,
//! `libelf-dev`, `libssl-dev`.
//! - `KTSTR_CACHE_DIR` (or default `~/.cache/ktstr/kernels/...`)
//! is writable. The test creates a temp `.kconfig` fragment under
//! `tempfile::tempdir()` and removes it on teardown.
//!
//! # Manual reproduction
//!
//! ```bash
//! mkdir -p /tmp/extras
//! printf '# CONFIG_BPF_SYSCALL is not set\n' > /tmp/extras/bad.kconfig
//! cargo ktstr kernel build \
//! --source ../linux \
//! --extra-kconfig /tmp/extras/bad.kconfig \
//! --force
//! # → exit 1, stderr contains "CONFIG_BPF_SYSCALL not set"
//! ```
use PathBuf;
use Command;
/// Path to the linux source tree used by the E2E test.
///
/// Resolved at runtime (the binary doesn't move between compile and
/// run) so a missing tree produces a clean skip rather than a
/// `compile_error!`. The convention is `../linux` relative to the
/// ktstr crate root (matching the kernel build patterns used
/// throughout the project).
/// Path to the cargo-ktstr binary cargo built for this test pass.
/// `CARGO_BIN_EXE_<name>` is set at compile time for every `[[bin]]`
/// the workspace declares, so the test resolves the absolute path
/// without shelling out to `which cargo-ktstr`.
const CARGO_KTSTR_BINARY: &str = env!;
/// Build a kernel with a fragment that disables `CONFIG_BPF_SYSCALL`
/// (a baked-in `=y` from `EMBEDDED_KCONFIG`) and prove the post-
/// build validator catches it. Pins three facets of the
/// `--extra-kconfig` pipeline:
///
/// 1. **Last-wins merge.** `merge_kconfig_fragments` interleaves the
/// user fragment AFTER `EMBEDDED_KCONFIG`, so olddefconfig
/// propagates the user's `# CONFIG_BPF_SYSCALL is not set` over
/// the baked-in `CONFIG_BPF_SYSCALL=y`. Without last-wins, this
/// test would not reach the validator's failure path.
/// 2. **Validator catches the baked-in disablement.** After build,
/// `validate_kernel_config` reads `.config` and looks for
/// `CONFIG_BPF_SYSCALL=y`. The user fragment removed it, so the
/// validator must bail. The error message names
/// `CONFIG_BPF_SYSCALL` and includes the actionable hint
/// "required for BPF program loading".
/// 3. **Cache store is skipped.** `kernel_build_pipeline` calls
/// `validate_kernel_config` BEFORE `cache.store(...)`. A failed
/// validation returns `Err` from the pipeline, so the broken
/// kernel image never lands in the cache directory. (Asserted
/// indirectly: the subprocess exits non-zero, and a successful
/// `cargo ktstr kernel build` would have exited 0.)
///
/// The test runs via `[[bin]] cargo-ktstr` and its `kernel build`
/// subcommand — exercising the same dispatch path the user invokes.
/// `--force` defeats any prior cache hit on this version+extras
/// pair, ensuring every run actually reaches the build phase.