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
171
172
173
174
175
176
177
178
//! Performance guard, with an honest scope: this test fails loudly on a
//! quadratic regression in parsing and import/namespace resolution — the
//! dominant cost in `load_project`, and the one actually capable of
//! pushing past the 10ms budget at a realistic beam count. Dependency-
//! graph validation (`validate_graph`'s `needs` lookup) is also genuinely
//! exercised by the fixture below, not left untested, but at 100 beams a
//! lookup regression there only moves the measured time by something on
//! the order of 10-20% (measured below) — nowhere near enough to trip the
//! threshold on its own. See below for why that's a property of the
//! problem's scale, not a gap in this test.
//!
//! Lives here, in `alba-core`, rather than as a CLI-level test that spawns
//! `alba check`: the work the criterion is actually about — parsing every
//! Beamfile, resolving imports, evaluating templates, and validating the
//! graph — happens entirely inside [`load_project`]. Spawning a process to
//! measure it would fold in process-startup cost (dynamic linker, OS
//! scheduling, first-touch filesystem caching) that has nothing to do with
//! parsing and can dwarf the sub-millisecond work under test, turning a
//! regression guard into a test that flakes for reasons unrelated to what
//! it is supposed to catch. Calling `load_project` directly, in-process,
//! measures exactly the work the criterion names and nothing else.
//!
//! The fixture is not just 100 beams with empty `needs` lists. An earlier
//! version of this test was exactly that, and it exercised only the
//! parsing/import half of `load_project`:
//! `validate_graph`'s `by_id` lookup (see `graph.rs`) never iterates a
//! `needs` entry when there are none, so an O(1)-to-O(n) regression there
//! was completely invisible to it — confirmed by deliberately breaking
//! that lookup into a linear scan and watching the measured median not
//! move at all. Every beam declared here now needs every beam declared
//! before it in the same file (a triangular fan-in, the densest DAG a
//! file's beams can form without a cycle), and each file that imports
//! another additionally needs that import's last beam through its alias,
//! so the cross-namespace `needs` lookup path is exercised too.
//!
//! Doing the same mutation against *this* fixture does move the measured
//! time — repeatably, not as noise — but nowhere near the multiple-times
//! jump the parsing-side mutation produces. Two independent measurements
//! agree on that much and disagree on the exact number: a handful of
//! 50-sample trials put it around 7%, a later independent re-measurement
//! (5 trials of 50 samples) put it at 18-20%. Both are real; the gap
//! between them is this machine's own run-to-run noise, not a
//! disagreement about what's being measured — the *unmutated* baseline
//! alone swung from a 1.69ms to a 1.92ms median across otherwise-identical
//! runs during this testing, a spread nearly as wide as the effect being
//! measured, and a handful of trials on each side isn't enough to average
//! that out reliably. Call the honest range 10-20%, not a single number:
//! real, repeatable, and an order of magnitude short of the parsing-side
//! mutation's 2-3x. That gap is not a weaker fixture: at 100 beams, a
//! linear scan over short ids is simply cheap in absolute terms (most
//! candidates are rejected by a length check before ever comparing
//! bytes), so an O(1)-vs-O(n) difference here is inherently a smaller
//! fraction of the total than the parsing-side regression is. Pushing the
//! fixture denser to force a larger gap was tried and rejected: it made
//! *this* test's own baseline (the correct implementation) approach the
//! 10ms budget on this machine, which would trade a real flake risk for a
//! bigger number in a comment. Catching a regression at all — clearly and
//! repeatably above run-to-run noise — is what this fixture is for; it is
//! not a benchmark of how bad a hypothetical regression could be made to
//! look.
//!
//! The 10ms budget is calibrated for `cargo test`'s debug (unoptimized)
//! profile, since that is what every CI leg actually runs; a `--release`
//! build only has more headroom under the same threshold, never less.
//! Building the fixture (writing ~100 files to a tempdir) happens before
//! the clock starts and is never part of the measurement.
use PathBuf;
use ;
use load_project;
/// Beams declared directly in each of the 4 files in the fixture's import
/// chain (root, level1, level2, level3) — `4 * 25 = 100` beams total.
const BEAMS_PER_FILE: usize = 25;
/// Writes `content` to `path`, creating any missing parent directories
/// first — mirrors `alba-core/tests/loader.rs`'s own fixture helper.
/// `count` beams with real dependency edges, not just distinct ids: beam
/// `bI` needs every beam declared before it in this same file
/// (`b0..b(I-1)`, a triangular fan-in — `I` edges for beam `I`, `O(count^2)`
/// edges overall), plus `cross_need` on `b0` when given — a namespaced
/// reference into whichever file this one imports, so the cross-file,
/// alias-prefixed `needs` lookup path is exercised too, not only the
/// plain one. None of this can create a cycle: every intra-file edge
/// points strictly backwards by index, and a cross-file edge points into
/// a file that has already finished loading and cannot depend back on
/// this one.
/// Lays out a 3-level import chain — root imports level1, which imports
/// level2, which imports level3 — with `BEAMS_PER_FILE` beams declared in
/// each of the 4 files, and returns the root Beamfile's path. Each file's
/// first beam additionally needs the last beam its import declares (the
/// one every other beam in that imported file already transitively needs,
/// being its triangular fan-in's final entry), so real dependency edges
/// run across the whole tree, not just within each file.