Update tests, examples, benches

This commit is contained in:
TechnoPorg committed 2026-01-22 00:30:48 +01:00
1 parent bacac903b3
commit 85ccd7e507
89 files changed
+942 -1785

No files matched your search

+40 -39
View File
@@ -1,23 +1,24 @@
// Basic execution tests for DagRunner
use crate::common::task_fn;
use futures::FutureExt;
use dagx::*;
#[tokio::test]
async fn test_empty_dag() {
let dag = DagRunner::new();
dag.run()
.await
.unwrap(); // Should complete without error
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap(); // Should complete without error
}
#[tokio::test]
async fn test_single_task() {
let dag = DagRunner::new();
let task = dag.add_task(task_fn(|_: ()| async { 42 }));
dag.run()
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(dag.get(task).unwrap(), 42);
}
@@ -39,9 +40,9 @@ async fn test_deep_chain() {
.add_task(task_fn(|x: i32| async move { x + 1 }))
.depends_on(d);
dag.run()
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Note: a, b, c, d are not sinks (they have dependents in the chain)
// Only e is a sink and can be retrieved
@@ -56,9 +57,9 @@ async fn test_wide_parallel() {
.map(|i| dag.add_task(task_fn(move |_: ()| async move { i * 2 })))
.collect();
dag.run()
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
for (i, task) in tasks.iter().enumerate() {
assert_eq!(dag.get(task).unwrap(), i * 2);
@@ -80,9 +81,9 @@ async fn test_diamond_dependency() {
.add_task(task_fn(|(x, y): (i32, i32)| async move { x + y }))
.depends_on((&b, &c));
dag.run()
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Note: a, b, and c are not sinks (they have dependents)
// Only d is a sink and can be retrieved
@@ -108,9 +109,9 @@ async fn test_different_output_types() {
}
}));
dag.run()
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(&dag.get(string_task).unwrap(), "hello");
assert_eq!(dag.get(vec_task).unwrap(), vec![1, 2, 3]);
@@ -141,9 +142,9 @@ async fn test_multiple_sinks() {
.add_task(task_fn(|x: i32| async move { x * 3 }))
.depends_on(branch2);
dag.run()
.await
.unwrap(); // Should wait for both sinks
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap(); // Should wait for both sinks
assert_eq!(dag.get(sink1).unwrap(), 21); // (10 * 2) + 1
assert_eq!(dag.get(sink2).unwrap(), 45); // (10 + 5) * 3
@@ -163,9 +164,9 @@ async fn test_single_task_inline_path() {
.add_task(task_fn(|x: i32| async move { x + 5 }))
.depends_on(t2);
dag.run()
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(dag.get(t3).unwrap(), 25); // (10 * 2) + 5
}
@@ -188,9 +189,9 @@ async fn test_multi_task_spawn_path() {
.add_task(task_fn(|x: i32| async move { x + 3 }))
.depends_on(&source);
dag.run()
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(dag.get(t1).unwrap(), 101);
assert_eq!(dag.get(t2).unwrap(), 102);
@@ -212,9 +213,9 @@ async fn test_producer_consumer_channels() {
))
.depends_on(&producer);
dag.run()
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(dag.get(consumer).unwrap(), 15);
}
@@ -237,9 +238,9 @@ async fn test_multi_consumer_fanout() {
.add_task(task_fn(|x: i32| async move { x - 5 }))
.depends_on(&producer);
dag.run()
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(dag.get(c1).unwrap(), 84);
assert_eq!(dag.get(c2).unwrap(), 52);
@@ -265,9 +266,9 @@ async fn test_compute_layers_with_dependents_tracking() {
.add_task(task_fn(|(l, r): (i32, i32)| async move { l + r }))
.depends_on((&left, &right));
dag.run()
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(dag.get(sink).unwrap(), 5); // (1+1) + (1+2) = 5
}
@@ -313,9 +314,9 @@ async fn test_layer_execution_order() {
}))
.depends_on(t2);
dag.run()
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
let order = execution_order.lock().clone();
assert_eq!(order, vec![1, 2, 3]);
+13 -20
View File
@@ -3,6 +3,7 @@ use crate::common::task_fn;
use crate::common::tasks::*;
use dagx::*;
use futures::FutureExt;
#[tokio::test]
async fn test_example_a_fan_out() {
@@ -12,11 +13,9 @@ async fn test_example_a_fan_out() {
let plus1 = dag.add_task(Inc::new()).depends_on(&base); // 11
let plus2 = dag.add_task(Inc::new()).depends_on(&base); // 11
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap(); // SPEC API with spawner
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap(); // SPEC API with spawner
assert_eq!(dag.get(plus1).unwrap(), 11);
assert_eq!(dag.get(plus2).unwrap(), 11);
@@ -32,11 +31,9 @@ async fn test_example_b_fan_in() {
let output = dag.add_task(OutputResult::new()).depends_on((&s, &n, &f));
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(&dag.get(output).unwrap(), "hi:42:true");
}
@@ -52,11 +49,9 @@ async fn test_example_c_many_to_many() {
let prod_yz = dag.add_task(Mul::new()).depends_on((&y, &z)); // 15
let total = dag.add_task(Add::new()).depends_on((&sum_xy, &prod_yz)); // 20
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(dag.get(total).unwrap(), 20);
}
@@ -76,11 +71,9 @@ async fn test_single_input_forms() {
.add_task(task_fn(|s: String| async move { s.to_uppercase() }))
.depends_on((&s,));
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(&dag.get(upper1).unwrap(), "HELLO");
assert_eq!(&dag.get(upper2).unwrap(), "HELLO");
+34 -27
View File
@@ -1,7 +1,8 @@
//! Integration tests for end-to-end functionality
use dagx::{DagResult, DagRunner, Task};
use crate::common::task_fn;
use dagx::{DagResult, DagRunner, Task};
use futures::FutureExt;
use std::time::Instant;
use tokio::time::{sleep, Duration};
@@ -18,7 +19,7 @@ impl LoadValue {
#[dagx::task]
impl LoadValue {
async fn run(&self) -> i32 {
self.value // Read-only state: use &self
self.value // Read-only state: use &self
}
}
@@ -33,7 +34,7 @@ impl Multiply {
#[dagx::task]
impl Multiply {
async fn run(&self, input: &i32) -> i32 {
input * self.factor // Read-only state: use &self
input * self.factor // Read-only state: use &self
}
}
@@ -41,7 +42,7 @@ struct Add;
#[dagx::task]
impl Add {
async fn run(a: &i32, b: &i32) -> i32 {
a + b // Stateless: no self needed
a + b // Stateless: no self needed
}
}
@@ -67,7 +68,7 @@ async fn integration_test_etl_pipeline() -> DagResult<()> {
let final_result = dag.add_task(Add).depends_on((&combine12, &transform3));
// Execute pipeline
dag.run(|fut| { tokio::spawn(fut); }).await?;
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
// Verify
let result = dag.get(final_result)?;
@@ -97,7 +98,7 @@ async fn integration_test_multi_layer_dag() -> DagResult<()> {
// Layer 4: Final result
let final_result = dag.add_task(Add).depends_on((&double_ab, &double_bc));
dag.run(|fut| { tokio::spawn(fut); }).await?;
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
let result = dag.get(final_result)?;
// (1+2)*2 + (2+3)*2 = 6 + 10 = 16
@@ -118,7 +119,7 @@ async fn integration_test_multiple_sinks() -> DagResult<()> {
let branch2 = dag.add_task(Multiply::new(3)).depends_on(&source);
let branch3 = dag.add_task(Multiply::new(4)).depends_on(&source);
dag.run(|fut| { tokio::spawn(fut); }).await?;
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
// All branches should complete
assert_eq!(dag.get(branch1)?, 20);
@@ -144,7 +145,7 @@ async fn integration_test_diamond_cascade() -> DagResult<()> {
let right2 = dag.add_task(Multiply::new(3)).depends_on(sink1);
let sink2 = dag.add_task(Add).depends_on((&left2, &right2));
dag.run(|fut| { tokio::spawn(fut); }).await?;
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
let result = dag.get(sink2)?;
// Diamond 1: (10*2) + (10*3) = 50
@@ -175,7 +176,7 @@ async fn integration_test_wide_and_deep() -> DagResult<()> {
final_nodes.push(step3);
}
dag.run(|fut| { tokio::spawn(fut); }).await?;
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
// Verify each chain
for (i, node) in final_nodes.iter().enumerate() {
@@ -194,7 +195,7 @@ async fn integration_test_error_recovery() -> DagResult<()> {
let source = dag.add_task(LoadValue::new(42));
let transform = dag.add_task(Multiply::new(2)).depends_on(&source);
dag.run(|fut| { tokio::spawn(fut); }).await?;
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
// Should be able to access results
assert_eq!(dag.get(transform)?, 84);
@@ -230,7 +231,7 @@ async fn integration_test_large_scale() -> DagResult<()> {
})
.collect();
dag.run(|fut| { tokio::spawn(fut); }).await?;
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
// Verify some results
assert_eq!(dag.get(aggregations[0])?, 1); // First two
@@ -254,7 +255,7 @@ async fn integration_test_reused_values() -> DagResult<()> {
// Combine some of the results
let combined = dag.add_task(Add).depends_on((&double, &triple));
dag.run(|fut| { tokio::spawn(fut); }).await?;
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
assert_eq!(dag.get(double)?, 200);
assert_eq!(dag.get(triple)?, 300);
@@ -283,7 +284,7 @@ async fn integration_test_parallel_execution_timing() -> DagResult<()> {
.collect();
let start = Instant::now();
dag.run(|fut| { tokio::spawn(fut); }).await?;
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
let elapsed = start.elapsed();
// Verify all tasks completed
@@ -322,23 +323,29 @@ async fn integration_test_parallel_vs_sequential_timing() -> DagResult<()> {
// Layer 2: Sequential chain of 3 tasks depending on the first parallel task
// (should add ~150ms)
let seq1 = dag.add_task(task_fn(move |x: i32| async move {
sleep(sleep_duration).await;
x + 1
})).depends_on(&parallel_tasks[0]);
let seq1 = dag
.add_task(task_fn(move |x: i32| async move {
sleep(sleep_duration).await;
x + 1
}))
.depends_on(&parallel_tasks[0]);
let seq2 = dag.add_task(task_fn(move |x: i32| async move {
sleep(sleep_duration).await;
x + 1
})).depends_on(seq1);
let seq2 = dag
.add_task(task_fn(move |x: i32| async move {
sleep(sleep_duration).await;
x + 1
}))
.depends_on(seq1);
let seq3 = dag.add_task(task_fn(move |x: i32| async move {
sleep(sleep_duration).await;
x + 1
})).depends_on(seq2);
let seq3 = dag
.add_task(task_fn(move |x: i32| async move {
sleep(sleep_duration).await;
x + 1
}))
.depends_on(seq2);
let start = Instant::now();
dag.run(|fut| { tokio::spawn(fut); }).await?;
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
let elapsed = start.elapsed();
// Verify results
@@ -361,4 +368,4 @@ async fn integration_test_parallel_vs_sequential_timing() -> DagResult<()> {
);
Ok(())
}
}
+13 -25
View File
@@ -3,6 +3,7 @@ use crate::common::task_fn;
use crate::common::tasks::Counter;
use dagx::*;
use futures::FutureExt;
#[tokio::test]
async fn test_result_type() {
@@ -13,11 +14,9 @@ async fn test_result_type() {
Err::<i32, String>("error".to_string())
}));
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(dag.get(success_task).unwrap(), Ok(42));
assert_eq!(dag.get(error_task).unwrap(), Err("error".to_string()));
@@ -30,10 +29,7 @@ async fn test_dag_result_type_works() {
// Test that DagResult<()> works with ? operator pattern
async fn run_dag(dag: &DagRunner) -> DagResult<()> {
dag.run(|fut| {
tokio::spawn(fut);
})
.await?;
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
Ok(())
}
@@ -68,7 +64,7 @@ async fn test_error_messages_are_helpful() {
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("Result not found"));
assert!(err_msg.contains("task #0")); // Task ID
assert!(err_msg.contains("Call dag.run()"));
assert!(err_msg.contains("Call dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))"));
}
#[tokio::test]
@@ -77,11 +73,7 @@ async fn test_successful_execution_returns_ok() {
let task = dag.add_task(task_fn(|_: ()| async { 42 }));
// Should return Ok(())
let result = dag
.run(|fut| {
tokio::spawn(fut);
})
.await;
let result = dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await;
assert!(result.is_ok());
// Should be able to get value
@@ -95,11 +87,9 @@ async fn test_stateful_tasks() {
let input = dag.add_task(task_fn(|_: ()| async { 5 }));
let counter = dag.add_task(Counter::new(10)).depends_on(&input);
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(dag.get(counter).unwrap(), 15); // 10 + 5
}
@@ -117,11 +107,9 @@ async fn test_task_fn_with_captured_state() {
.add_task(task_fn(move |x: i32| async move { x * multiplier }))
.depends_on(&base);
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(dag.get(scaled).unwrap(), 35);
}