#![allow(missing_docs)]
use std::{path::PathBuf, process::Command};
use hpsvm::HPSVM;
use hpsvm_fixture::{
AccountSnapshot, CaptureBuilder, Compare, ExecutionSnapshot, FixtureFormat,
RuntimeFixtureConfig,
};
use solana_address::Address;
use solana_keypair::Keypair;
use solana_message::Message;
use solana_signer::Signer;
use solana_system_interface::instruction::transfer;
use solana_transaction::versioned::VersionedTransaction;
struct FixtureDir(PathBuf);
impl FixtureDir {
fn new(stem: &str) -> Self {
let unique = Address::new_unique();
let process = std::process::id();
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("system time must be after unix epoch")
.as_nanos();
Self(std::env::temp_dir().join(format!("{stem}-{process}-{nanos}-{unique}")))
}
fn path(&self) -> &std::path::Path {
&self.0
}
}
impl Drop for FixtureDir {
fn drop(&mut self) {
std::fs::remove_dir_all(&self.0).ok();
}
}
fn snapshot_account(svm: &HPSVM, address: Address) -> AccountSnapshot {
let account = svm.get_account(&address).expect("account must exist");
AccountSnapshot::from_readable(address, &account)
}
fn fixture_path(stem: &str) -> PathBuf {
let unique = Address::new_unique();
std::env::temp_dir().join(format!("{stem}-{unique}.json"))
}
fn write_fixture(name: &str) -> PathBuf {
let path = fixture_path("hpsvm-cli-compare");
write_fixture_to_path(name, path.clone(), FixtureFormat::Json);
path
}
fn write_fixture_to_path(name: &str, path: PathBuf, format: FixtureFormat) {
let mut svm = HPSVM::new();
let payer = Keypair::new();
let recipient = Address::new_unique();
svm.airdrop(&payer.pubkey(), 10_000).expect("payer airdrop must succeed");
svm.airdrop(&recipient, 1).expect("recipient airdrop must succeed");
let tx = VersionedTransaction::from(solana_transaction::Transaction::new(
&[&payer],
Message::new(&[transfer(&payer.pubkey(), &recipient, 64)], Some(&payer.pubkey())),
svm.latest_blockhash(),
));
let baseline = ExecutionSnapshot::from_outcome(&svm.transact(tx.clone()));
let fixture = CaptureBuilder::new(name)
.runtime(RuntimeFixtureConfig::new(svm.block_env().slot, None, true, false))
.pre_accounts(vec![
snapshot_account(&svm, payer.pubkey()),
snapshot_account(&svm, recipient),
])
.baseline(baseline)
.compares(Compare::everything())
.capture_transaction(&tx)
.expect("fixture capture must succeed");
fixture.save(&path, format).expect("fixture save must succeed");
}
#[test]
fn fixture_compare_passes_for_identical_inputs() {
let path = write_fixture("cli-compare");
let output = Command::new(env!("CARGO_BIN_EXE_hpsvm"))
.args(["fixture", "compare", path.to_str().expect("temp path must be valid utf-8")])
.output()
.expect("compare command must execute");
assert!(output.status.success());
assert!(String::from_utf8_lossy(&output.stdout).contains("PASS:"));
std::fs::remove_file(path).ok();
}
#[test]
fn fixture_compare_passes_for_fixture_directory() {
let dir = FixtureDir::new("hpsvm-cli-compare-dir");
std::fs::create_dir(dir.path()).expect("fixture directory must be created");
write_fixture_to_path("cli-compare-dir-second", dir.path().join("b.json"), FixtureFormat::Json);
write_fixture_to_path("cli-compare-dir-first", dir.path().join("a.bin"), FixtureFormat::Binary);
std::fs::write(dir.path().join("notes.txt"), "ignore me").expect("notes file must be written");
let output = Command::new(env!("CARGO_BIN_EXE_hpsvm"))
.args(["fixture", "compare", dir.path().to_str().expect("temp path must be valid utf-8")])
.output()
.expect("compare command must execute");
eprintln!("status: {}", output.status);
eprintln!("stdout: {}", String::from_utf8_lossy(&output.stdout));
eprintln!("stderr: {}", String::from_utf8_lossy(&output.stderr));
for ext in &["json", "bin"] {
for entry in std::fs::read_dir(dir.path()).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.extension().and_then(|e| e.to_str()) == Some(*ext) {
let output = Command::new(env!("CARGO_BIN_EXE_hpsvm"))
.args(["fixture", "compare", path.to_str().unwrap()])
.output()
.unwrap();
eprintln!(
"individual {}: status={}, stderr={}",
path.display(),
output.status,
String::from_utf8_lossy(&output.stderr)
);
}
}
}
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
let first = stdout.find("PASS: cli-compare-dir-first").expect("first fixture should pass");
let second = stdout.find("PASS: cli-compare-dir-second").expect("second fixture should pass");
assert!(first < second, "fixtures should run in sorted path order: {stdout}");
}
#[test]
fn fixture_compare_rejects_empty_fixture_directory() {
let dir = FixtureDir::new("hpsvm-cli-compare-empty-dir");
std::fs::create_dir(dir.path()).expect("fixture directory must be created");
std::fs::write(dir.path().join("notes.txt"), "ignore me").expect("notes file must be written");
let output = Command::new(env!("CARGO_BIN_EXE_hpsvm"))
.args(["fixture", "compare", dir.path().to_str().expect("temp path must be valid utf-8")])
.output()
.expect("compare command must execute");
assert!(!output.status.success());
assert!(String::from_utf8_lossy(&output.stderr).contains("no fixture files found"));
}