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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
//! The code fences on the registry front pages are copied verbatim into strangers' editors,
//! and until this file no test read a single one of them. An ES import of an export that
//! did not exist and a Rust example that did not compile both shipped to their registries
//! that way. Two checks, each over the fence as it is actually published:
//!
//! 1. Every name imported from `"kshana"` in a JavaScript fence is a `#[wasm_bindgen]`
//! function in `src/wasm.rs`, or one of the loaders wasm-bindgen generates beside them
//! (the `default` export `init`, and `initSync`, which the Node.js recipe uses).
//! 2. The Rust fence in `README.crates.md` is byte-identical to the `readme_crates_example`
//! module below, which this test binary compiles. A README edit that stops compiling
//! therefore fails here as a mismatch, and an API change that breaks the example fails
//! the build. The fence is a complete program and carries its scenario inline, so it
//! runs as written in a fresh project with no scenario file on disk.
//! The example is compiled, never called: running it would write a result file into the
//! working directory on every `cargo test`.
fn fences<'a>(doc: &'a str, lang: &str) -> Vec<Vec<&'a str>> {
let open = format!("```{lang}");
let mut out = Vec::new();
let mut cur: Option<Vec<&str>> = None;
for line in doc.lines() {
match cur.as_mut() {
None if line.trim_end() == open => cur = Some(Vec::new()),
Some(body) if line.trim_end() == "```" => {
out.push(std::mem::take(body));
cur = None;
}
Some(body) => body.push(line),
None => {}
}
}
out
}
/// The named loader wasm-bindgen writes into every `--target web` package beside the
/// crate's own exports. `init` itself is the `default` export and is never imported by name.
const GENERATED_LOADERS: &[&str] = &["initSync"];
fn wasm_exports() -> std::collections::BTreeSet<String> {
let src = include_str!("../src/wasm.rs");
let lines: Vec<&str> = src.lines().collect();
let mut names: std::collections::BTreeSet<String> =
GENERATED_LOADERS.iter().map(|s| s.to_string()).collect();
for (i, l) in lines.iter().enumerate() {
if !l.trim_start().starts_with("#[wasm_bindgen") {
continue;
}
// The attribute may be followed by doc comments or further attributes.
if let Some(sig) = lines[i + 1..]
.iter()
.map(|s| s.trim_start())
.find(|s| s.starts_with("pub fn "))
{
let name = sig["pub fn ".len()..]
.split(['(', '<'])
.next()
.unwrap()
.to_string();
names.insert(name);
}
}
names
}
#[test]
fn every_js_import_from_kshana_is_a_real_wasm_export() {
let exports = wasm_exports();
assert!(
exports.contains("run") && exports.len() >= 6,
"found only {exports:?} in src/wasm.rs — the export scan is broken, not the README"
);
let surfaces = [
("README.npm.md", include_str!("../README.npm.md")),
("README.md", include_str!("../README.md")),
];
let mut checked = 0;
let mut bad = Vec::new();
for (name, doc) in surfaces {
for fence in fences(doc, "js")
.into_iter()
.chain(fences(doc, "javascript"))
{
for line in fence {
let l = line.trim();
if !(l.starts_with("import ")
&& (l.contains("from \"kshana\"") || l.contains("from 'kshana'")))
{
continue;
}
let (Some(a), Some(b)) = (l.find('{'), l.find('}')) else {
continue;
};
if b < a {
continue;
}
for imp in l[a + 1..b].split(',') {
// `x as y` imports the export `x`.
let imp = imp.split(" as ").next().unwrap().trim();
if imp.is_empty() {
continue;
}
checked += 1;
if !exports.contains(imp) {
bad.push(format!(
"{name}: imports `{imp}`, which src/wasm.rs does not export"
));
}
}
}
}
}
assert!(
checked > 0,
"no `import {{ … }} from \"kshana\"` line found in any JS fence — the gate is grading nothing"
);
assert!(bad.is_empty(), "{}", bad.join("\n"));
}
#[test]
fn every_site_import_from_the_wasm_package_is_a_real_export() {
// The Studio's engine worker and the smoke test import from the generated package
// (`./pkg/kshana.js` beside the smoke test, `../pkg/kshana.js` from the Studio's lib/). An
// ES module that imports a name the package does not export fails to LINK, so one
// stale name blanks the whole page — not just the feature that used it.
let exports = wasm_exports();
let files = [
("web/smoke.mjs", include_str!("../web/smoke.mjs")),
(
"web/studio/lib/engine-worker.mjs",
include_str!("../web/studio/lib/engine-worker.mjs"),
),
];
let mut checked = 0;
let mut bad = Vec::new();
for (name, src) in files {
// Imports may span lines; join the statement up to its `from`.
let flat = src.replace('\n', " ");
let mut rest = flat.as_str();
while let Some(i) = rest.find("import ") {
rest = &rest[i + "import ".len()..];
let Some(from) = rest.find(" from ") else {
break;
};
let (clause, after) = rest.split_at(from);
let module = after[" from ".len()..].trim_start().get(1..).unwrap_or("");
if !module.starts_with("./pkg/kshana.js") && !module.starts_with("../pkg/kshana.js") {
continue;
}
let (Some(a), Some(b)) = (clause.find('{'), clause.find('}')) else {
continue;
};
if b < a {
continue;
}
for imp in clause[a + 1..b].split(',') {
let imp = imp.split(" as ").next().unwrap().trim();
if imp.is_empty() {
continue;
}
checked += 1;
if !exports.contains(imp) {
bad.push(format!(
"{name}: imports `{imp}`, which src/wasm.rs does not export"
));
}
}
}
}
assert!(
checked >= 3,
"found only {checked} imported names from the generated package — the scan is broken"
);
// The Studio's main script loads the package with a dynamic import and then reads the
// functions off the module object (`mod.run`, `mod.chart_svg`, …). A name the package
// does not export is `undefined` there, and the call fails only when a visitor uses
// that feature. Check those names against the same export list.
let app = include_str!("../web/studio/app.js");
let at = app
.find("import(\"./pkg/kshana.js\")")
.expect("web/studio/app.js no longer loads ./pkg/kshana.js with a dynamic import");
let window: String = app[at..].chars().take(1500).collect();
let mut used = 0;
let mut rest = window.as_str();
while let Some(i) = rest.find("mod.") {
rest = &rest[i + "mod.".len()..];
let name: String = rest
.chars()
.take_while(|c| c.is_ascii_alphanumeric() || *c == '_')
.collect();
if name.is_empty() || name == "default" {
continue;
}
used += 1;
if !exports.contains(name.as_str()) {
bad.push(format!(
"web/studio/app.js: uses `mod.{name}`, which src/wasm.rs does not export"
));
}
}
assert!(
used >= 3,
"found only {used} package functions used by web/studio/app.js — the scan is broken"
);
assert!(bad.is_empty(), "{}", bad.join("\n"));
}
// ---- README.crates.md example: the region between the markers is compared to the fence.
// The fence is a whole program, so the compiled copy is a whole program too, inside a
// module of its own: there its `main` is an ordinary function, compiled and never called.
#[allow(dead_code)]
#[rustfmt::skip] // the body must stay byte-identical to the published fence
mod readme_crates_example {
// README-EXAMPLE-BEGIN
use kshana::api;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// A complete scenario, not a sketch: scenarios/clock-holdover.toml without its
// comment lines. It runs 2 h, 10 min of GNSS then about 1.8 h with GNSS denied, and
// asks how long a strontium optical clock and a chip-scale atomic clock (CSAC) each
// hold time to within 20 ns. sigma_y(1s) is a clock's Allan deviation at an averaging
// time of one second; q_wf, the white-frequency-noise intensity, is its square.
let toml = r#"
seed = 42
threshold_ns = 20.0
[time]
step_s = 10.0
duration_s = 7200.0
[gnss]
windows = [
{ t0 = 0.0, t1 = 600.0, state = "nominal" },
{ t0 = 600.0, t1 = 7200.0, state = "denied" },
]
[clock_quantum]
id = "optical-sr-lattice"
provenance = "Strontium optical lattice clock, space-oriented goal sigma_y(1s)=1e-15 (Origlia/Schiller/Bongs et al., arXiv:1503.08457); q_wf=sigma_y(1s)^2; ground-demonstrator maturity, not flown; flicker/aging not modeled"
y0 = 5.0e-17
q_wf = 1.0e-30
q_rw = 0.0
[clock_classical]
id = "csac-sa45s"
provenance = "Microchip SA65 / SA.45s CSAC datasheet sigma_y(1s)=3e-10; q_wf=sigma_y(1s)^2; flicker/aging not modeled"
y0 = 5.0e-10
q_wf = 9.0e-20
q_rw = 0.0
"#;
let out = api::run_toml(toml)?; // RunOutput { json, svg, summary, csv }
println!("{}", out.summary); // the one-line result string
std::fs::write("clock-holdover.result.json", &out.json)?; // the full result document
Ok(())
}
// README-EXAMPLE-END
}
#[test]
fn readme_crates_rust_fence_is_the_compiled_example() {
let me = include_str!("readme_code_fences_doc_sync.rs");
let begin = me.find("// README-EXAMPLE-BEGIN\n").expect("begin marker")
+ "// README-EXAMPLE-BEGIN\n".len();
let end = me[begin..]
.find(" // README-EXAMPLE-END")
.expect("end marker")
+ begin;
let compiled: Vec<&str> = me[begin..end].lines().collect();
let rust = fences(include_str!("../README.crates.md"), "rust");
assert_eq!(
rust.len(),
1,
"README.crates.md should carry exactly one ```rust fence"
);
// No rustdoc-hidden `# ` lines: crates.io renders them, and a reader who pastes the
// fence gets a syntax error. The fence is a whole program instead, `main` and all.
let hidden: Vec<&str> = rust[0]
.iter()
.copied()
.filter(|l| l.starts_with("# "))
.collect();
assert!(
hidden.is_empty(),
"README.crates.md's Rust fence has rustdoc-hidden lines, which crates.io shows and \
which do not compile when pasted: {hidden:?}"
);
let published: Vec<&str> = rust[0].to_vec();
assert_eq!(
published, compiled,
"README.crates.md's Rust example no longer matches the compiled copy in \
tests/readme_code_fences_doc_sync.rs — edit both together"
);
}