sandl 0.1.0

A framework for building parallel execution engines with dependency management, type-safe method dispatch, and event observation.
Documentation
use std::sync::{
    Arc, Mutex,
    atomic::{AtomicUsize, Ordering},
};

use sandl::*;

#[test]
fn observer_callbacks() {
    let mut observer = Observer::new();
    let events = Arc::new(Mutex::new(Vec::new()));

    let e = events.clone();
    observer.on_slice_start(move |slice| {
        e.lock().unwrap().push(format!("slice_start:{}", slice));
    });

    let e = events.clone();
    observer.on_method_start(move |slice, layer, method| {
        e.lock()
            .unwrap()
            .push(format!("method_start:{}:{}:{}", slice, layer, method));
    });

    observer.emit(EngineEvent::SliceStart {
        slice: "s1".to_string(),
    });
    observer.emit(EngineEvent::MethodStart {
        slice: "s1".to_string(),
        layer: "l1".to_string(),
        method: "m1".to_string(),
    });

    let recorded = events.lock().unwrap();
    assert_eq!(recorded.len(), 2);
    assert_eq!(recorded[0], "slice_start:s1");
    assert_eq!(recorded[1], "method_start:s1:l1:m1");
}

#[test]
fn observer_method_start() {
    let layer = quick_layer!("layer", "work", Value, |_args, _ctx| { Ok(value!({})) });

    let slice = Slice::builder("test")
        .layer("layer", |m| m.call_default("work"))
        .build();

    let called = Arc::new(AtomicUsize::new(0));
    let c = called.clone();

    let engine = Engine::builder()
        .add_layer(layer)
        .add_slice(slice)
        .observe(move |observer| {
            observer.on_method_start(move |_, _, _| {
                c.fetch_add(1, Ordering::SeqCst);
            });
        })
        .build()
        .unwrap();

    engine.run(RunFlags::SILENT);

    assert_eq!(called.load(Ordering::SeqCst), 1);
}

#[test]
fn observer_method_complete() {
    let layer = quick_layer!("layer", "work", Value, |_args, _ctx| { Ok(value!({})) });

    let slice = Slice::builder("test")
        .layer("layer", |m| m.call_default("work"))
        .build();

    let called = Arc::new(AtomicUsize::new(0));
    let c = called.clone();

    let engine = Engine::builder()
        .add_layer(layer)
        .add_slice(slice)
        .observe(move |observer| {
            observer.on_method_complete(move |_, _, _, _| {
                println!("HEREEEEEEEEEEEEEEEEEEEEEEEEEEE");
                c.fetch_add(1, Ordering::SeqCst);
            });
        })
        .build()
        .unwrap();

    engine.run(RunFlags::SILENT);

    assert_eq!(called.load(Ordering::SeqCst), 1);
}

#[test]
fn observer_method_failed() {
    let layer = quick_layer!("layer", "work", Value, |_args, _ctx| {
        Err(Error::ExecutionError("test error".to_string()))
    });

    let slice = Slice::builder("test")
        .layer("layer", |m| m.call_default("work"))
        .build();

    let errors = Arc::new(Mutex::new(Vec::new()));
    let e = errors.clone();

    let engine = Engine::builder()
        .add_layer(layer)
        .add_slice(slice)
        .observe(move |observer| {
            observer.on_method_failed(move |slice, layer, method, error| {
                e.lock()
                    .unwrap()
                    .push(format!("{}:{}:{}:{}", slice, layer, method, error));
            });
        })
        .build()
        .unwrap();

    engine.run(RunFlags::SILENT);

    let errors = errors.lock().unwrap();
    assert_eq!(errors.len(), 1);
}

#[test]
fn observer_slice_lifecycle() {
    let layer = quick_layer!("layer", "work", Value, |_args, _ctx| { Ok(value!({})) });

    let slice = Slice::builder("test")
        .layer("layer", |m| m.call_default("work"))
        .build();

    let events = Arc::new(Mutex::new(Vec::new()));
    let e = events.clone();

    let engine = Engine::builder()
        .add_layer(layer)
        .add_slice(slice)
        .observe(move |observer| {
            let e1 = e.clone();
            observer.on_slice_start(move |slice| {
                e1.lock().unwrap().push(format!("start:{}", slice));
            });

            let e2 = e.clone();
            observer.on_slice_complete(move |slice, _| {
                e2.lock().unwrap().push(format!("complete:{}", slice));
            });
        })
        .build()
        .unwrap();

    engine.run(RunFlags::SILENT);

    let events = events.lock().unwrap();
    assert_eq!(events.len(), 2);
    assert_eq!(events[0], "start:test");
    assert_eq!(events[1], "complete:test");
}

#[test]
fn observer_multiple_slices() {
    let layer = quick_layer!("layer", "work", Value, |_args, _ctx| { Ok(value!({})) });

    let s1 = Slice::builder("s1")
        .layer("layer", |m| m.call_default("work"))
        .build();
    let s2 = Slice::builder("s2")
        .layer("layer", |m| m.call_default("work"))
        .build();

    let count = Arc::new(AtomicUsize::new(0));
    let c = count.clone();

    let engine = Engine::builder()
        .add_layer(layer)
        .add_slice(s1)
        .add_slice(s2)
        .observe(move |observer| {
            observer.on_method_complete(move |_, _, _, _| {
                c.fetch_add(1, Ordering::SeqCst);
            });
        })
        .build()
        .unwrap();

    engine.run(RunFlags::SILENT);

    assert_eq!(count.load(Ordering::SeqCst), 2);
}