Update tests, examples, benches

This commit is contained in:
TechnoPorg committed 2026-02-11 22:01:50 +01:00
1 parent 0137296fb4
commit 86787b3a0d
71 files changed
+1791 -3547

No files matched your search

+33 -35
View File
@@ -11,12 +11,12 @@ async fn test_1000_level_deep_chain() -> DagResult<()> {
// Test a linear chain of 1000 tasks
let dag = DagRunner::new();
let first = dag.add_task(task_fn(|_: ()| async { 0 }));
let first = dag.add_task(task_fn::<(), _, _>(|_: ()| 0));
let mut current = first.into(); // Convert to TaskHandle
for _i in 1..1000 {
current = dag
.add_task(task_fn(move |prev: i32| async move { prev + 1 }))
.add_task(task_fn::<i32, _, _>(move |&prev: &i32| prev + 1))
.depends_on(current);
}
@@ -31,20 +31,20 @@ async fn test_deep_chain_with_branches() -> DagResult<()> {
// Test a deep chain where each node has a side branch
let dag = DagRunner::new();
let first = dag.add_task(task_fn(|_: ()| async { 0 }));
let first = dag.add_task(task_fn::<(), _, _>(|_: ()| 0));
let mut main_chain: dagx::TaskHandle<i32> = first.into();
let mut branches = Vec::new();
for i in 1..500 {
// Main chain continues
main_chain = dag
.add_task(task_fn(move |prev: i32| async move { prev + 1 }))
.add_task(task_fn::<i32, _, _>(move |&prev: &i32| prev + 1))
.depends_on(main_chain);
// Create a branch at every 10th node
if i % 10 == 0 {
let branch = dag
.add_task(task_fn(move |main_val: i32| async move { main_val * 2 }))
.add_task(task_fn::<i32, _, _>(move |main_val: &i32| main_val * 2))
.depends_on(main_chain);
branches.push(branch);
}
@@ -71,12 +71,12 @@ async fn test_multiple_parallel_deep_chains() -> DagResult<()> {
let mut chain_ends = Vec::new();
for chain_id in 0..8 {
let first = dag.add_task(task_fn(move |_: ()| async move { chain_id * 1000 }));
let first = dag.add_task(task_fn::<(), _, _>(move |_: ()| chain_id * 1000));
let mut current: dagx::TaskHandle<i32> = first.into();
for _ in 0..500 {
current = dag
.add_task(task_fn(|prev: i32| async move { prev + 1 }))
.add_task(task_fn::<i32, _, _>(|&prev: &i32| prev + 1))
.depends_on(current);
}
@@ -85,8 +85,8 @@ async fn test_multiple_parallel_deep_chains() -> DagResult<()> {
// Add a final task that depends on all chains
let final_task = dag
.add_task(task_fn(
|inputs: (i32, i32, i32, i32, i32, i32, i32, i32)| async move {
.add_task(task_fn::<(i32, i32, i32, i32, i32, i32, i32, i32), _, _>(
|inputs: (&i32, &i32, &i32, &i32, &i32, &i32, &i32, &i32)| {
inputs.0
+ inputs.1
+ inputs.2
@@ -126,28 +126,24 @@ async fn test_deep_chain_execution_order() -> DagResult<()> {
let dag = DagRunner::new();
let order = Arc::new(AtomicUsize::new(0));
let first = dag.add_task(task_fn({
let first = dag.add_task(task_fn::<(), _, _>({
let order = order.clone();
move |_: ()| {
let order = order.clone();
async move {
assert_eq!(order.fetch_add(1, Ordering::SeqCst), 0);
0
}
assert_eq!(order.fetch_add(1, Ordering::SeqCst), 0);
0
}
}));
let mut current: dagx::TaskHandle<i32> = first.into();
for expected in 1..100 {
current = dag
.add_task(task_fn({
.add_task(task_fn::<i32, _, _>({
let order = order.clone();
move |prev: i32| {
move |&prev: &i32| {
let order = order.clone();
async move {
assert_eq!(order.fetch_add(1, Ordering::SeqCst), expected);
prev + 1
}
assert_eq!(order.fetch_add(1, Ordering::SeqCst), expected);
prev + 1
}
}))
.depends_on(current);
@@ -168,14 +164,16 @@ async fn test_deep_binary_tree() -> DagResult<()> {
fn build_tree(dag: &DagRunner, depth: usize, value: i32) -> dagx::TaskHandle<i32> {
if depth == 0 {
let task = dag.add_task(task_fn(move |_: ()| async move { value }));
let task = dag.add_task(task_fn::<(), _, _>(move |_: ()| value));
task.into() // Convert TaskBuilder to TaskHandle
} else {
let left = build_tree(dag, depth - 1, value * 2);
let right = build_tree(dag, depth - 1, value * 2 + 1);
dag.add_task(task_fn(move |(l, r): (i32, i32)| async move { l + r }))
.depends_on((&left, &right))
dag.add_task(task_fn::<(i32, i32), _, _>(move |(l, r): (&i32, &i32)| {
l + r
}))
.depends_on((&left, &right))
}
}
@@ -196,15 +194,15 @@ async fn test_fibonacci_chain() -> DagResult<()> {
// Test a Fibonacci-like dependency pattern
let dag = DagRunner::new();
let f0_builder = dag.add_task(task_fn(|_: ()| async { 0i64 }));
let f1_builder = dag.add_task(task_fn(|_: ()| async { 1i64 }));
let f0_builder = dag.add_task(task_fn::<(), _, _>(|_: ()| 0i64));
let f1_builder = dag.add_task(task_fn::<(), _, _>(|_: ()| 1i64));
let mut prev2: dagx::TaskHandle<i64> = f0_builder.into();
let mut prev1: dagx::TaskHandle<i64> = f1_builder.into();
for _ in 2..50 {
let next = dag
.add_task(task_fn(|(a, b): (i64, i64)| async move { a + b }))
.add_task(task_fn::<(i64, i64), _, _>(|(a, b): (&i64, &i64)| a + b))
.depends_on((&prev2, &prev1));
prev2 = prev1;
@@ -225,12 +223,12 @@ async fn test_deep_chain_with_accumulator() -> DagResult<()> {
let dag = DagRunner::new();
// Use a simpler accumulation pattern - just sum
let first = dag.add_task(task_fn(|_: ()| async { 0i64 }));
let first = dag.add_task(task_fn::<(), _, _>(|_: ()| 0i64));
let mut current: dagx::TaskHandle<i64> = first.into();
for i in 1..=200 {
current = dag
.add_task(task_fn(move |sum: i64| async move { sum + i as i64 }))
.add_task(task_fn::<i64, _, _>(move |sum: &i64| sum + i as i64))
.depends_on(current);
}
@@ -247,8 +245,8 @@ async fn test_zigzag_dependencies() -> DagResult<()> {
// Test a zigzag pattern: two chains that cross-reference each other
let dag = DagRunner::new();
let a_first = dag.add_task(task_fn(|_: ()| async { 1 }));
let b_first = dag.add_task(task_fn(|_: ()| async { 2 }));
let a_first = dag.add_task(task_fn::<(), _, _>(|_: ()| 1));
let b_first = dag.add_task(task_fn::<(), _, _>(|_: ()| 2));
let mut chain_a: dagx::TaskHandle<i32> = a_first.into();
let mut chain_b: dagx::TaskHandle<i32> = b_first.into();
@@ -257,19 +255,19 @@ async fn test_zigzag_dependencies() -> DagResult<()> {
if i % 2 == 0 {
// A depends on B
chain_a = dag
.add_task(task_fn(move |b_val: i32| async move { b_val + 1 }))
.add_task(task_fn::<i32, _, _>(move |b_val: &i32| b_val + 1))
.depends_on(chain_b);
} else {
// B depends on A
chain_b = dag
.add_task(task_fn(move |a_val: i32| async move { a_val + 2 }))
.add_task(task_fn::<i32, _, _>(move |a_val: &i32| a_val + 2))
.depends_on(chain_a);
}
}
// Final task depends on both chains
let final_task = dag
.add_task(task_fn(|(a, b): (i32, i32)| async move { a + b }))
.add_task(task_fn::<(i32, i32), _, _>(|(a, b): (&i32, &i32)| a + b))
.depends_on((&chain_a, &chain_b));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
@@ -286,12 +284,12 @@ async fn test_10000_level_chain_stress() -> DagResult<()> {
// Ultimate deep chain test
let dag = DagRunner::new();
let first = dag.add_task(task_fn(|_: ()| async { 0u64 }));
let first = dag.add_task(task_fn::<(), _, _>(|_: ()| 0u64));
let mut current: dagx::TaskHandle<u64> = first.into();
for _ in 1..10_000 {
current = dag
.add_task(task_fn(|prev: u64| async move { prev + 1 }))
.add_task(task_fn::<u64, _, _>(|prev: &u64| prev + 1))
.depends_on(current);
}
+51 -56
View File
@@ -16,12 +16,9 @@ async fn test_10000_independent_tasks() -> DagResult<()> {
let mut tasks: Vec<_> = (0..10_000)
.map(|i| {
let counter = counter.clone();
dag.add_task(task_fn(move |_: ()| {
let counter = counter.clone();
async move {
counter.fetch_add(1, Ordering::Relaxed);
i
}
dag.add_task(task_fn::<(), _, _>(move |_: ()| {
counter.fetch_add(1, Ordering::Relaxed);
i
}))
})
.collect();
@@ -48,13 +45,15 @@ async fn test_5000_node_pyramid() -> DagResult<()> {
// Bottom layer: 2048 nodes - convert to TaskHandles
let mut current_layer: Vec<dagx::TaskHandle<i32>> = (0..2048)
.map(|i| {
let task = dag.add_task(task_fn(move |_: ()| async move {
if i < 1024 {
1
} else {
0
}
}));
let task = dag.add_task(task_fn::<(), _, _>(
move |_: ()| {
if i < 1024 {
1
} else {
0
}
},
));
task.into()
})
.collect();
@@ -74,20 +73,20 @@ async fn test_5000_node_pyramid() -> DagResult<()> {
let task = match deps.len() {
1 => dag
.add_task(task_fn(|a: i32| async move { a }))
.add_task(task_fn::<i32, _, _>(|&a: &i32| a))
.depends_on(deps[0]),
2 => dag
.add_task(task_fn(|(a, b): (i32, i32)| async move { a + b }))
.add_task(task_fn::<(i32, i32), _, _>(|(a, b): (&i32, &i32)| a + b))
.depends_on((deps[0], deps[1])),
3 => dag
.add_task(task_fn(
|(a, b, c): (i32, i32, i32)| async move { a + b + c },
.add_task(task_fn::<(i32, i32, i32), _, _>(
|(a, b, c): (&i32, &i32, &i32)| a + b + c,
))
.depends_on((deps[0], deps[1], deps[2])),
4 => dag
.add_task(task_fn(|(a, b, c, d): (i32, i32, i32, i32)| async move {
a + b + c + d
}))
.add_task(task_fn::<(i32, i32, i32, i32), _, _>(
|(a, b, c, d): (&i32, &i32, &i32, &i32)| a + b + c + d,
))
.depends_on((deps[0], deps[1], deps[2], deps[3])),
_ => unreachable!(),
};
@@ -98,7 +97,7 @@ async fn test_5000_node_pyramid() -> DagResult<()> {
// Final layer combines the last 2
let root = dag
.add_task(task_fn(|(a, b): (i32, i32)| async move { a + b }))
.add_task(task_fn::<(i32, i32), _, _>(|(a, b): (&i32, &i32)| a + b))
.depends_on((&current_layer[0], &current_layer[1]));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
@@ -119,13 +118,15 @@ async fn test_5000_node_pyramid_single_threaded() -> DagResult<()> {
// Bottom layer: 2048 nodes - convert to TaskHandles
let mut current_layer: Vec<dagx::TaskHandle<i32>> = (0..2048)
.map(|i| {
let task = dag.add_task(task_fn(move |_: ()| async move {
if i < 1024 {
1
} else {
0
}
}));
let task = dag.add_task(task_fn::<(), _, _>(
move |_: ()| {
if i < 1024 {
1
} else {
0
}
},
));
task.into()
})
.collect();
@@ -145,20 +146,20 @@ async fn test_5000_node_pyramid_single_threaded() -> DagResult<()> {
let task = match deps.len() {
1 => dag
.add_task(task_fn(|a: i32| async move { a }))
.add_task(task_fn::<i32, _, _>(|&a: &i32| a))
.depends_on(deps[0]),
2 => dag
.add_task(task_fn(|(a, b): (i32, i32)| async move { a + b }))
.add_task(task_fn::<(i32, i32), _, _>(|(a, b): (&i32, &i32)| a + b))
.depends_on((deps[0], deps[1])),
3 => dag
.add_task(task_fn(
|(a, b, c): (i32, i32, i32)| async move { a + b + c },
.add_task(task_fn::<(i32, i32, i32), _, _>(
|(a, b, c): (&i32, &i32, &i32)| a + b + c,
))
.depends_on((deps[0], deps[1], deps[2])),
4 => dag
.add_task(task_fn(|(a, b, c, d): (i32, i32, i32, i32)| async move {
a + b + c + d
}))
.add_task(task_fn::<(i32, i32, i32, i32), _, _>(
|(a, b, c, d): (&i32, &i32, &i32, &i32)| a + b + c + d,
))
.depends_on((deps[0], deps[1], deps[2], deps[3])),
_ => unreachable!(),
};
@@ -169,7 +170,7 @@ async fn test_5000_node_pyramid_single_threaded() -> DagResult<()> {
// Final layer combines the last 2
let root = dag
.add_task(task_fn(|(a, b): (i32, i32)| async move { a + b }))
.add_task(task_fn::<(i32, i32), _, _>(|(a, b): (&i32, &i32)| a + b))
.depends_on((&current_layer[0], &current_layer[1]));
dag.run(|fut| fut).await?;
@@ -189,7 +190,7 @@ async fn test_wide_dag_1000_sources_1000_sinks() -> DagResult<()> {
// Create 1000 source tasks
let sources: Vec<_> = (0..1000)
.map(|i| {
dag.add_task(task_fn(move |_: ()| async move { i % 10 }))
dag.add_task(task_fn::<(), _, _>(move |_: ()| i % 10))
.into()
})
.collect();
@@ -201,9 +202,9 @@ async fn test_wide_dag_1000_sources_1000_sinks() -> DagResult<()> {
let idx2 = (i * 13) % 1000;
let idx3 = (i * 17) % 1000;
dag.add_task(task_fn(move |(a, b, c): (i32, i32, i32)| async move {
a + b + c + i as i32
}))
dag.add_task(task_fn::<(i32, i32, i32), _, _>(
move |(a, b, c): (&i32, &i32, &i32)| a + b + c + i as i32,
))
.depends_on((&sources[idx1], &sources[idx2], &sources[idx3]))
})
.collect();
@@ -231,28 +232,24 @@ async fn test_10000_node_linear_chain_segments() -> DagResult<()> {
let mut chain_ends = Vec::new();
for chain_id in 0..100 {
let first = dag.add_task(task_fn({
let first = dag.add_task(task_fn::<(), _, _>({
let counter = counter.clone();
move |_: ()| {
let counter = counter.clone();
async move {
counter.fetch_add(1, Ordering::Relaxed);
chain_id * 100
}
counter.fetch_add(1, Ordering::Relaxed);
chain_id * 100
}
}));
let mut current: dagx::TaskHandle<i32> = first.into();
for _i in 1..100 {
current = dag
.add_task(task_fn({
.add_task(task_fn::<i32, _, _>({
let counter = counter.clone();
move |prev: i32| {
move |&prev: &i32| {
let counter = counter.clone();
async move {
counter.fetch_add(1, Ordering::Relaxed);
prev + 1
}
counter.fetch_add(1, Ordering::Relaxed);
prev + 1
}
}))
.depends_on(current);
@@ -291,12 +288,10 @@ async fn test_100000_nodes_stress() -> DagResult<()> {
.map(|i| {
let completed = completed.clone();
let task_id = batch * batch_size + i;
dag.add_task(task_fn(move |_: ()| {
dag.add_task(task_fn::<(), _, _>(move |_: ()| {
let completed = completed.clone();
async move {
completed.fetch_add(1, Ordering::Relaxed);
task_id
}
completed.fetch_add(1, Ordering::Relaxed);
task_id
}))
})
.collect();
+66 -97
View File
@@ -19,21 +19,19 @@ async fn test_zero_sized_types_throughout() -> DagResult<()> {
let dag = DagRunner::new();
// Task producing ZST
let t1 = dag.add_task(task_fn(|_: ()| async { Empty }));
let t1 = dag.add_task(task_fn::<(), _, _>(|_: ()| Empty));
// Task that also produces ZST (independent since we can't extract custom types)
let t2: TaskHandle<_> = dag.add_task(task_fn(|_: ()| async { 42 })).into();
let t2: TaskHandle<_> = dag.add_task(task_fn::<(), _, _>(|_: ()| 42)).into();
// Task with PhantomData
let _t3 = dag.add_task(task_fn(|_: ()| async {
PhantomWrapper::<i32> {
_phantom: PhantomData,
}
let _t3 = dag.add_task(task_fn::<(), _, _>(|_: ()| PhantomWrapper::<i32> {
_phantom: PhantomData,
}));
// Task that depends on t2 (uses primitive type)
let t4 = dag
.add_task(task_fn(|val: i32| async move {
.add_task(task_fn::<i32, _, _>(|&val: &i32| {
assert_eq!(val, 42);
"success"
}))
@@ -59,7 +57,7 @@ async fn test_large_value_types() -> DagResult<()> {
let dag = DagRunner::new();
let t1 = dag.add_task(task_fn(|_: ()| async {
let t1 = dag.add_task(task_fn::<(), _, _>(|_: ()| {
let large = LargeStruct {
data: [42; 8192],
more_data: vec![1; 1000],
@@ -69,7 +67,7 @@ async fn test_large_value_types() -> DagResult<()> {
}));
let t2 = dag
.add_task(task_fn(|val: usize| async move {
.add_task(task_fn::<usize, _, _>(|&val: &usize| {
val // Just pass through to verify
}))
.depends_on(t1);
@@ -86,12 +84,16 @@ async fn test_unit_type_chains() -> DagResult<()> {
// Test chains of unit type operations
let dag = DagRunner::new();
let t1: TaskHandle<_> = dag.add_task(task_fn(|_: ()| async {})).into();
let t2 = dag.add_task(task_fn(|_: ()| async {})).depends_on(t1);
let t3 = dag.add_task(task_fn(|_: ()| async {})).depends_on(t2);
let t1: TaskHandle<_> = dag.add_task(task_fn::<(), _, _>(|_: ()| {})).into();
let t2 = dag
.add_task(task_fn::<(), _, _>(|_: ()| {}))
.depends_on((t1,));
let t3 = dag
.add_task(task_fn::<(), _, _>(|_: ()| {}))
.depends_on((t2,));
let t4 = dag
.add_task(task_fn(|_: ()| async { "done" }))
.depends_on(t3);
.add_task(task_fn::<(), _, _>(|_: ()| "done"))
.depends_on((t3,));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
@@ -103,82 +105,43 @@ async fn test_unit_type_chains() -> DagResult<()> {
Ok(())
}
#[tokio::test]
async fn test_nested_option_result_types() -> DagResult<()> {
// Test deeply nested generic types
type ComplexType = Result<Option<Result<Vec<Option<i32>>, String>>, &'static str>;
let dag = DagRunner::new();
let t1 = dag.add_task(task_fn(|_: ()| async {
let val: ComplexType = Ok(Some(Ok(vec![Some(42), None, Some(13)])));
val
}));
let t2 = dag
.add_task(task_fn(|complex: ComplexType| async move {
match complex {
Ok(Some(Ok(vec))) => vec.iter().filter_map(|x| *x).sum::<i32>(),
_ => 0,
}
}))
.depends_on(t1);
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
assert_eq!(dag.get(t2)?, 55);
Ok(())
}
#[tokio::test]
async fn test_tuple_of_tuples() -> DagResult<()> {
// Test nested tuple types
let dag = DagRunner::new();
let t1 = dag.add_task(task_fn(|_: ()| async { ((1, 2), (3, 4)) }));
let t2 = dag.add_task(task_fn(|_: ()| async { ((5, 6), (7, 8)) }));
let t3 = dag
.add_task(task_fn(
|inputs: (((i32, i32), (i32, i32)), ((i32, i32), (i32, i32)))| async move {
let (((a, b), (c, d)), ((e, f), (g, h))) = inputs;
a + b + c + d + e + f + g + h
},
))
.depends_on((t1, t2));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
assert_eq!(dag.get(t3)?, 36);
Ok(())
}
#[tokio::test]
async fn test_string_types_variety() -> DagResult<()> {
// Test various string types
let dag = DagRunner::new();
let t1 = dag.add_task(task_fn(|_: ()| async { "static str" }));
let t2 = dag.add_task(task_fn(|_: ()| async { String::from("owned string") }));
let t3 = dag.add_task(task_fn(|_: ()| async {
let t1 = dag.add_task(task_fn::<(), _, _>(|_: ()| "static str"));
let t2 = dag.add_task(task_fn::<(), _, _>(|_: ()| String::from("owned string")));
let t3 = dag.add_task(task_fn::<(), _, _>(|_: ()| {
std::borrow::Cow::Borrowed("cow str")
}));
let t4 = dag.add_task(task_fn(|_: ()| async { Box::new("boxed str") }));
let t5 = dag.add_task(task_fn(|_: ()| async { std::sync::Arc::new("arc str 2") }));
let t6 = dag.add_task(task_fn(|_: ()| async { std::sync::Arc::new("arc str") }));
let t4 = dag.add_task(task_fn::<(), _, _>(|_: ()| Box::new("boxed str")));
let t5 = dag.add_task(task_fn::<(), _, _>(|_: ()| {
std::sync::Arc::new("arc str 2")
}));
let t6 = dag.add_task(task_fn::<(), _, _>(|_: ()| std::sync::Arc::new("arc str")));
let combined = dag
.add_task(task_fn(
|(s1, s2, s3, s4, s5, s6): (
.add_task(task_fn::<
(
&str,
String,
std::borrow::Cow<str>,
Box<&str>,
std::sync::Arc<&str>,
std::sync::Arc<&str>,
)| async move { format!("{} {} {} {} {} {}", s1, s2, s3, s4, s5, s6) },
),
_,
_,
>(
|(s1, s2, s3, s4, s5, s6): (
&&str,
&String,
&std::borrow::Cow<str>,
&Box<&str>,
&std::sync::Arc<&str>,
&std::sync::Arc<&str>,
)| { format!("{} {} {} {} {} {}", s1, s2, s3, s4, s5, s6) },
))
.depends_on((t1, t2, t3, t4, t5, t6));
@@ -199,13 +162,17 @@ async fn test_generic_type_constraints() -> DagResult<()> {
let dag = DagRunner::new();
let t1 = dag.add_task(task_fn(|_: ()| async { Wrapper(42) }));
let t2 = dag.add_task(task_fn(|_: ()| async { Wrapper("hello") }));
let t3 = dag.add_task(task_fn(|_: ()| async { Wrapper(vec![1, 2, 3]) }));
let t1 = dag.add_task(task_fn::<(), _, _>(|_: ()| Wrapper(42)));
let t2 = dag.add_task(task_fn::<(), _, _>(|_: ()| Wrapper("hello")));
let t3 = dag.add_task(task_fn::<(), _, _>(|_: ()| Wrapper(vec![1, 2, 3])));
let combined = dag
.add_task(task_fn(
|(w1, w2, w3): (Wrapper<i32>, Wrapper<&str>, Wrapper<Vec<i32>>)| async move {
.add_task(task_fn::<
(Wrapper<i32>, Wrapper<&str>, Wrapper<Vec<i32>>),
_,
_,
>(
|(w1, w2, w3): (&Wrapper<i32>, &Wrapper<&str>, &Wrapper<Vec<i32>>)| {
format!("{:?} {:?} {:?}", w1, w2, w3)
},
))
@@ -226,7 +193,7 @@ async fn test_reference_wrapper_types() -> DagResult<()> {
let dag = DagRunner::new();
let t1 = dag.add_task(task_fn(|_: ()| async {
let t1 = dag.add_task(task_fn::<(), _, _>(|_: ()| {
let arc = Arc::new(Mutex::new(100));
// Extract value instead of passing Arc
let guard = arc.lock().unwrap();
@@ -234,7 +201,7 @@ async fn test_reference_wrapper_types() -> DagResult<()> {
}));
let t2 = dag
.add_task(task_fn(|val: i32| async move { val * 2 }))
.add_task(task_fn::<i32, _, _>(|&val: &i32| val * 2))
.depends_on(t1);
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
@@ -249,17 +216,19 @@ async fn test_array_types() -> DagResult<()> {
// Test with fixed-size arrays
let dag = DagRunner::new();
let t1 = dag.add_task(task_fn(|_: ()| async { [1, 2, 3, 4, 5] }));
let t2 = dag.add_task(task_fn(|_: ()| async { [6, 7, 8, 9, 10] }));
let t1 = dag.add_task(task_fn::<(), _, _>(|_: ()| [1, 2, 3, 4, 5]));
let t2 = dag.add_task(task_fn::<(), _, _>(|_: ()| [6, 7, 8, 9, 10]));
let t3 = dag
.add_task(task_fn(|(a1, a2): ([i32; 5], [i32; 5])| async move {
let mut sum = 0;
for i in 0..5 {
sum += a1[i] + a2[i];
}
sum
}))
.add_task(task_fn::<([i32; 5], [i32; 5]), _, _>(
|(a1, a2): (&[i32; 5], &[i32; 5])| {
let mut sum = 0;
for i in 0..5 {
sum += a1[i] + a2[i];
}
sum
},
))
.depends_on((t1, t2));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
@@ -283,16 +252,16 @@ async fn test_enum_variants() -> DagResult<()> {
let dag = DagRunner::new();
let t1 = dag.add_task(task_fn(|_: ()| async { MyEnum::Unit }));
let t2 = dag.add_task(task_fn(|_: ()| async {
let t1 = dag.add_task(task_fn::<(), _, _>(|_: ()| MyEnum::Unit));
let t2 = dag.add_task(task_fn::<(), _, _>(|_: ()| {
MyEnum::Tuple(42, "test".to_string())
}));
let t3 = dag.add_task(task_fn(|_: ()| async { MyEnum::Struct { x: 10, y: 20 } }));
let t4 = dag.add_task(task_fn(|_: ()| async { MyEnum::LargeVariant([0; 1024]) }));
let t3 = dag.add_task(task_fn::<(), _, _>(|_: ()| MyEnum::Struct { x: 10, y: 20 }));
let t4 = dag.add_task(task_fn::<(), _, _>(|_: ()| MyEnum::LargeVariant([0; 1024])));
let combined = dag
.add_task(task_fn(
|(e1, e2, e3, e4): (MyEnum, MyEnum, MyEnum, MyEnum)| async move {
.add_task(task_fn::<(MyEnum, MyEnum, MyEnum, MyEnum), _, _>(
|(e1, e2, e3, e4): (&MyEnum, &MyEnum, &MyEnum, &MyEnum)| {
matches!(e1, MyEnum::Unit)
&& matches!(e2, MyEnum::Tuple(42, _))
&& matches!(e3, MyEnum::Struct { x: 10, y: 20 })
+19 -14
View File
@@ -5,6 +5,7 @@ use dagx::{task, DagResult, DagRunner};
use futures::FutureExt;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use tokio::task::yield_now;
#[tokio::test]
async fn test_empty_dag_execution() -> DagResult<()> {
@@ -22,7 +23,7 @@ async fn test_single_task_no_deps() -> DagResult<()> {
// Test the absolute minimum: one task with no dependencies
let dag = DagRunner::new();
let task = dag.add_task(task_fn(|_: ()| async { 42 }));
let task = dag.add_task(task_fn::<(), _, _>(|_: ()| 42));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
@@ -38,11 +39,9 @@ async fn test_single_task_unit_input_output() -> DagResult<()> {
let executed = Arc::new(AtomicUsize::new(0));
let exec_clone = executed.clone();
let task = dag.add_task(task_fn(move |_: ()| {
let task = dag.add_task(task_fn::<(), _, _>(move |_: ()| {
let exec = exec_clone.clone();
async move {
exec.fetch_add(1, Ordering::SeqCst);
}
exec.fetch_add(1, Ordering::SeqCst);
}));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
@@ -60,7 +59,7 @@ async fn test_zero_sized_type_task() -> DagResult<()> {
let dag = DagRunner::new();
let task = dag.add_task(task_fn(|_: ()| async { ZeroSized }));
let task = dag.add_task(task_fn::<(), _, _>(|_: ()| ZeroSized));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
@@ -86,7 +85,7 @@ async fn test_dag_with_only_source_tasks() -> DagResult<()> {
let dag = DagRunner::new();
let tasks: Vec<_> = (0..100)
.map(|i| dag.add_task(task_fn(move |_: ()| async move { i })))
.map(|i| dag.add_task(task_fn::<(), _, _>(move |_: ()| i)))
.collect();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
@@ -132,7 +131,7 @@ async fn test_alternating_empty_and_filled_dag_runs() -> DagResult<()> {
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
} else {
// DAG with a task
let task = dag.add_task(task_fn(move |_: ()| async move { i }));
let task = dag.add_task(task_fn::<(), _, _>(move |_: ()| i));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
assert_eq!(dag.get(task)?, i);
}
@@ -151,7 +150,7 @@ async fn test_dag_with_never_type_simulation() -> DagResult<()> {
let dag = DagRunner::new();
// This task completes immediately but returns a Result containing "never" type
let task = dag.add_task(task_fn(|_: ()| async { Result::<i32, Never>::Ok(42) }));
let task = dag.add_task(task_fn::<(), _, _>(|_: ()| Result::<i32, Never>::Ok(42)));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
@@ -164,11 +163,17 @@ async fn test_minimal_task_with_minimal_async_work() -> DagResult<()> {
// Test a task that does the absolute minimum async work
let dag = DagRunner::new();
let task = dag.add_task(task_fn(|_: ()| async {
// Just yield once to the runtime
tokio::task::yield_now().await;
1337
}));
struct YieldTask;
#[task]
impl YieldTask {
async fn run() -> i32 {
yield_now().await;
1337
}
}
let task = dag.add_task(YieldTask);
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;