#[path = "support/runtime.rs"]
mod runtime_support;
mod support;
use rsaeb::input::RunSeed;
use rsaeb::inspect::OnceRuleCount;
use rsaeb::limits::{
DEFAULT_MAX_INPUT_LEN, DEFAULT_MAX_RETURN_LEN, DEFAULT_MAX_STATE_LEN, DEFAULT_MAX_STEPS,
DEFAULT_PARSE_LIMITS, StepLimit,
};
use rsaeb::program::{Program, RunOutcome, RunResult};
use rsaeb::source::ProgramSource;
use runtime_support::TestRunPolicy;
use support::{TestFailure, TestResult, ensure_eq, ensure_matches, parse_program};
fn expect_stable_bytes<'result>(
result: &'result RunResult,
expected: &[u8],
) -> Result<&'result [u8], TestFailure> {
match result.outcome() {
RunOutcome::Stable(output) if output.as_slice() == expected => Ok(output.as_slice()),
RunOutcome::Stable(_) => Err(TestFailure::message("stable output bytes differed")),
RunOutcome::Return(_) => Err(TestFailure::message("expected stable outcome")),
}
}
fn expect_return_bytes<'result>(
result: &'result RunResult,
expected: &[u8],
) -> Result<&'result [u8], TestFailure> {
match result.outcome() {
RunOutcome::Return(output) if output.as_slice() == expected => Ok(output.as_slice()),
RunOutcome::Return(_) => Err(TestFailure::message("return output bytes differed")),
RunOutcome::Stable(_) => Err(TestFailure::message("expected return outcome")),
}
}
fn runtime_input(bytes: &[u8], limits: TestRunPolicy) -> Result<RunSeed, TestFailure> {
runtime_support::run_seed(bytes, limits)
}
#[test]
fn program_parse_accepts_text_and_byte_sources() -> TestResult {
let limits = TestRunPolicy::new(
DEFAULT_MAX_INPUT_LEN,
DEFAULT_MAX_STEPS,
DEFAULT_MAX_STATE_LEN,
DEFAULT_MAX_RETURN_LEN,
);
let program = parse_program("a=b")?;
let input = runtime_input(b"a", limits)?;
let result = program.run(input)?;
expect_stable_bytes(&result, b"b")?;
ensure_matches(result.steps().get() == 1, "expected one execution step")?;
let program = Program::parse(ProgramSource::from_bytes(b"a=b#\xff"), DEFAULT_PARSE_LIMITS)?;
let input = runtime_input(b"a", limits)?;
let result = program.run(input)?;
expect_stable_bytes(&result, b"b")?;
Ok(())
}
#[test]
fn program_language_surface_handles_spacing_comments_and_actions() -> TestResult {
let limits = TestRunPolicy::new(
DEFAULT_MAX_INPUT_LEN,
StepLimit::new(10_000),
DEFAULT_MAX_STATE_LEN,
DEFAULT_MAX_RETURN_LEN,
);
let program = parse_program("a b=bb")?;
let result = program.run(runtime_input(b"abc", limits)?)?;
expect_stable_bytes(&result, b"bbc")?;
let program = parse_program("a=b\r\nb=c\r\n")?;
let result = program.run(runtime_input(b"a", limits)?)?;
expect_stable_bytes(&result, b"c")?;
let program = parse_program("a\tb = c\tc")?;
let result = program.run(runtime_input(b"ab", limits)?)?;
expect_stable_bytes(&result, b"cc")?;
let program = parse_program("a=b#ignored")?;
let result = program.run(runtime_input(b"a", limits)?)?;
expect_stable_bytes(&result, b"b")?;
let program = parse_program("#a=b")?;
let result = program.run(runtime_input(b"a", limits)?)?;
expect_stable_bytes(&result, b"a")?;
let program = parse_program("a=(start)x")?;
let result = program.run(runtime_input(b"ba", limits)?)?;
expect_stable_bytes(&result, b"xb")?;
let program = parse_program("a=(end)x")?;
let result = program.run(runtime_input(b"ba", limits)?)?;
expect_stable_bytes(&result, b"bx")?;
let program = parse_program("a=(return)ok")?;
let result = program.run(runtime_input(b"a", limits)?)?;
expect_return_bytes(&result, b"ok")?;
Ok(())
}
#[test]
fn program_values_are_reusable_across_runs() -> TestResult {
let limits = TestRunPolicy::new(
DEFAULT_MAX_INPUT_LEN,
StepLimit::new(10_000),
DEFAULT_MAX_STATE_LEN,
DEFAULT_MAX_RETURN_LEN,
);
let program = parse_program("(once)a=b\na=c")?;
let first = program.run(runtime_input(b"aa", limits)?)?;
let second = program.run(runtime_input(b"aa", limits)?)?;
expect_stable_bytes(&first, b"bc")?;
expect_stable_bytes(&second, b"bc")?;
ensure_eq!(program.rule_count().get(), 2)?;
let once_rules: OnceRuleCount = program.once_rule_count();
ensure_eq!(once_rules.get(), 1)
}