use brink_runtime::{FastRng, Step, Story};
#[expect(clippy::unwrap_used)]
fn story_from_source(src: &str) -> Story<FastRng> {
let data = brink_compiler::compile("main.ink", |_p| Ok(src.to_owned()))
.unwrap()
.data;
let (program, line_tables) = brink_runtime::link(&data).unwrap();
Story::new(std::sync::Arc::new(program), line_tables)
}
#[test]
fn adjacent_lines_in_one_run_share_a_block_id() {
let mut story = story_from_source("One.\nTwo.\n-> END\n");
let steps = story.continue_maximally().expect("drive to END");
let lines: Vec<_> = steps
.iter()
.filter_map(|s| match s {
Step::Line(line) => Some(line),
_ => None,
})
.collect();
assert_eq!(lines.len(), 2, "{steps:?}");
assert_eq!(lines[0].text, "One.\n");
assert_eq!(lines[1].text, "Two.\n");
assert_eq!(
lines[0].block_id, lines[1].block_id,
"lines from one uninterrupted run must share a block_id: {steps:?}"
);
}
#[test]
fn block_id_changes_after_a_choice() {
let mut story = story_from_source(
"-> start\n\
=== start ===\n\
Before.\n\
* [left] Went left.\n-> END\n\
* [right] Went right.\n-> END\n",
);
let before_steps = story.continue_maximally().expect("drive to choices");
let before_id = before_steps
.iter()
.find_map(|s| match s {
Step::Line(line) => Some(line.block_id),
_ => None,
})
.expect("a line before the choices");
assert!(
matches!(before_steps.last(), Some(Step::Choices(_))),
"{before_steps:?}"
);
story.choose(0).expect("choose left");
let after_steps = story.continue_maximally().expect("drive from the choice");
let after_id = after_steps
.iter()
.find_map(|s| match s {
Step::Line(line) => Some(line.block_id),
_ => None,
})
.expect("a line after the choice");
assert_ne!(
before_id, after_id,
"the chosen branch must start a fresh block_id: before={before_steps:?} after={after_steps:?}"
);
}
#[test]
fn block_id_changes_after_a_done_resume() {
let mut story = story_from_source("One.\n-> DONE\nTwo.\n-> END\n");
let first_steps = story.continue_maximally().expect("drive to Done");
assert!(
matches!(first_steps.last(), Some(Step::Done)),
"{first_steps:?}"
);
let first_id = first_steps
.iter()
.find_map(|s| match s {
Step::Line(line) => Some(line.block_id),
_ => None,
})
.expect("a line before Done");
let second_steps = story.continue_maximally().expect("drive past Done");
let second_id = second_steps
.iter()
.find_map(|s| match s {
Step::Line(line) => Some(line.block_id),
_ => None,
})
.expect("a line after resuming from Done");
assert_ne!(
first_id, second_id,
"resuming past Done must start a fresh block_id: first={first_steps:?} second={second_steps:?}"
);
}
#[test]
fn block_id_changes_after_choose_path_string() {
let mut story = story_from_source(
"Hello.\n-> END\n\
=== elsewhere ===\n\
Elsewhere.\n-> DONE\n",
);
let before_steps = story.continue_maximally().expect("drive to END");
let before_id = before_steps
.iter()
.find_map(|s| match s {
Step::Line(line) => Some(line.block_id),
_ => None,
})
.expect("a line before the jump");
story.choose_path_string("elsewhere").expect("jump");
let after_steps = story.continue_maximally().expect("drive from the jump");
let after_id = after_steps
.iter()
.find_map(|s| match s {
Step::Line(line) => Some(line.block_id),
_ => None,
})
.expect("a line after the jump");
assert_ne!(
before_id, after_id,
"the jump target must start a fresh block_id: before={before_steps:?} after={after_steps:?}"
);
}