use std::sync::{Mutex, MutexGuard, OnceLock};
pub fn mutating_test_guard() -> MutexGuard<'static, ()> {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
LOCK.get_or_init(|| Mutex::new(()))
.lock()
.unwrap_or_else(|err| err.into_inner())
}
use std::path::{Path, PathBuf};
use flate2::{Compression, write::GzEncoder};
pub struct OfflineFixture {
pub manifest_path: PathBuf,
pub manifest_uri: String,
pub dir: PathBuf,
devnet: String,
mainnet: String,
testnet: String,
channels: Vec<serde_json::Value>,
}
impl OfflineFixture {
pub fn new(root: &Path) -> Self {
let dir = root.join("offline-fixture");
std::fs::create_dir_all(&dir).expect("failed to create fixture dir");
let manifest_path = dir.join("channel-manifest.json");
let manifest_uri = format!("file://{}", manifest_path.display());
Self {
manifest_path,
manifest_uri,
dir,
devnet: String::new(),
mainnet: String::new(),
testnet: String::new(),
channels: vec![],
}
}
pub fn create(root: &Path, channel: &str) -> Self {
Self::new(root).with_channel(channel).build()
}
pub fn build(mut self) -> Self {
let channels = core::mem::take(&mut self.channels);
let manifest = serde_json::json!({
"manifest_version": "3.0.0",
"date": 1735689600,
"networks": {"devnet": self.devnet.clone(), "mainnet": self.mainnet.clone(), "testnet": self.testnet.clone()},
"channels": channels
});
std::fs::write(&self.manifest_path, serde_json::to_string_pretty(&manifest).unwrap())
.expect("failed to write fixture manifest");
self
}
pub fn with_channel(mut self, channel: &str) -> Self {
if self.channels.is_empty() {
self.mainnet = channel.to_string();
self.testnet = self.mainnet.clone();
self.devnet = self.mainnet.clone();
}
let dir = self.dir.join(channel);
std::fs::create_dir_all(&dir).expect("failed to create fixture channel dir");
let vm_binary = dir.join("miden-vm");
let vm_script = format!(
r#"#!/bin/sh
echo "miden-vm {channel}"
exit 0
"#,
channel = channel
);
std::fs::write(&vm_binary, &vm_script).expect("failed to write fixture binary");
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(&vm_binary, std::fs::Permissions::from_mode(0o755))
.expect("failed to chmod fixture binary");
}
let core_package = dir.join("core.masp");
std::fs::write(&core_package, b"fixture-package").expect("failed to write fixture package");
let asset = dir.join("config.yml");
std::fs::write(&asset, b"fixture: true\n").expect("failed to write fixture asset");
let uri = |path: &Path| format!("file://{}", path.display());
self.channels.push(serde_json::json!({
"name": channel,
"components": [
{
"name": "vm",
"version": {"kind": "registry", "version": "0.1.0"},
"kind": "executable",
"installation_method": {"kind": "prebuilt"},
"installed-executable": "miden-vm",
"profiles": ["minimal"],
"artifacts": {"miden-vm": {"uri": uri(&vm_binary)}}
},
{
"name": "core",
"version": {"kind": "registry", "version": "0.1.0"},
"kind": "package",
"profiles": ["minimal"],
"artifacts": {"core.masp": {"uri": uri(&core_package)}}
},
{
"name": "assets",
"version": {"kind": "registry", "version": "0.1.0"},
"kind": "asset",
"profiles": ["complete"],
"artifacts": {"config.yml": {"uri": uri(&asset)}}
}
]
}));
self
}
pub fn with_archived_component(mut self) -> Self {
use std::io::Write;
let channel = self
.channels
.last_mut()
.expect("with_archived_component needs a channel to add to");
let name = channel["name"].as_str().expect("a channel has a name").to_string();
let mut tar = tar::Builder::new(Vec::new());
let mut dir = tar::Header::new_gnu();
dir.set_entry_type(tar::EntryType::Directory);
dir.set_size(0);
dir.set_mode(0o755);
dir.set_path("packages-1.0/").unwrap();
dir.set_cksum();
tar.append(&dir, std::io::empty()).expect("failed to add directory");
let contents = b"archived-package";
let mut header = tar::Header::new_gnu();
header.set_size(contents.len() as u64);
header.set_mode(0o644);
header.set_cksum();
tar.append_data(&mut header, "packages-1.0/archived.masp", &contents[..])
.expect("failed to add member");
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
encoder
.write_all(&tar.into_inner().unwrap())
.expect("failed to compress fixture tarball");
let tarball = self.dir.join(&name).join("archived.masp.tar.gz");
std::fs::write(&tarball, encoder.finish().unwrap()).expect("failed to write tarball");
channel["components"].as_array_mut().expect("a channel has components").push(
serde_json::json!({
"name": "packages",
"version": {"kind": "registry", "version": "1.0.0"},
"kind": "package",
"profiles": ["minimal"],
"artifacts": {
"archived.masp": {
"uri": format!("file://{}", tarball.display()),
"archive": "tar.gz"
}
}
}),
);
self
}
pub fn with_cargo_component(mut self, name: &str) -> Self {
let package = format!("fixture-{name}");
let binary = format!("miden-{name}");
let source = self.dir.join(format!("{name}-source"));
write_trivial_crate(&source, &package, &binary);
let channel = self.channels.last_mut().expect("a channel must be added before a component");
channel["components"].as_array_mut().expect("components is an array").push(
serde_json::json!({
"name": name,
"version": {"kind": "path", "path": source.to_str().expect("utf-8 fixture path")},
"kind": "executable",
"installation_method": {"kind": "cargo", "crate_name": package},
"installed-executable": binary,
"profiles": ["minimal"]
}),
);
self
}
}
pub struct SourceFixture {
pub path_crate: PathBuf,
pub git_repo: PathBuf,
pub revisions: Vec<String>,
}
impl SourceFixture {
pub fn build(root: &Path) -> Self {
let path_crate = root.join("path-source");
write_trivial_crate(&path_crate, "fixture-vm", "miden-vm");
let git_repo = root.join("git-source");
write_trivial_crate(&git_repo, "fixture-client", "miden-client");
let revisions = init_repo_with_two_commits(&git_repo);
Self { path_crate, git_repo, revisions }
}
pub fn git_url(&self) -> String {
format!("file://{}", self.git_repo.display())
}
}
fn write_trivial_crate(dir: &Path, package: &str, binary: &str) {
std::fs::create_dir_all(dir.join("src")).expect("failed to create fixture crate dir");
std::fs::write(
dir.join("Cargo.toml"),
format!(
"[package]\nname = \"{package}\"\nversion = \"0.1.0\"\nedition = \
\"2021\"\n\n[[bin]]\nname = \"{binary}\"\npath = \"src/main.rs\"\n"
),
)
.expect("failed to write fixture Cargo.toml");
std::fs::write(dir.join("src").join("main.rs"), "fn main() {}\n")
.expect("failed to write fixture main.rs");
let status = std::process::Command::new("cargo")
.arg("generate-lockfile")
.arg("--manifest-path")
.arg(dir.join("Cargo.toml"))
.status()
.expect("failed to run cargo generate-lockfile");
assert!(status.success(), "cargo generate-lockfile failed for {}", dir.display());
}
fn init_repo_with_two_commits(dir: &Path) -> Vec<String> {
let git = |args: &[&str]| {
let output = std::process::Command::new("git")
.args(args)
.current_dir(dir)
.output()
.unwrap_or_else(|err| panic!("failed to run git {args:?}: {err}"));
assert!(
output.status.success(),
"git {args:?} failed: {}",
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8_lossy(&output.stdout).trim().to_string()
};
git(&["init", "--quiet", "--initial-branch=main"]);
git(&["config", "user.email", "fixture@example.invalid"]);
git(&["config", "user.name", "Fixture"]);
git(&["add", "."]);
git(&["commit", "--quiet", "-m", "first"]);
let first = git(&["rev-parse", "HEAD"]);
std::fs::write(dir.join("CHANGES"), b"second\n").expect("failed to write fixture change");
git(&["add", "."]);
git(&["commit", "--quiet", "-m", "second"]);
let second = git(&["rev-parse", "HEAD"]);
vec![first, second]
}
pub fn write_source_manifest(
dir: &Path,
name: &str,
fixture: &SourceFixture,
revision: &str,
) -> String {
let manifest = serde_json::json!({
"manifest_version": "3.0.0",
"date": 1735689600,
"networks": {"mainnet": "0.15.0"},
"channels": [{
"name": "0.15.0",
"components": [
{
"name": "vm",
"version": {"kind": "path", "path": fixture.path_crate.to_str().unwrap()},
"kind": "executable",
"installation_method": {"kind": "cargo", "crate_name": "fixture-vm"},
"installed-executable": "miden-vm",
"profiles": ["minimal"]
},
{
"name": "client",
"version": {
"kind": "git",
"repository_url": fixture.git_url(),
"revision": revision
},
"kind": "executable",
"installation_method": {"kind": "cargo", "crate_name": "fixture-client"},
"installed-executable": "miden-client",
"profiles": ["minimal"]
}
]
}]
});
let path = dir.join(name);
std::fs::write(&path, serde_json::to_string_pretty(&manifest).unwrap())
.expect("failed to write source manifest");
format!("file://{}", path.display())
}
pub struct UpdateFixture {
dir: PathBuf,
}
impl UpdateFixture {
pub fn build(root: &Path) -> Self {
let dir = root.join("update-fixture");
std::fs::create_dir_all(&dir).expect("failed to create update fixture dir");
for version in ["0.23.1", "0.23.2", "0.23.3", "0.23.4"] {
std::fs::write(dir.join(format!("miden-vm-{version}")), b"#!/bin/sh\nexit 0\n")
.expect("failed to write fixture binary");
}
std::fs::write(dir.join("miden-client"), b"#!/bin/sh\nexit 0\n")
.expect("failed to write fixture binary");
std::fs::write(dir.join("core.masp"), b"fixture-package")
.expect("failed to write fixture package");
Self { dir }
}
fn uri(&self, name: &str) -> String {
format!("file://{}", self.dir.join(name).display())
}
fn vm(&self, version: &str) -> serde_json::Value {
serde_json::json!({
"name": "vm",
"version": {"kind": "registry", "version": version},
"kind": "executable",
"installation_method": {"kind": "prebuilt"},
"installed-executable": "miden-vm",
"profiles": ["minimal"],
"artifacts": {"miden-vm": {"uri": self.uri("miden-vm-%version")}}
})
}
fn core(&self, authority: serde_json::Value) -> serde_json::Value {
serde_json::json!({
"name": "core",
"version": authority,
"kind": "package",
"profiles": ["minimal"],
"artifacts": {"core.masp": {"uri": self.uri("core.masp")}}
})
}
fn client(&self) -> serde_json::Value {
serde_json::json!({
"name": "client",
"version": {"kind": "registry", "version": "0.9.0"},
"kind": "executable",
"installation_method": {"kind": "prebuilt"},
"installed-executable": "miden-client",
"profiles": ["minimal"],
"artifacts": {"miden-client": {"uri": self.uri("miden-client")}}
})
}
fn registry(version: &str) -> serde_json::Value {
serde_json::json!({"kind": "registry", "version": version})
}
fn write(&self, name: &str, mainnet: &str, channels: serde_json::Value) -> String {
let manifest = serde_json::json!({
"manifest_version": "3.0.0",
"date": 1735689600,
"networks": {"mainnet": mainnet},
"channels": channels
});
let path = self.dir.join(name);
std::fs::write(&path, serde_json::to_string_pretty(&manifest).unwrap())
.expect("failed to write fixture manifest");
format!("file://{}", path.display())
}
pub fn initial(&self) -> String {
self.write(
"manifest-1.json",
"0.14.0",
serde_json::json!([{
"name": "0.14.0",
"components": [self.vm("0.23.2"), self.core(Self::registry("0.23.2"))]
}]),
)
}
pub fn with_new_stable(&self) -> String {
self.write(
"manifest-2.json",
"0.15.0",
serde_json::json!([
{
"name": "0.14.0",
"components": [self.vm("0.23.2"), self.core(Self::registry("0.23.2"))]
},
{
"name": "0.15.0",
"components": [self.vm("0.23.3"), self.core(Self::registry("0.23.3"))]
}
]),
)
}
pub fn with_every_change(&self) -> String {
self.write(
"manifest-3.json",
"0.16.0",
serde_json::json!([
{
"name": "0.14.0",
"components": [
self.vm("0.23.1"),
self.core(serde_json::json!({
"kind": "git",
"repository_url": "https://example.invalid/core.git",
"revision": "0000000000000000000000000000000000000000"
})),
self.client()
]
},
{
"name": "0.15.0",
"components": [self.vm("0.23.4")]
},
{
"name": "0.16.0",
"components": [self.vm("0.23.4"), self.core(Self::registry("0.23.4"))]
}
]),
)
}
pub fn with_split_networks(&self) -> String {
self.write_split("manifest-split.json", "0.23.3")
}
pub fn with_split_networks_and_a_bumped_component(&self) -> String {
self.write_split("manifest-split-bumped.json", "0.23.4")
}
pub fn with_networks_on_one_channel(&self) -> String {
self.write_split_at("manifest-shared.json", "0.15.0", "0.23.3")
}
fn write_split(&self, name: &str, devnet_vm: &str) -> String {
self.write_split_at(name, "0.14.0", devnet_vm)
}
fn write_split_at(&self, name: &str, mainnet: &str, devnet_vm: &str) -> String {
let manifest = serde_json::json!({
"manifest_version": "3.0.0",
"date": 1735689600,
"networks": {"devnet": "0.15.0", "mainnet": mainnet},
"channels": [
{
"name": "0.14.0",
"components": [self.vm("0.23.2"), self.core(Self::registry("0.23.2"))]
},
{
"name": "0.15.0",
"components": [self.vm(devnet_vm), self.core(Self::registry("0.23.3"))]
}
]
});
let path = self.dir.join(name);
std::fs::write(&path, serde_json::to_string_pretty(&manifest).unwrap())
.expect("failed to write fixture manifest");
format!("file://{}", path.display())
}
}