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
//! The one version of each `murmur:*` WIT package this host serves.
/// Every `murmur:*` WIT package declared under `crates/capsule-runtime/wit/`, paired with the one
/// version of it this host links and resolves, sorted by package name.
///
/// These are the only versions the host accepts. Each interface is resolved or provided under
/// exactly one instance name, with no fallback to any other version (see `wit/VERSIONING.md`), so
/// a component naming any other version of one of these packages fails to instantiate.
/// `mur doctor` compares every installed artifact's recorded interfaces against this table to name
/// the ones a launch would refuse.
///
/// The tests below hold this table equal to the `package` declarations in the WIT tree and to
/// every interface name the host resolves, so it moves with a version bump or fails the build.
pub const SERVED_WIT_PACKAGES: &[(&str, &str)] = &[
("murmur:artifact-manager", "0.1.0"),
("murmur:capsule", "0.1.0"),
("murmur:conversation", "0.2.0"),
("murmur:driver", "0.3.0"),
("murmur:hook", "0.9.0"),
("murmur:host", "0.1.0"),
("murmur:message", "0.1.0"),
("murmur:runtime", "0.4.0"),
("murmur:runtime-guest", "0.1.0"),
("murmur:shell", "0.1.0"),
("murmur:stream", "0.1.0"),
("murmur:task", "0.1.0"),
("murmur:task-io", "0.1.0"),
("murmur:tool", "0.1.0"),
("murmur:tool-registry", "0.1.0"),
];
#[cfg(test)]
mod tests {
use std::collections::BTreeSet;
use std::path::Path;
use super::SERVED_WIT_PACKAGES;
fn served_set() -> BTreeSet<(String, String)> {
SERVED_WIT_PACKAGES
.iter()
.map(|(package, version)| (package.to_string(), version.to_string()))
.collect()
}
/// The table names each package once, and exactly the package versions the WIT tree declares.
/// The tree is what `wit-bindgen` generates the host's bindings from, so a version bump in it
/// that the table does not follow fails here.
#[test]
fn wit_versions_table_matches_the_wit_tree() {
let declared = murmur_artifact::wit_package_declarations(Path::new(concat!(
env!("CARGO_MANIFEST_DIR"),
"/wit"
)))
.unwrap();
assert!(!declared.is_empty(), "no murmur package declarations found");
let mut packages: Vec<&str> = SERVED_WIT_PACKAGES.iter().map(|(p, _)| *p).collect();
packages.sort_unstable();
packages.dedup();
assert_eq!(
packages.len(),
SERVED_WIT_PACKAGES.len(),
"a package appears twice in SERVED_WIT_PACKAGES"
);
assert_eq!(served_set(), declared);
}
/// Every instance name the host resolves or provides is at its package's served version. A
/// resolver constant moved without the table, or the table without the constant, fails here.
#[test]
fn wit_versions_table_covers_every_resolved_interface() {
let resolved = [
crate::hooks::LIFECYCLE_IFACE,
crate::process_driver::PROCESS_DRIVER_IFACE,
crate::runtime::WIT_CAPSULE_IFACE_VERSIONED,
crate::runtime::WIT_TOOL_IFACE_VERSIONED,
crate::runtime::WIT_TOOL_REGISTRY_IFACE,
crate::runtime::WIT_STREAM_EVENTS_IFACE,
crate::runtime::WIT_TASK_IFACE,
crate::conversation_import::CONVERSATION_IFACE_VERSIONED,
crate::inference_import::INFERENCE_IFACE_VERSIONED,
crate::task_io_import::TASK_IO_IFACE_VERSIONED,
crate::tokens_import::TOKENS_IFACE_VERSIONED,
];
let served = served_set();
for interface in resolved {
let (package, rest) = interface
.split_once('/')
.unwrap_or_else(|| panic!("{interface} names no interface"));
let (_, version) = rest
.rsplit_once('@')
.unwrap_or_else(|| panic!("{interface} carries no version"));
assert!(
served.contains(&(package.to_string(), version.to_string())),
"{interface} is not at a version SERVED_WIT_PACKAGES lists for {package}"
);
}
}
/// Doctor compares versions as exact strings, while wasmtime's linker treats two names that
/// differ only in a non-zero patch field as semver-compatible and links them. The two answers
/// agree only while murmur never ships a package at a non-zero patch.
#[test]
fn wit_versions_every_patch_field_is_zero() {
for (package, version) in SERVED_WIT_PACKAGES {
let patch = version
.split('.')
.nth(2)
.unwrap_or_else(|| panic!("{package}@{version} has no patch field"));
assert_eq!(patch, "0", "{package}@{version} has a non-zero patch field");
}
}
}