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

+4 -2
View File
@@ -31,6 +31,7 @@ impl IsUnitType for () {}
///
/// ```no_run
/// # use dagx::{task, DagRunner, Task};
/// # use futures::FutureExt;
/// // Using tuple struct for simple constants
/// struct Constant(i32);
///
@@ -65,7 +66,7 @@ impl IsUnitType for () {}
/// let b = b.depends_on(&a);
/// // Now b is a TaskHandle<i32>
///
/// dag.run().await.unwrap();
/// dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap();
/// assert_eq!(dag.get(&b).unwrap(), 20);
/// # };
/// ```
@@ -89,6 +90,7 @@ impl<'a, Tk: Task, Deps> TaskBuilder<'a, Tk, Deps> {
///
/// ```no_run
/// # use dagx::{task, DagRunner, Task};
/// # use futures::FutureExt;
/// // Tuple struct
/// struct Value(i32);
///
@@ -125,7 +127,7 @@ impl<'a, Tk: Task, Deps> TaskBuilder<'a, Tk, Deps> {
/// // Multiple dependencies: tuple form
/// let sum = dag.add_task(Add).depends_on((&x, &y));
///
/// dag.run().await.unwrap();
/// dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap();
/// # };
/// ```
pub fn depends_on<D>(self, deps: D) -> TaskHandle<Tk::Output>
+1 -1
View File
@@ -63,7 +63,7 @@ impl std::fmt::Display for DagError {
f,
"Result not found for task #{}.\n\
\n\
Call dag.run().",
Call dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).",
task_id
)
}
+1 -1
View File
@@ -51,7 +51,7 @@ fn test_dag_error_display_result_not_found() {
assert!(display.contains("Result not found"));
assert!(display.contains("task #7"));
assert!(display.contains("Call dag.run()"));
assert!(display.contains("Call dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))"));
}
#[test]
+37 -119
View File
@@ -5,7 +5,6 @@
//! We directly call extract_from_deps with mismatched types.
use crate::extract::ExtractInput;
use futures::channel::oneshot;
use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
@@ -13,17 +12,10 @@ use std::sync::Arc;
#[tokio::test]
async fn test_result_type_mismatch_coverage() {
// Tests line 95 in extract.rs - Result<T,E> type mismatch error message
// Create a channel that sends the WRONG type (String instead of Result<i32, String>)
let (tx, rx) = oneshot::channel::<Arc<String>>();
// Send a String value
tokio::spawn(async move {
let _ = tx.send(Arc::new("hello".to_string()));
});
// Create a dependency that provides the WRONG type (String instead of Result<i32, String>)
// Try to extract as Result<i32, String> - this will fail with type mismatch
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = Result::<i32, String>::extract_from_channels(deps).await;
let deps = vec![Arc::new("hello") as Arc<dyn Any + Send + Sync>];
let result = Result::<i32, String>::extract_from_deps(deps).await;
// Verify the error message contains the expected type information
assert!(result.is_err());
@@ -45,17 +37,10 @@ async fn test_result_type_mismatch_coverage() {
#[tokio::test]
async fn test_option_type_mismatch_coverage() {
// Tests line 124 in extract.rs - Option<T> type mismatch error message
// Create a channel that sends the WRONG type (i32 instead of Option<i32>)
let (tx, rx) = oneshot::channel::<Arc<i32>>();
// Send an i32 value
tokio::spawn(async move {
let _ = tx.send(Arc::new(42));
});
// Create a dependency that provides the WRONG type (i32 instead of Option<i32>)
// Try to extract as Option<i32> - this will fail with type mismatch
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = Option::<i32>::extract_from_channels(deps).await;
let deps = vec![Arc::new(42) as Arc<dyn Any + Send + Sync>];
let result = Option::<i32>::extract_from_deps(deps).await;
// Verify the error message contains the expected type information
assert!(result.is_err());
@@ -77,17 +62,10 @@ async fn test_option_type_mismatch_coverage() {
#[tokio::test]
async fn test_vec_type_mismatch_coverage() {
// Tests line 152 in extract.rs - Vec<T> type mismatch error message
// Create a channel that sends the WRONG type (String instead of Vec<u8>)
let (tx, rx) = oneshot::channel::<Arc<String>>();
// Send a String value
tokio::spawn(async move {
let _ = tx.send(Arc::new("not a vec".to_string()));
});
// Create a dependency that provides the WRONG type (String instead of Vec<u8>)
// Try to extract as Vec<u8> - this will fail with type mismatch
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = Vec::<u8>::extract_from_channels(deps).await;
let deps = vec![Arc::new("not a vec".to_string()) as Arc<dyn Any + Send + Sync>];
let result = Vec::<u8>::extract_from_deps(deps).await;
// Verify the error message contains the expected type information
assert!(result.is_err());
@@ -109,17 +87,10 @@ async fn test_vec_type_mismatch_coverage() {
#[tokio::test]
async fn test_hashmap_type_mismatch_coverage() {
// Tests line 181 in extract.rs - HashMap<K,V> type mismatch error message
// Create a channel that sends the WRONG type (Vec<i32> instead of HashMap)
let (tx, rx) = oneshot::channel::<Arc<Vec<i32>>>();
// Send a Vec value
tokio::spawn(async move {
let _ = tx.send(Arc::new(vec![1, 2, 3]));
});
// Create a dependency that provides the WRONG type (Vec<i32> instead of HashMap)
// Try to extract as HashMap<String, i32> - this will fail with type mismatch
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = HashMap::<String, i32>::extract_from_channels(deps).await;
let deps = vec![Arc::new(vec![1, 2, 3]) as Arc<dyn Any + Send + Sync>];
let result = HashMap::<String, i32>::extract_from_deps(deps).await;
// Verify the error message contains the expected type information
assert!(result.is_err());
@@ -141,12 +112,12 @@ async fn test_hashmap_type_mismatch_coverage() {
#[tokio::test]
async fn test_arc_type_mismatch_coverage() {
// Tests line 211 in extract.rs - Arc<T> type mismatch error message
// Arc is special: it expects Arc<Arc<T>> from dependency
// Arc is special: it expects Arc<Arc<T>> from channel
// Create a dependency that provides Arc<String> instead of Arc<Arc<String>>
let deps = vec![Arc::new("not double arc".to_string()) as Arc<dyn Any + Send + Sync>];
// Try to extract as Arc<String> - this will fail with type mismatch
// because it expects Arc<Arc<String>> in the dependency
// because it expects Arc<Arc<String>> in the channel
let result = Arc::<String>::extract_from_deps(deps).await;
// Verify the error message contains the expected type information
@@ -171,15 +142,9 @@ async fn test_arc_type_mismatch_coverage() {
#[tokio::test]
async fn test_result_with_wrong_ok_type() {
// Test Result with wrong Ok type
let (tx, rx) = oneshot::channel::<Arc<Result<String, String>>>();
tokio::spawn(async move {
let _ = tx.send(Arc::new(Ok("test".to_string())));
});
// Try to extract as Result<i32, String> - wrong Ok type
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = Result::<i32, String>::extract_from_channels(deps).await;
let deps = vec![Arc::new(Ok::<_, String>("test".to_string())) as Arc<dyn Any + Send + Sync>];
let result = Result::<i32, String>::extract_from_deps(deps).await;
assert!(result.is_err());
let err_msg = result.unwrap_err();
@@ -193,15 +158,9 @@ async fn test_result_with_wrong_ok_type() {
#[tokio::test]
async fn test_result_with_wrong_err_type() {
// Test Result with wrong Err type
let (tx, rx) = oneshot::channel::<Arc<Result<i32, i32>>>();
tokio::spawn(async move {
let _ = tx.send(Arc::new(Err(42)));
});
// Try to extract as Result<i32, String> - wrong Err type
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = Result::<i32, String>::extract_from_channels(deps).await;
let deps = vec![Arc::new(Err::<i32, i32>(42)) as Arc<dyn Any + Send + Sync>];
let result = Result::<i32, String>::extract_from_deps(deps).await;
assert!(result.is_err());
let err_msg = result.unwrap_err();
@@ -215,15 +174,9 @@ async fn test_result_with_wrong_err_type() {
#[tokio::test]
async fn test_option_with_wrong_inner_type() {
// Test Option with wrong inner type
let (tx, rx) = oneshot::channel::<Arc<Option<String>>>();
tokio::spawn(async move {
let _ = tx.send(Arc::new(Some("test".to_string())));
});
// Try to extract as Option<i32> - wrong inner type
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = Option::<i32>::extract_from_channels(deps).await;
let deps = vec![Arc::new(Some("test".to_string())) as Arc<dyn Any + Send + Sync>];
let result = Option::<i32>::extract_from_deps(deps).await;
assert!(result.is_err());
let err_msg = result.unwrap_err();
@@ -237,15 +190,9 @@ async fn test_option_with_wrong_inner_type() {
#[tokio::test]
async fn test_vec_with_wrong_element_type() {
// Test Vec with wrong element type
let (tx, rx) = oneshot::channel::<Arc<Vec<String>>>();
tokio::spawn(async move {
let _ = tx.send(Arc::new(vec!["a".to_string(), "b".to_string()]));
});
// Try to extract as Vec<i32> - wrong element type
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = Vec::<i32>::extract_from_channels(deps).await;
let deps = vec![Arc::new(vec!["a".to_string(), "b".to_string()]) as Arc<dyn Any + Send + Sync>];
let result = Vec::<i32>::extract_from_deps(deps).await;
assert!(result.is_err());
let err_msg = result.unwrap_err();
@@ -259,17 +206,11 @@ async fn test_vec_with_wrong_element_type() {
#[tokio::test]
async fn test_hashmap_with_wrong_key_type() {
// Test HashMap with wrong key type
let (tx, rx) = oneshot::channel::<Arc<HashMap<i32, String>>>();
tokio::spawn(async move {
let mut map = HashMap::new();
map.insert(1, "one".to_string());
let _ = tx.send(Arc::new(map));
});
let mut map = HashMap::new();
map.insert(1, "one".to_string());
// Try to extract as HashMap<String, String> - wrong key type
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = HashMap::<String, String>::extract_from_channels(deps).await;
let deps = vec![Arc::new(map) as Arc<dyn Any + Send + Sync>];
let result = HashMap::<String, String>::extract_from_deps(deps).await;
assert!(result.is_err());
let err_msg = result.unwrap_err();
@@ -283,17 +224,11 @@ async fn test_hashmap_with_wrong_key_type() {
#[tokio::test]
async fn test_hashmap_with_wrong_value_type() {
// Test HashMap with wrong value type
let (tx, rx) = oneshot::channel::<Arc<HashMap<String, i32>>>();
tokio::spawn(async move {
let mut map = HashMap::new();
map.insert("key".to_string(), 42);
let _ = tx.send(Arc::new(map));
});
let mut map = HashMap::new();
map.insert("key".to_string(), 42);
// Try to extract as HashMap<String, String> - wrong value type
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = HashMap::<String, String>::extract_from_channels(deps).await;
let deps = vec![Arc::new(map) as Arc<dyn Any + Send + Sync>];
let result = HashMap::<String, String>::extract_from_deps(deps).await;
assert!(result.is_err());
let err_msg = result.unwrap_err();
@@ -308,15 +243,9 @@ async fn test_hashmap_with_wrong_value_type() {
async fn test_arc_with_wrong_inner_type() {
// Test Arc with completely wrong inner type
// Send Arc<i32> but expect Arc<String>
let (tx, rx) = oneshot::channel::<Arc<Arc<i32>>>();
tokio::spawn(async move {
let _ = tx.send(Arc::new(Arc::new(42)));
});
// Try to extract as Arc<String> - wrong inner type
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = Arc::<String>::extract_from_channels(deps).await;
let deps = vec![Arc::new(Arc::new(Arc::new(42))) as Arc<dyn Any + Send + Sync>];
let result = Arc::<String>::extract_from_deps(deps).await;
assert!(result.is_err());
let err_msg = result.unwrap_err();
@@ -330,15 +259,9 @@ async fn test_arc_with_wrong_inner_type() {
#[tokio::test]
async fn test_primitive_type_mismatch_for_completeness() {
// Test primitive type mismatch (already covered in existing tests but good for completeness)
let (tx, rx) = oneshot::channel::<Arc<String>>();
tokio::spawn(async move {
let _ = tx.send(Arc::new("not an i32".to_string()));
});
// Try to extract as i32 - type mismatch
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = i32::extract_from_channels(deps).await;
let deps = vec![Arc::new("not an i32".to_string()) as Arc<dyn Any + Send + Sync>];
let result = i32::extract_from_deps(deps).await;
assert!(result.is_err());
let err_msg = result.unwrap_err();
@@ -352,15 +275,10 @@ async fn test_primitive_type_mismatch_for_completeness() {
#[tokio::test]
async fn test_complex_nested_result_option() {
// Test complex nested type: send Result<String, String>, expect Result<Option<i32>, String>
let (tx, rx) = oneshot::channel::<Arc<Result<String, String>>>();
tokio::spawn(async move {
let _ = tx.send(Arc::new(Ok("test".to_string())));
});
// Try to extract as Result<Option<i32>, String> - nested type mismatch
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = Result::<Option<i32>, String>::extract_from_channels(deps).await;
let deps =
vec![Arc::new(Ok::<String, String>("test".to_string())) as Arc<dyn Any + Send + Sync>];
let result = Result::<Option<i32>, String>::extract_from_deps(deps).await;
assert!(result.is_err());
let err_msg = result.unwrap_err();
+50 -130
View File
@@ -38,11 +38,11 @@ impl TestTaskWithInput {
async fn test_extract_result_type_single_dependency_error() {
// Test Result<T,E> with wrong dependency count
let deps = vec![
Box::new(oneshot::channel::<Result<i32, String>>().1) as Box<dyn Any + Send>,
Box::new(oneshot::channel::<Result<i32, String>>().1) as Box<dyn Any + Send>,
Arc::new(Ok::<i32, String>(5)) as Arc<dyn Any + Send + Sync>,
Arc::new(Err::<i32, String>("test".to_string())) as Arc<dyn Any + Send + Sync>,
]; // Two dependencies when expecting 1
let result = Result::<i32, String>::extract_from_channels(deps).await;
let result = Result::<i32, String>::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 2"));
}
@@ -50,15 +50,9 @@ async fn test_extract_result_type_single_dependency_error() {
#[tokio::test]
async fn test_extract_result_type_success() {
// Test Result<T,E> successful extraction
let (tx, rx) = oneshot::channel::<Arc<Result<i32, String>>>();
// Send value in background
tokio::spawn(async move {
let _ = tx.send(Arc::new(Ok(42)));
});
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = Result::<i32, String>::extract_from_channels(deps).await;
let deps = vec![Arc::new(Ok::<i32, String>(42)) as Arc<dyn Any + Send + Sync>];
let result = Result::<i32, String>::extract_from_deps(deps).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), Ok(42));
}
@@ -67,12 +61,12 @@ async fn test_extract_result_type_success() {
async fn test_extract_option_type_single_dependency_error() {
// Test Option<T> with wrong dependency count
let deps = vec![
Box::new(oneshot::channel::<Option<String>>().1) as Box<dyn Any + Send>,
Box::new(oneshot::channel::<Option<String>>().1) as Box<dyn Any + Send>,
Box::new(oneshot::channel::<Option<String>>().1) as Box<dyn Any + Send>,
Arc::new(None::<String>) as Arc<dyn Any + Send + Sync>,
Arc::new(None::<String>) as Arc<dyn Any + Send + Sync>,
Arc::new(None::<String>) as Arc<dyn Any + Send + Sync>,
]; // Three dependencies when expecting 1
let result = Option::<String>::extract_from_channels(deps).await;
let result = Option::<String>::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 3"));
}
@@ -80,15 +74,8 @@ async fn test_extract_option_type_single_dependency_error() {
#[tokio::test]
async fn test_extract_option_type_success() {
// Test Option<T> successful extraction
let (tx, rx) = oneshot::channel::<Arc<Option<i32>>>();
// Send value in background
tokio::spawn(async move {
let _ = tx.send(Arc::new(Some(99)));
});
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = Option::<i32>::extract_from_channels(deps).await;
let deps = vec![Arc::new(Some(99)) as Arc<dyn Any + Send + Sync>];
let result = Option::<i32>::extract_from_deps(deps).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), Some(99));
}
@@ -98,7 +85,7 @@ async fn test_extract_vec_type_single_dependency_error() {
// Test Vec<T> with wrong dependency count
let deps = vec![]; // Zero dependencies when expecting 1
let result = Vec::<u8>::extract_from_channels(deps).await;
let result = Vec::<u8>::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 0"));
}
@@ -106,15 +93,8 @@ async fn test_extract_vec_type_single_dependency_error() {
#[tokio::test]
async fn test_extract_vec_type_success() {
// Test Vec<T> successful extraction
let (tx, rx) = oneshot::channel::<Arc<Vec<i32>>>();
// Send value in background
tokio::spawn(async move {
let _ = tx.send(Arc::new(vec![1, 2, 3]));
});
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = Vec::<i32>::extract_from_channels(deps).await;
let deps = vec![Arc::new(vec![1, 2, 3]) as Arc<dyn Any + Send + Sync>];
let result = Vec::<i32>::extract_from_deps(deps).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), vec![1, 2, 3]);
}
@@ -123,29 +103,29 @@ async fn test_extract_vec_type_success() {
async fn test_extract_primitive_single_dependency_error() {
// Test primitive types with wrong dependency count
let deps = vec![
Box::new(oneshot::channel::<i32>().1) as Box<dyn Any + Send>,
Box::new(oneshot::channel::<i32>().1) as Box<dyn Any + Send>,
Arc::new(0) as Arc<dyn Any + Send + Sync>,
Arc::new(0) as Arc<dyn Any + Send + Sync>,
]; // Two dependencies when expecting 1
let result = i32::extract_from_channels(deps).await;
let result = i32::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 2"));
// Test with String
let deps = vec![
Box::new(oneshot::channel::<String>().1) as Box<dyn Any + Send>,
Box::new(oneshot::channel::<String>().1) as Box<dyn Any + Send>,
Arc::new(String::default()) as Arc<dyn Any + Send + Sync>,
Arc::new(String::default()) as Arc<dyn Any + Send + Sync>,
];
let result = String::extract_from_channels(deps).await;
let result = String::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 2"));
// Test with f64
let deps = vec![
Box::new(oneshot::channel::<f64>().1) as Box<dyn Any + Send>,
Box::new(oneshot::channel::<f64>().1) as Box<dyn Any + Send>,
Arc::new(0.0) as Arc<dyn Any + Send + Sync>,
Arc::new(0.0) as Arc<dyn Any + Send + Sync>,
];
let result = f64::extract_from_channels(deps).await;
let result = f64::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 2"));
}
@@ -154,58 +134,36 @@ async fn test_extract_primitive_single_dependency_error() {
async fn test_extract_unit_type() {
// Test that unit type always succeeds regardless of dependencies
let deps = vec![];
let result = <()>::extract_from_channels(deps).await;
let result = <()>::extract_from_deps(deps).await;
assert!(result.is_ok());
// Even with dependencies (ignored), unit type succeeds
let deps = vec![
Box::new(oneshot::channel::<i32>().1) as Box<dyn Any + Send>,
Box::new(oneshot::channel::<i32>().1) as Box<dyn Any + Send>,
Arc::new(0) as Arc<dyn Any + Send + Sync>,
Arc::new(0) as Arc<dyn Any + Send + Sync>,
];
let result = <()>::extract_from_channels(deps).await;
let result = <()>::extract_from_deps(deps).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_extract_tuple_basic() {
// Test tuple extraction with channels
let (tx1, rx1) = oneshot::channel::<Arc<i32>>();
let (tx2, rx2) = oneshot::channel::<Arc<i32>>();
// Send values in background
tokio::spawn(async move {
let _ = tx1.send(Arc::new(42));
let _ = tx2.send(Arc::new(100));
});
// Test tuple extraction
let deps = vec![
Box::new(rx1) as Box<dyn Any + Send>,
Box::new(rx2) as Box<dyn Any + Send>,
Arc::new(42) as Arc<dyn Any + Send + Sync>,
Arc::new(100) as Arc<dyn Any + Send + Sync>,
];
// Extract a tuple (i32, i32)
let result = <(i32, i32)>::extract_from_channels(deps).await;
let result = <(i32, i32)>::extract_from_deps(deps).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), (42, 100));
}
#[tokio::test]
async fn test_extract_channel_closed() {
// Test error when channel is closed
let (tx, rx) = oneshot::channel::<Arc<i32>>();
// Explicitly drop tx to close the channel
drop(tx);
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = i32::extract_from_channels(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Channel closed"));
}
#[tokio::test]
async fn test_extract_type_mismatch() {
// Test type mismatch during downcast
// Try to extract as wrong type - put wrong parameter type in deps
// Try to extract as wrong type - put wrong receiver type in deps
let deps = vec![Arc::new("hello".to_string()) as Arc<dyn Any + Send + Sync>];
let result = i32::extract_from_deps(deps).await;
assert!(result.is_err());
@@ -217,14 +175,8 @@ async fn test_extract_all_primitive_types() {
// Test that all primitive types in impl_extract_single! work correctly
macro_rules! test_type {
($t:ty, $val:expr) => {
let (tx, rx) = oneshot::channel::<Arc<$t>>();
tokio::spawn(async move {
let _ = tx.send(Arc::new($val));
});
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = <$t>::extract_from_channels(deps).await;
let deps = vec![Arc::new($val) as Arc<dyn Any + Send + Sync>];
let result = <$t>::extract_from_deps(deps).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), $val);
};
@@ -240,31 +192,16 @@ async fn test_extract_all_primitive_types() {
#[tokio::test]
async fn test_extract_larger_tuples() {
// Test extraction for larger tuple sizes
let (tx1, rx1) = oneshot::channel::<Arc<i32>>();
let (tx2, rx2) = oneshot::channel::<Arc<i32>>();
let (tx3, rx3) = oneshot::channel::<Arc<i32>>();
let (tx4, rx4) = oneshot::channel::<Arc<i32>>();
let (tx5, rx5) = oneshot::channel::<Arc<i32>>();
// Send values in background
tokio::spawn(async move {
let _ = tx1.send(Arc::new(0));
let _ = tx2.send(Arc::new(1));
let _ = tx3.send(Arc::new(2));
let _ = tx4.send(Arc::new(3));
let _ = tx5.send(Arc::new(4));
});
let deps = vec![
Box::new(rx1) as Box<dyn Any + Send>,
Box::new(rx2) as Box<dyn Any + Send>,
Box::new(rx3) as Box<dyn Any + Send>,
Box::new(rx4) as Box<dyn Any + Send>,
Box::new(rx5) as Box<dyn Any + Send>,
Arc::new(0) as Arc<dyn Any + Send + Sync>,
Arc::new(1) as Arc<dyn Any + Send + Sync>,
Arc::new(2) as Arc<dyn Any + Send + Sync>,
Arc::new(3) as Arc<dyn Any + Send + Sync>,
Arc::new(4) as Arc<dyn Any + Send + Sync>,
];
// Extract a 5-element tuple
let result = <(i32, i32, i32, i32, i32)>::extract_from_channels(deps).await;
let result = <(i32, i32, i32, i32, i32)>::extract_from_deps(deps).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), (0, 1, 2, 3, 4));
}
@@ -272,17 +209,11 @@ async fn test_extract_larger_tuples() {
#[tokio::test]
async fn test_extract_arc_type() {
// Test Arc<T> extraction
let (tx, rx) = oneshot::channel::<Arc<Arc<String>>>();
let test_arc = Arc::new("test".to_string());
let test_arc_clone = test_arc.clone();
tokio::spawn(async move {
let _ = tx.send(Arc::new(test_arc));
});
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = Arc::<String>::extract_from_channels(deps).await;
let deps = vec![Arc::new(test_arc) as Arc<dyn Any + Send + Sync>];
let result = Arc::<String>::extract_from_deps(deps).await;
assert!(result.is_ok());
assert_eq!(*result.unwrap(), *test_arc_clone);
}
@@ -293,11 +224,11 @@ use std::collections::HashMap;
#[tokio::test]
async fn test_hashmap_extract_wrong_dependency_count() {
let deps = vec![
Box::new(oneshot::channel::<Arc<HashMap<String, i32>>>().1) as Box<dyn Any + Send>,
Box::new(oneshot::channel::<Arc<HashMap<String, i32>>>().1) as Box<dyn Any + Send>,
Arc::new(HashMap::<String, i32>::default()) as Arc<dyn Any + Send + Sync>,
Arc::new(HashMap::<String, i32>::default()) as Arc<dyn Any + Send + Sync>,
];
let result = HashMap::<String, i32>::extract_from_channels(deps).await;
let result = HashMap::<String, i32>::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 2"));
}
@@ -310,17 +241,6 @@ async fn test_hashmap_extract_type_mismatch() {
assert!(result.unwrap_err().contains("Type mismatch"));
}
#[tokio::test]
async fn test_hashmap_extract_channel_closed() {
let (tx, rx) = oneshot::channel::<Arc<HashMap<String, i32>>>();
drop(tx);
let deps = vec![Box::new(rx) as Box<dyn Any + Send>];
let result = HashMap::<String, i32>::extract_from_channels(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Channel closed"));
}
#[tokio::test]
async fn test_result_extract_downcast_failure() {
let deps = vec![Arc::new("wrong".to_string()) as Arc<dyn Any + Send + Sync>];
@@ -348,7 +268,7 @@ async fn test_vec_extract_downcast_failure() {
#[tokio::test]
async fn test_arc_extract_wrong_dependency_count() {
let deps = vec![];
let result = Arc::<i32>::extract_from_channels(deps).await;
let result = Arc::<i32>::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 0"));
}
@@ -364,12 +284,12 @@ async fn test_arc_extract_downcast_failure() {
#[tokio::test]
async fn test_tuple_wrong_dependency_count() {
let deps = vec![
Box::new(oneshot::channel::<Arc<i32>>().1) as Box<dyn Any + Send>,
Box::new(oneshot::channel::<Arc<i32>>().1) as Box<dyn Any + Send>,
Box::new(oneshot::channel::<Arc<i32>>().1) as Box<dyn Any + Send>,
Arc::new(0) as Arc<dyn Any + Send + Sync>,
Arc::new(0) as Arc<dyn Any + Send + Sync>,
Arc::new(0) as Arc<dyn Any + Send + Sync>,
];
let result = <(i32, i32)>::extract_from_channels(deps).await;
let result = <(i32, i32)>::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result
.unwrap_err()
+17 -10
View File
@@ -24,6 +24,7 @@
//!
//! ```no_run
//! use dagx::{task, DagRunner, Task};
//! use futures::FutureExt; // for FutureExt::map
//!
//! // Source task with read-only state (tuple struct)
//! struct Value(i32);
@@ -52,7 +53,7 @@
//! let sum = dag.add_task(Add).depends_on((&x, &y));
//!
//! // Execute and retrieve results
//! dag.run().await.unwrap();
//! dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap();
//! assert_eq!(dag.get(sum).unwrap(), 5);
//! # };
//! ```
@@ -290,6 +291,7 @@
//!
//! ```no_run
//! use dagx::{task, DagRunner, Task};
//! use futures::FutureExt;
//!
//! // Source task (tuple struct)
//! struct Value(i32);
@@ -322,7 +324,7 @@
//! let plus1 = dag.add_task(Add(1)).depends_on(&base);
//! let times2 = dag.add_task(Scale(2)).depends_on(&base);
//!
//! dag.run().await.unwrap();
//! dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap();
//!
//! assert_eq!(dag.get(plus1).unwrap(), 11);
//! assert_eq!(dag.get(times2).unwrap(), 20);
@@ -335,6 +337,7 @@
//!
//! ```no_run
//! use dagx::{task, DagRunner, Task};
//! use futures::FutureExt;
//!
//! // Source task for String (tuple struct)
//! struct Name(String);
@@ -378,7 +381,7 @@
//! let active = dag.add_task(Active(true));
//! let result = dag.add_task(FormatUser).depends_on((&name, &age, &active));
//!
//! dag.run().await.unwrap();
//! dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap();
//!
//! assert_eq!(dag.get(result).unwrap(), "User: Alice, Age: 30, Active: true");
//! # };
@@ -390,6 +393,7 @@
//!
//! ```no_run
//! use dagx::{task, DagRunner, Task};
//! use futures::FutureExt;
//!
//! // Source task (tuple struct)
//! struct Value(i32);
@@ -430,7 +434,7 @@
//! // Layer 3: Final result
//! let total = dag.add_task(Add).depends_on((&sum_xy, &prod_yz)); // 5 + 15 = 20
//!
//! dag.run().await.unwrap();
//! dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap();
//!
//! assert_eq!(dag.get(total).unwrap(), 20);
//! # };
@@ -442,7 +446,8 @@
//! the `#[task]` macro generates the necessary extraction logic:
//!
//! ```no_run
//! use dagx::{task, DagRunner, Task};
//! # use dagx::{task, DagRunner, Task};
//! # use futures::FutureExt;
//!
//! // Just derive Clone - that's all you need!
//! #[derive(Clone)]
@@ -473,7 +478,7 @@
//! let user = dag.add_task(CreateUser);
//! let formatted = dag.add_task(FormatUser).depends_on(&user);
//!
//! dag.run().await.unwrap();
//! dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap();
//!
//! assert_eq!(dag.get(formatted).unwrap(), "Alice is 30 years old");
//! # };
@@ -530,6 +535,7 @@
//!
//! ```no_run
//! # use dagx::{task, DagRunner, Task};
//! # use futures::FutureExt;
//! # struct Value(i32);
//! # #[task]
//! # impl Value {
@@ -539,11 +545,11 @@
//! # let dag = DagRunner::new();
//! # let node = dag.add_task(Value(42));
//! // Simple approach with .unwrap()
//! dag.run().await.unwrap();
//! dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap();
//! let result = dag.get(node).unwrap();
//!
//! // Or handle errors explicitly
//! match dag.run().await {
//! match dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await {
//! Ok(_) => println!("DAG executed successfully"),
//! Err(e) => eprintln!("DAG execution failed: {}", e),
//! }
@@ -626,7 +632,8 @@
//! You output `T`, the framework handles the Arc wrapping:
//!
//! ```
//! use dagx::{task, DagRunner, Task};
//! # use dagx::{task, DagRunner, Task};
//! # use futures::FutureExt;
//!
//! // ✅ CORRECT: Just output Vec<String>, framework wraps in Arc internally
//! struct FetchData;
@@ -656,7 +663,7 @@
//! let task2 = dag.add_task(ProcessData).depends_on(&data);
//! let task3 = dag.add_task(ProcessData).depends_on(&data);
//!
//! dag.run().await.unwrap();
//! dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap();
//! # };
//! ```
//!
+13 -63
View File
@@ -1,5 +1,7 @@
//! Unit tests for node module
use std::sync::Arc;
use crate::node::{ExecutableNode, TypedNode};
use crate::task::Task;
use crate::types::NodeId;
@@ -16,28 +18,6 @@ impl TestTask {
}
}
#[test]
fn test_create_output_channels() {
let node = TypedNode::new(NodeId(0), TestTask { value: 42 });
// Create channels for 3 dependents
let (senders, receivers) = node.create_output_channels(3);
assert_eq!(senders.len(), 3);
assert_eq!(receivers.len(), 3);
}
#[test]
fn test_create_output_channels_zero_dependents() {
let node = TypedNode::new(NodeId(0), TestTask { value: 42 });
// Create channels for 0 dependents (sink node)
let (senders, receivers) = node.create_output_channels(0);
assert_eq!(senders.len(), 0);
assert_eq!(receivers.len(), 0);
}
struct TaskWithDependency;
#[crate::task]
@@ -48,15 +28,14 @@ impl TaskWithDependency {
}
#[tokio::test]
async fn test_execute_with_channels_missing_input() {
async fn test_execute_with_deps_missing_input() {
// Test when task expects input but doesn't receive it
let node = Box::new(TypedNode::new(NodeId(0), TaskWithDependency));
// TaskWithDependency expects an i32 input, but we provide no receivers
let receivers = vec![];
let senders = vec![];
// TaskWithDependency expects an i32 input, but we provide no dependencies
let dependencies = vec![];
let result = node.execute_with_channels(receivers, senders).await;
let result = node.execute_with_deps(dependencies).await;
assert!(result.is_err());
if let Err(crate::error::DagError::InvalidDependency { task_id }) = result {
@@ -76,11 +55,11 @@ async fn test_execute_with_deps_success() {
let dep_dependencies = vec![];
// Execute in background
let dep_handle = tokio::spawn(async move {
dependency_node
.execute_with_channels(dep_receivers, dep_senders)
let dep_result =
tokio::spawn(async move { dependency_node.execute_with_deps(dep_dependencies).await })
.await
});
.unwrap()
.unwrap();
// Execute dependent node with receiver
let dependencies = vec![dep_result as Arc<dyn std::any::Any + Send + Sync>];
@@ -91,14 +70,14 @@ async fn test_execute_with_deps_success() {
}
#[tokio::test]
async fn test_execute_with_channels_sink_node() {
async fn test_execute_with_deps_sink_node() {
// Test that sink nodes (no dependents) return their output
let node = Box::new(TypedNode::new(NodeId(0), TestTask { value: 42 }));
// No senders means this is a sink node
let dependencies = vec![];
let result = node.execute_with_channels(receivers, senders).await;
let result = node.execute_with_deps(dependencies).await;
assert!(result.is_ok());
// Check output was returned (Arc-wrapped)
@@ -114,41 +93,12 @@ async fn test_execute_with_deps_non_sink_node() {
// Execute with senders (non-sink node)
let node = Box::new(node);
let exec_receivers = vec![];
let result = node.execute_with_channels(exec_receivers, senders).await;
let result = node.execute_with_deps(vec![]).await;
assert!(result.is_ok());
// Verify output was sent through channel (Arc-wrapped)
let arc_value = result.unwrap().downcast::<i32>().unwrap();
// Also verify output was returned (Arc-wrapped)
let output = result.unwrap();
let arc_value = output.downcast::<i32>().unwrap();
assert_eq!(*arc_value, 42);
}
#[tokio::test]
async fn test_execute_with_multiple_dependents() {
// Test that output is sent to multiple dependents
let node = TypedNode::new(NodeId(0), TestTask { value: 100 });
// Create output channels for three dependents
let (senders, receivers) = node.create_output_channels(3);
// Execute node
let node = Box::new(node);
let exec_receivers = vec![];
let result = node.execute_with_channels(exec_receivers, senders).await;
assert!(result.is_ok());
// Verify all three dependents receive the output (Arc-wrapped)
for receiver in receivers {
let rx = receiver
.downcast::<futures::channel::oneshot::Receiver<std::sync::Arc<i32>>>()
.unwrap();
let received_arc = rx.await;
assert!(received_arc.is_ok());
assert_eq!(*received_arc.unwrap(), 100);
}
}
+8 -4
View File
@@ -56,6 +56,7 @@ impl<'a> Drop for RunGuard<'a> {
///
/// ```no_run
/// # use dagx::{task, DagRunner, Task};
/// # use futures::FutureExt;
/// // Task with state constructed via ::new()
/// struct LoadValue { value: i32 }
///
@@ -84,7 +85,7 @@ impl<'a> Drop for RunGuard<'a> {
/// let y = dag.add_task(LoadValue::new(3));
/// let sum = dag.add_task(Add).depends_on((&x, &y));
///
/// dag.run().await.unwrap();
/// dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap();
///
/// assert_eq!(dag.get(sum).unwrap(), 5);
/// # };
@@ -149,6 +150,7 @@ impl DagRunner {
///
/// ```no_run
/// # use dagx::{task, DagRunner, Task};
/// # use futures::FutureExt;
/// // Task with state - shows you construct with specific value
/// struct LoadValue {
/// initial: i32,
@@ -190,7 +192,7 @@ impl DagRunner {
/// // Construct task with offset of 1
/// let inc = dag.add_task(AddOffset::new(1)).depends_on(&base);
///
/// dag.run().await.unwrap();
/// dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap();
/// assert_eq!(dag.get(&inc).unwrap(), 11);
/// # };
/// ```
@@ -254,6 +256,7 @@ impl DagRunner {
///
/// ```
/// # use dagx::{task, DagRunner, Task};
/// # use futures::FutureExt;
/// // Tuple struct
/// struct Value(i32);
///
@@ -277,7 +280,7 @@ impl DagRunner {
/// let b = dag.add_task(Value(2));
/// let sum = dag.add_task(Add).depends_on((&a, &b));
///
/// dag.run().await.unwrap(); // Executes all tasks
/// dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap(); // Executes all tasks
/// # };
/// ```
#[inline]
@@ -527,6 +530,7 @@ impl DagRunner {
///
/// ```no_run
/// # use dagx::{task, DagRunner, Task};
/// # use futures::FutureExt;
/// struct Configuration {
/// setting: i32,
/// }
@@ -548,7 +552,7 @@ impl DagRunner {
/// // Construct task with specific setting value
/// let task = dag.add_task(Configuration::new(42));
///
/// dag.run().await.unwrap();
/// dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap();
///
/// assert_eq!(dag.get(task).unwrap(), 42);
/// # };
+19 -30
View File
@@ -6,6 +6,7 @@ use crate::error::DagError;
use crate::runner::DagRunner;
use crate::task::Task;
use crate::types::TaskHandle;
use futures::FutureExt;
use std::collections::HashMap;
#[tokio::test]
@@ -20,11 +21,9 @@ async fn test_get_type_mismatch_error() {
// Task outputs i32
let source = dag.add_task(task_fn(|_: ()| async { 42i32 }));
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Try to get as String - TYPE MISMATCH!
// Create a handle with the same ID but wrong type
@@ -56,11 +55,9 @@ async fn test_get_type_mismatch_i32_to_vec() {
// Task outputs i32
let int_task = dag.add_task(task_fn(|_: ()| async { 123i32 }));
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Try to get as Vec<u8>
let wrong_handle: TaskHandle<Vec<u8>> = TaskHandle {
@@ -102,11 +99,9 @@ async fn test_get_type_mismatch_struct_to_hashmap() {
let dag = DagRunner::new();
let task_handle = dag.add_task(CustomTask);
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Try to get as HashMap
let wrong_handle: TaskHandle<HashMap<String, i32>> = TaskHandle {
@@ -136,11 +131,9 @@ async fn test_get_type_mismatch_vec_different_types() {
// Task outputs Vec<i32>
let vec_task = dag.add_task(task_fn(|_: ()| async { vec![1, 2, 3] }));
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Try to get as Vec<String>
let wrong_handle: TaskHandle<Vec<String>> = TaskHandle {
@@ -170,11 +163,9 @@ async fn test_get_type_mismatch_option_types() {
// Task outputs Option<i32>
let opt_task = dag.add_task(task_fn(|_: ()| async { Some(42) }));
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Try to get as Option<String>
let wrong_handle: TaskHandle<Option<String>> = TaskHandle {
@@ -206,11 +197,9 @@ async fn test_multiple_type_mismatches_same_dag() {
let string_task = dag.add_task(task_fn(|_: ()| async { "hello".to_string() }));
let bool_task = dag.add_task(task_fn(|_: ()| async { true }));
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Try to get int_task as String
let wrong_int_handle: TaskHandle<String> = TaskHandle {
+14 -28
View File
@@ -5,6 +5,8 @@ use crate::runner::DagRunner;
use crate::task::Task;
use crate::types::TaskHandle;
use futures::FutureExt;
// Initialize tracing subscriber for tests (idempotent)
#[cfg(feature = "tracing")]
fn init_tracing() {
@@ -68,11 +70,9 @@ async fn test_get_wrong_type() {
let dag = DagRunner::new();
let handle = dag.add_task(TestTask { value: 42 });
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Try to get with wrong type - downcast will fail
let fake_handle: TaskHandle<String> = TaskHandle {
@@ -145,18 +145,12 @@ async fn test_concurrent_run_protection() {
// Start two runs concurrently
let handle1 = tokio::spawn(async move {
barrier1.wait().await;
dag1.run(|fut| {
tokio::spawn(fut);
})
.await
dag1.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await
});
let handle2 = tokio::spawn(async move {
barrier2.wait().await;
dag2.run(|fut| {
tokio::spawn(fut);
})
.await
dag2.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await
});
// One should succeed, one should fail (concurrent execution not supported)
@@ -205,11 +199,9 @@ async fn test_multiple_get_calls() {
let dag = DagRunner::new();
let handle = dag.add_task(TestTask { value: 100 });
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Multiple get calls should all return the same value
let result1 = dag.get(&handle).unwrap();
@@ -233,11 +225,9 @@ async fn test_invalid_node_id_in_get() {
_phantom: std::marker::PhantomData,
};
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
let result = dag.get(invalid_handle);
assert!(result.is_err());
@@ -288,11 +278,7 @@ async fn test_task_panic_in_multi_task_layer() {
let _good_task = dag.add_task(GoodTask).depends_on(&source);
// Run the DAG - the panic should be caught
let result = dag
.run(|fut| {
tokio::spawn(fut);
})
.await;
let result = dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await;
// The run should fail due to the panic
// Note: Tokio catches panics in spawned tasks, so the behavior depends on the runtime
+2 -1
View File
@@ -3,6 +3,7 @@
use crate::task::{task_fn, Task};
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
// Test the task_fn function and TaskFn struct
#[tokio::test]
@@ -87,7 +88,7 @@ impl Task for CustomTask {
#[allow(refining_impl_trait)]
fn extract_and_run(
self,
_receivers: Vec<Box<dyn std::any::Any + Send>>,
_dependencies: Vec<Arc<dyn std::any::Any + Send + Sync>>,
) -> Pin<Box<dyn Future<Output = Result<Self::Output, String>> + Send>> {
Box::pin(async move {
// This is a test helper - just return an error
+4 -3
View File
@@ -6,9 +6,9 @@
use crate::builder::{IsUnitType, TaskBuilder};
use crate::task::Task;
/// Opaque node identifier (internal use)
/// Opaque node identifier
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub(crate) struct NodeId(pub usize);
pub struct NodeId(pub usize);
/// Opaque, typed token for a node's output.
///
@@ -23,6 +23,7 @@ pub(crate) struct NodeId(pub usize);
///
/// ```no_run
/// # use dagx::{task, DagRunner, Task};
/// # use futures::FutureExt;
/// # struct LoadValue { value: i32 }
/// # impl LoadValue { pub fn new(v: i32) -> Self { Self { value: v } } }
/// # #[task]
@@ -33,7 +34,7 @@ pub(crate) struct NodeId(pub usize);
/// let dag = DagRunner::new();
/// let node = dag.add_task(LoadValue::new(42));
///
/// dag.run().await.unwrap();
/// dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await.unwrap();
///
/// assert_eq!(dag.get(node).unwrap(), 42);
/// # };
+16 -25
View File
@@ -4,6 +4,7 @@
use crate::types::TaskHandle;
use crate::{task, DagRunner, Task};
use futures::FutureExt;
#[tokio::test]
async fn test_unit_type_taskbuilder_to_handle_conversion() {
@@ -31,11 +32,9 @@ async fn test_unit_type_taskbuilder_to_handle_conversion() {
// specifically covering line 71
let handle: TaskHandle<i32> = builder.into();
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Verify the task ran and we can get its output
assert_eq!(dag.get(handle).unwrap(), 42);
@@ -81,11 +80,9 @@ async fn test_unit_type_conversion_with_multiple_tasks() {
let second_handle: TaskHandle<i32> = second_builder.into();
let third_handle: TaskHandle<bool> = third_builder.into();
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Verify all outputs
assert_eq!(dag.get(first_handle).unwrap(), "first");
@@ -122,11 +119,9 @@ async fn test_unit_struct_with_state() {
// Explicit conversion - triggers line 71
let handle: TaskHandle<i32> = builder.into();
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(dag.get(handle).unwrap(), 42);
}
@@ -151,11 +146,9 @@ async fn test_taskbuilder_conversion_assignment() {
let handle: TaskHandle<Vec<i32>> = builder.into();
let id = handle.id;
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
let result = dag.get(handle).unwrap();
assert_eq!(result, vec![1, 2, 3]);
@@ -197,11 +190,9 @@ async fn test_mixed_unit_and_dependent_tasks() {
// Doubler needs dependencies, so we use it differently
let doubler = dag.add_task(Doubler).depends_on(source_handle);
dag.run(|fut| {
tokio::spawn(fut);
})
.await
.unwrap();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
assert_eq!(dag.get(source_handle).unwrap(), 10);
assert_eq!(dag.get(doubler).unwrap(), 20);