use assert_cmd::prelude::*;
use predicates::prelude::*;
use serde_json::Value;
use std::io::Write;
mod common;
use common::{expected_schema_url, fixture_path, rtl_fixture_path, wavepeek_cmd};
#[test]
fn info_human_output_is_default_for_vcd_fixture() {
let fixture = fixture_path("m2_core.vcd");
let fixture = fixture.to_string_lossy().into_owned();
let mut command = wavepeek_cmd();
command
.args(["info", "--waves", fixture.as_str()])
.assert()
.success()
.stdout(predicate::str::contains("time_unit: 1ns"))
.stdout(predicate::str::contains("time_precision").not())
.stdout(predicate::str::contains("time_start: 0ns"))
.stdout(predicate::str::contains("time_end: 10ns"))
.stderr(predicate::str::is_empty());
}
#[test]
fn info_json_contract_for_vcd_fixture() {
let fixture = fixture_path("m2_core.vcd");
let fixture = fixture.to_string_lossy().into_owned();
let mut command = wavepeek_cmd();
let assert = command
.args(["info", "--waves", fixture.as_str(), "--json"])
.assert()
.success();
let stdout = String::from_utf8_lossy(&assert.get_output().stdout).to_string();
let value: Value = serde_json::from_str(&stdout).expect("info output should be valid json");
assert_eq!(value["$schema"], expected_schema_url());
assert!(value.get("schema_version").is_none());
assert_eq!(value["command"], "info");
assert_eq!(value["warnings"], Value::Array(vec![]));
assert_eq!(value["data"]["time_unit"], "1ns");
assert!(value["data"].get("time_precision").is_none());
assert_eq!(value["data"]["time_start"], "0ns");
assert_eq!(value["data"]["time_end"], "10ns");
}
#[test]
fn info_json_contract_for_fst_fixture() {
let fixture = fixture_path("m2_core.fst");
let fixture = fixture.to_string_lossy().into_owned();
let mut command = wavepeek_cmd();
let assert = command
.args(["info", "--waves", fixture.as_str(), "--json"])
.assert()
.success();
let stdout = String::from_utf8_lossy(&assert.get_output().stdout).to_string();
let value: Value = serde_json::from_str(&stdout).expect("info output should be valid json");
assert_eq!(value["$schema"], expected_schema_url());
assert!(value.get("schema_version").is_none());
assert_eq!(value["command"], "info");
assert_eq!(value["warnings"], Value::Array(vec![]));
assert_eq!(value["data"]["time_unit"], "1ns");
assert!(value["data"].get("time_precision").is_none());
assert_eq!(value["data"]["time_start"], "0ns");
assert_eq!(value["data"]["time_end"], "10ns");
}
#[test]
fn info_json_output_is_deterministic_across_runs() {
let fixture = fixture_path("m2_core.vcd");
let fixture = fixture.to_string_lossy().into_owned();
let first = wavepeek_cmd()
.args(["info", "--waves", fixture.as_str(), "--json"])
.output()
.expect("first run should execute");
let second = wavepeek_cmd()
.args(["info", "--waves", fixture.as_str(), "--json"])
.output()
.expect("second run should execute");
assert!(first.status.success());
assert!(second.status.success());
assert_eq!(first.stdout, second.stdout);
assert_eq!(first.stderr, second.stderr);
}
#[test]
fn info_json_contract_for_external_picorv32_fixture() {
let fixture = rtl_fixture_path("picorv32_test_vcd.fst");
assert!(
fixture.exists(),
"required external fixture is missing: {}",
fixture.display()
);
let mut command = wavepeek_cmd();
let fixture = fixture.to_string_lossy().into_owned();
let assert = command
.args(["info", "--waves", fixture.as_str(), "--json"])
.assert()
.success();
let stdout = String::from_utf8_lossy(&assert.get_output().stdout).to_string();
let value: Value = serde_json::from_str(&stdout).expect("info output should be valid json");
assert_eq!(value["command"], "info");
assert!(value["data"]["time_unit"].as_str().is_some());
assert!(value["data"]["time_start"].as_str().is_some());
assert!(value["data"]["time_end"].as_str().is_some());
assert!(value["data"].get("time_precision").is_none());
}
#[test]
fn info_missing_file_is_file_error_with_exit_code_two() {
let mut command = wavepeek_cmd();
command
.args(["info", "--waves", "/tmp/wavepeek-does-not-exist.vcd"])
.assert()
.failure()
.code(2)
.stdout(predicate::str::is_empty())
.stderr(predicate::str::starts_with("error: file: cannot open"));
}
#[test]
fn info_invalid_file_is_parse_error_with_exit_code_two() {
let mut fixture = tempfile::NamedTempFile::new().expect("temp file should be created");
fixture
.write_all(b"not-a-waveform")
.expect("temp file should be writable");
let mut command = wavepeek_cmd();
let path = fixture.path().to_string_lossy().into_owned();
command
.args(["info", "--waves", path.as_str()])
.assert()
.failure()
.code(2)
.stdout(predicate::str::is_empty())
.stderr(predicate::str::starts_with("error: file: cannot parse"));
}