Add save-slice-backed runtime fixtures
This commit is contained in:
parent
09b6514dbf
commit
8ca65cbbfb
12 changed files with 974 additions and 47 deletions
|
|
@ -1,6 +1,10 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use rrt_runtime::{load_runtime_snapshot_document, validate_runtime_snapshot_document};
|
||||
use rrt_runtime::{
|
||||
load_runtime_save_slice_document, load_runtime_snapshot_document,
|
||||
project_save_slice_to_runtime_state_import, validate_runtime_save_slice_document,
|
||||
validate_runtime_snapshot_document,
|
||||
};
|
||||
|
||||
use crate::{FixtureDocument, FixtureStateOrigin, RawFixtureDocument};
|
||||
|
||||
|
|
@ -28,17 +32,23 @@ fn resolve_raw_fixture_document(
|
|||
raw: RawFixtureDocument,
|
||||
base_dir: &Path,
|
||||
) -> Result<FixtureDocument, Box<dyn std::error::Error>> {
|
||||
let state = match (&raw.state, &raw.state_snapshot_path) {
|
||||
(Some(_), Some(_)) => {
|
||||
return Err(
|
||||
"fixture must not specify both inline state and state_snapshot_path".into(),
|
||||
);
|
||||
}
|
||||
(None, None) => {
|
||||
return Err("fixture must specify either inline state or state_snapshot_path".into());
|
||||
}
|
||||
(Some(state), None) => state.clone(),
|
||||
(None, Some(snapshot_path)) => {
|
||||
let specified_state_inputs = usize::from(raw.state.is_some())
|
||||
+ usize::from(raw.state_snapshot_path.is_some())
|
||||
+ usize::from(raw.state_save_slice_path.is_some());
|
||||
if specified_state_inputs != 1 {
|
||||
return Err(
|
||||
"fixture must specify exactly one of inline state, state_snapshot_path, or state_save_slice_path"
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
let state = match (
|
||||
&raw.state,
|
||||
&raw.state_snapshot_path,
|
||||
&raw.state_save_slice_path,
|
||||
) {
|
||||
(Some(state), None, None) => state.clone(),
|
||||
(None, Some(snapshot_path), None) => {
|
||||
let snapshot_path = resolve_snapshot_path(base_dir, snapshot_path);
|
||||
let snapshot = load_runtime_snapshot_document(&snapshot_path)?;
|
||||
validate_runtime_snapshot_document(&snapshot).map_err(|err| {
|
||||
|
|
@ -49,11 +59,35 @@ fn resolve_raw_fixture_document(
|
|||
})?;
|
||||
snapshot.state
|
||||
}
|
||||
(None, None, Some(save_slice_path)) => {
|
||||
let save_slice_path = resolve_snapshot_path(base_dir, save_slice_path);
|
||||
let document = load_runtime_save_slice_document(&save_slice_path)?;
|
||||
validate_runtime_save_slice_document(&document).map_err(|err| {
|
||||
format!(
|
||||
"invalid runtime save slice document {}: {err}",
|
||||
save_slice_path.display()
|
||||
)
|
||||
})?;
|
||||
project_save_slice_to_runtime_state_import(
|
||||
&document.save_slice,
|
||||
&document.save_slice_id,
|
||||
document.source.description.clone(),
|
||||
)
|
||||
.map_err(|err| {
|
||||
format!(
|
||||
"failed to project runtime save slice document {}: {err}",
|
||||
save_slice_path.display()
|
||||
)
|
||||
})?
|
||||
.state
|
||||
}
|
||||
_ => unreachable!("state input exclusivity checked above"),
|
||||
};
|
||||
|
||||
let state_origin = match raw.state_snapshot_path {
|
||||
Some(snapshot_path) => FixtureStateOrigin::SnapshotPath(snapshot_path),
|
||||
None => FixtureStateOrigin::Inline,
|
||||
let state_origin = match (raw.state_snapshot_path, raw.state_save_slice_path) {
|
||||
(Some(snapshot_path), None) => FixtureStateOrigin::SnapshotPath(snapshot_path),
|
||||
(None, Some(save_slice_path)) => FixtureStateOrigin::SaveSlicePath(save_slice_path),
|
||||
_ => FixtureStateOrigin::Inline,
|
||||
};
|
||||
|
||||
Ok(FixtureDocument {
|
||||
|
|
@ -82,9 +116,11 @@ mod tests {
|
|||
use super::*;
|
||||
use crate::FixtureStateOrigin;
|
||||
use rrt_runtime::{
|
||||
CalendarPoint, RuntimeSaveProfileState, RuntimeServiceState, RuntimeSnapshotDocument,
|
||||
RuntimeSnapshotSource, RuntimeState, RuntimeWorldRestoreState, SNAPSHOT_FORMAT_VERSION,
|
||||
save_runtime_snapshot_document,
|
||||
CalendarPoint, RuntimeSaveProfileState, RuntimeSaveSliceDocument,
|
||||
RuntimeSaveSliceDocumentSource, RuntimeServiceState, RuntimeSnapshotDocument,
|
||||
RuntimeSnapshotSource, RuntimeState, RuntimeWorldRestoreState,
|
||||
SAVE_SLICE_DOCUMENT_FORMAT_VERSION, SNAPSHOT_FORMAT_VERSION,
|
||||
save_runtime_save_slice_document, save_runtime_snapshot_document,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
|
|
@ -167,4 +203,76 @@ mod tests {
|
|||
let _ = std::fs::remove_file(snapshot_path);
|
||||
let _ = std::fs::remove_dir(fixture_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loads_fixture_from_relative_save_slice_path() {
|
||||
let nonce = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.expect("system time should be after epoch")
|
||||
.as_nanos();
|
||||
let fixture_dir = std::env::temp_dir().join(format!("rrt-fixture-save-slice-{nonce}"));
|
||||
std::fs::create_dir_all(&fixture_dir).expect("fixture dir should be created");
|
||||
|
||||
let save_slice_path = fixture_dir.join("state-save-slice.json");
|
||||
let save_slice = RuntimeSaveSliceDocument {
|
||||
format_version: SAVE_SLICE_DOCUMENT_FORMAT_VERSION,
|
||||
save_slice_id: "save-slice-backed-fixture-state".to_string(),
|
||||
source: RuntimeSaveSliceDocumentSource {
|
||||
description: Some("test save slice".to_string()),
|
||||
original_save_filename: Some("fixture.gms".to_string()),
|
||||
original_save_sha256: None,
|
||||
notes: vec![],
|
||||
},
|
||||
save_slice: rrt_runtime::SmpLoadedSaveSlice {
|
||||
file_extension_hint: Some("gms".to_string()),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
trailer_family: None,
|
||||
bridge_family: None,
|
||||
profile: None,
|
||||
candidate_availability_table: None,
|
||||
special_conditions_table: None,
|
||||
event_runtime_collection: None,
|
||||
notes: vec![],
|
||||
},
|
||||
};
|
||||
save_runtime_save_slice_document(&save_slice_path, &save_slice)
|
||||
.expect("save slice should save");
|
||||
|
||||
let fixture_json = r#"
|
||||
{
|
||||
"format_version": 1,
|
||||
"fixture_id": "save-slice-backed-fixture",
|
||||
"source": {
|
||||
"kind": "captured-runtime"
|
||||
},
|
||||
"state_save_slice_path": "state-save-slice.json",
|
||||
"commands": [],
|
||||
"expected_summary": {
|
||||
"calendar_projection_is_placeholder": true,
|
||||
"packed_event_collection_present": false
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let fixture = load_fixture_document_from_str_with_base(fixture_json, &fixture_dir)
|
||||
.expect("save-slice-backed fixture should load");
|
||||
|
||||
assert_eq!(
|
||||
fixture.state_origin,
|
||||
FixtureStateOrigin::SaveSlicePath("state-save-slice.json".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
fixture
|
||||
.state
|
||||
.metadata
|
||||
.get("save_slice.import_projection")
|
||||
.map(String::as_str),
|
||||
Some("partial-runtime-restore-v1")
|
||||
);
|
||||
|
||||
let _ = std::fs::remove_file(save_slice_path);
|
||||
let _ = std::fs::remove_dir(fixture_dir);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@ pub struct ExpectedRuntimeSummary {
|
|||
#[serde(default)]
|
||||
pub packed_event_imported_runtime_record_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub packed_event_parity_only_record_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub packed_event_unsupported_record_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub event_runtime_record_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub candidate_availability_count: Option<usize>,
|
||||
|
|
@ -341,6 +345,22 @@ impl ExpectedRuntimeSummary {
|
|||
));
|
||||
}
|
||||
}
|
||||
if let Some(count) = self.packed_event_parity_only_record_count {
|
||||
if actual.packed_event_parity_only_record_count != count {
|
||||
mismatches.push(format!(
|
||||
"packed_event_parity_only_record_count mismatch: expected {count}, got {}",
|
||||
actual.packed_event_parity_only_record_count
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(count) = self.packed_event_unsupported_record_count {
|
||||
if actual.packed_event_unsupported_record_count != count {
|
||||
mismatches.push(format!(
|
||||
"packed_event_unsupported_record_count mismatch: expected {count}, got {}",
|
||||
actual.packed_event_unsupported_record_count
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(count) = self.event_runtime_record_count {
|
||||
if actual.event_runtime_record_count != count {
|
||||
mismatches.push(format!(
|
||||
|
|
@ -510,6 +530,7 @@ pub struct FixtureDocument {
|
|||
pub enum FixtureStateOrigin {
|
||||
Inline,
|
||||
SnapshotPath(String),
|
||||
SaveSlicePath(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
|
@ -523,6 +544,8 @@ pub struct RawFixtureDocument {
|
|||
#[serde(default)]
|
||||
pub state_snapshot_path: Option<String>,
|
||||
#[serde(default)]
|
||||
pub state_save_slice_path: Option<String>,
|
||||
#[serde(default)]
|
||||
pub commands: Vec<StepCommand>,
|
||||
#[serde(default)]
|
||||
pub expected_summary: ExpectedRuntimeSummary,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue