statechart 0.0.8

statecharts: hierarchical, reactive state machines
Documentation
#[cfg(test)]

use std::collections::HashMap;

extern crate env_logger;
extern crate statechart;
use statechart::*;

#[test]
fn blocked_indefinitely() {
    let st = state!{ S {} };
    let ctx = Context::new(st);
    let mut it = Interpreter::new();
    let result = it.run(&ctx);
    assert_eq!(result, Err(Fault::BlockedIndefinitely));
}

#[test]
fn transitions() {
    let ctx = Context::new(c123());
    let st = ctx.root();
    assert_eq!(st.node().id(), &vec![]);
    assert_eq!(st.node().substate("S1").unwrap().node().id(), &vec![0]);
    assert_eq!(st.node().substate("S2").unwrap().node().id(), &vec![1]);
    assert_eq!(st.node().substate("S3").unwrap().node().id(), &vec![2]);
    assert_eq!(st.node().substate("nonesuch"), None);
    assert_eq!(ctx.state_by_id(&vec![0]).unwrap().node().id(), &vec![0]);
    assert_eq!(ctx.state_by_id(&vec![1]).unwrap().node().id(), &vec![1]);
    assert_eq!(ctx.state_by_id(&vec![2]).unwrap().node().id(), &vec![2]);
}

#[test]
fn goto() {
    let g = goto!(target: S1, topics: ["foo", "bar", "baz"]);
    assert_eq!(g,
               TransitionBuilder::default()
                   .target_label(Some("S1".to_string()))
                   .topics(["foo", "bar", "baz"].iter().map(|x| x.to_string()).collect())
                   .build()
                   .unwrap());
}

#[test]
fn context() {
    let ctx = Context::new(c123());
    assert_eq!(ctx.state("S1").unwrap().node().label(), "S1");
    for ss in vec!["S1", "S2", "S3"] {
        assert_eq!(ctx.state_by_id(ctx.state(ss).unwrap().node().parent().unwrap()).unwrap().node().label(),
                   "S");
    }
    assert_eq!(ctx.state("S").unwrap().node().parent(), None);
    let mut it = Interpreter::new();
    let result = it.run(&ctx);
    assert!(result.is_ok(), "{:?}", result.err().unwrap());
    assert_eq!(result.unwrap(), Value::Object(HashMap::new()));
}

#[test]
fn parallel() {
    let ctx = Context::new(phello());
    let mut it = Interpreter::new();
    let result = it.run(&ctx);
    assert!(result.is_ok(), "{:?}", result.err().unwrap());
    assert_eq!(result.unwrap(), Value::Object(HashMap::new()));
}

#[test]
fn parallel_final() {
    let ctx = Context::new(phellofinal());
    let mut it = Interpreter::new();
    let result = it.run(&ctx);
    assert!(result.is_ok(), "{:?}", result.err().unwrap());
    assert_eq!(result.unwrap(), Value::Object(HashMap::new()));
}

#[test]
fn parallel_swap() {
    let ctx = Context::new(pswap());
    let mut it = Interpreter::new();
    for _ in 0..100 {
        let result = it.step(&ctx);
        assert_eq!(result, Ok(Status::Runnable));
    }
}

fn c123() -> State {
    let _ = env_logger::init();
    states!{ S {
        substates: [
            state!{ S1 {
                transitions: [goto!(target: S2)],
                on_entry: [action_log!(message: "hello s1")],
            }},
            state!{ S2 {
                transitions: [goto!(target: S3)],
                on_entry: [action_log!(message: "hello s2")],
            }},
            final_state!{ S3 {
                on_entry: [action_log!(message: "hello s3")],
                on_exit: [action_log!(message: "and goodbye now")],
            }},
        ]}}
}

fn phello() -> State {
    let _ = env_logger::init();
    states!{ S {
        substates: [
            parallel!{ P  {
                substates: [
                    state!{ S1 {
                        transitions: [goto!(target: SF)],
                        on_entry: [action_log!(message: "hello s1")],
                    }},
                    state!{ S2 {
                        on_entry: [action_log!(message: "hello s2")],
                    }},
                    state!{ S3 {
                        on_entry: [action_log!(message: "hello s3")],
                    }},
                ]}},
            final_state!{ SF {
                on_exit: [action_log!(message: "goodbye now")],
            }},
        ]}}
}

fn phellofinal() -> State {
    let _ = env_logger::init();
    states!{ S {
        substates: [
            parallel!{ P  {
                substates: [
                    state!{ S1 {
                        on_entry: [action_log!(message: "hello s1")],
                    }},
                    state!{ S2 {
                        on_entry: [action_log!(message: "hello s2")],
                    }},
                    state!{ S3 {
                        on_entry: [action_log!(message: "hello s3")],
                    }},
                    final_state!{ SF {
                        on_entry: [action_log!(message: "hello sf")],
                        on_exit: [action_log!(message: "goodbye now")],
                    }},
            ]}},
        ]}}
}

fn pswap() -> State {
    let _ = env_logger::init();
    states!{ S {
        substates: [
            parallel!{ P {
                substates: [
                    state!{ S1 {
                        on_entry: [action_log!(message: "s1 wants to be an s2")],
                        transitions: [goto!(target: S2)],
                    }},
                    state!{ S2 {
                        on_entry: [action_log!(message: "s2 wants to be an s1")],
                        transitions: [goto!(target: S1)],
                    }},
                ]}},
            ]}}
}