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

+6 -19
View File
@@ -1,6 +1,5 @@
//! Unit tests for builder module
use crate::builder::IsUnitType;
use crate::runner::DagRunner;
use crate::types::TaskHandle;
@@ -52,24 +51,12 @@ fn test_task_builder_from_conversion() {
let builder = dag.add_task(TestTask { value: 42 });
// Convert builder to TaskHandle
let handle: TaskHandle<i32> = builder.into();
let handle: TaskHandle<_> = builder.into();
// Check that the ID is preserved
assert_eq!(handle.id.0, 0); // First task should have ID 0
}
#[test]
fn test_is_unit_type_trait() {
// Test that IsUnitType is correctly implemented for ()
fn assert_is_unit_type<T: IsUnitType>() {}
// This should compile - () implements IsUnitType
assert_is_unit_type::<()>();
// We can't test the negative case (non-unit types don't implement it)
// because that would be a compile error
}
#[test]
fn test_task_builder_depends_on_returns_handle() {
init_tracing();
@@ -79,7 +66,7 @@ fn test_task_builder_depends_on_returns_handle() {
let dependent = dag.add_task(TestTaskWithInput).depends_on(source);
// depends_on should return a TaskHandle
let _handle: TaskHandle<i32> = dependent;
let _handle: TaskHandle<_> = dependent;
}
#[test]
@@ -92,9 +79,9 @@ fn test_task_builder_chain() {
let t3 = dag.add_task(TestTaskWithInput).depends_on(t2);
// All should return TaskHandles
let _: TaskHandle<i32> = t1;
let _: TaskHandle<i32> = t2;
let _: TaskHandle<i32> = t3;
let _: TaskHandle<_> = t1;
let _: TaskHandle<_> = t2;
let _: TaskHandle<_> = t3;
}
#[test]
@@ -118,7 +105,7 @@ fn test_multiple_dependencies() {
let sum = dag.add_task(AddTask).depends_on((a, b));
// Should return a TaskHandle
let _: TaskHandle<i32> = sum;
let _: TaskHandle<_> = sum;
}
#[test]
+1 -1
View File
@@ -207,7 +207,7 @@ fn test_deps_handle_ref_explicit() {
// Create a reference and explicitly call to_node_ids
let handle_ref = &handle;
let node_ids = DepsTuple::<i32>::to_node_ids(handle_ref);
let node_ids = DepsTuple::<(i32,)>::to_node_ids(handle_ref);
assert_eq!(node_ids.len(), 1);
assert_eq!(node_ids[0], NodeId(77));
-290
View File
@@ -1,290 +0,0 @@
//! Coverage tests for uncovered error paths in extract.rs
//!
//! These tests specifically target type mismatch error messages that aren't hit
//! in normal usage because the #[task] macro generates type-safe code.
//! We directly call extract_from_deps with mismatched types.
use crate::extract::ExtractInput;
use std::any::Any;
use std::collections::HashMap;
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 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![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());
let err_msg = result.unwrap_err();
// The error should mention Result type mismatch (line 95)
assert!(
err_msg.contains("Result"),
"Expected type mismatch error mentioning Result, got: {}",
err_msg
);
assert!(
err_msg.contains("Type mismatch"),
"Expected 'Type mismatch' in error, got: {}",
err_msg
);
}
#[tokio::test]
async fn test_option_type_mismatch_coverage() {
// Tests line 124 in extract.rs - Option<T> type mismatch error message
// 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![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());
let err_msg = result.unwrap_err();
// The error should mention Option type mismatch (line 124)
assert!(
err_msg.contains("Option"),
"Expected type mismatch error mentioning Option, got: {}",
err_msg
);
assert!(
err_msg.contains("Type mismatch"),
"Expected 'Type mismatch' in error, got: {}",
err_msg
);
}
#[tokio::test]
async fn test_vec_type_mismatch_coverage() {
// Tests line 152 in extract.rs - Vec<T> type mismatch error message
// 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![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());
let err_msg = result.unwrap_err();
// The error should mention Vec type mismatch (line 152)
assert!(
err_msg.contains("Vec"),
"Expected type mismatch error mentioning Vec, got: {}",
err_msg
);
assert!(
err_msg.contains("Type mismatch"),
"Expected 'Type mismatch' in error, got: {}",
err_msg
);
}
#[tokio::test]
async fn test_hashmap_type_mismatch_coverage() {
// Tests line 181 in extract.rs - HashMap<K,V> type mismatch error message
// 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![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());
let err_msg = result.unwrap_err();
// The error should mention HashMap type mismatch (line 181)
assert!(
err_msg.contains("HashMap"),
"Expected type mismatch error mentioning HashMap, got: {}",
err_msg
);
assert!(
err_msg.contains("Type mismatch"),
"Expected 'Type mismatch' in error, got: {}",
err_msg
);
}
#[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 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 channel
let result = Arc::<String>::extract_from_deps(deps).await;
// Verify the error message contains the expected type information
assert!(result.is_err());
let err_msg = result.unwrap_err();
// The error should mention Arc type mismatch (line 211)
assert!(
err_msg.contains("Arc"),
"Expected type mismatch error mentioning Arc, got: {}",
err_msg
);
assert!(
err_msg.contains("Type mismatch"),
"Expected 'Type mismatch' in error, got: {}",
err_msg
);
}
// Additional tests for better coverage of complex scenarios
#[tokio::test]
async fn test_result_with_wrong_ok_type() {
// Test Result with wrong Ok type
// Try to extract as Result<i32, String> - wrong Ok type
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();
assert!(
err_msg.contains("Result") && err_msg.contains("Type mismatch"),
"Expected Result type mismatch, got: {}",
err_msg
);
}
#[tokio::test]
async fn test_result_with_wrong_err_type() {
// Test Result with wrong Err type
// Try to extract as Result<i32, String> - wrong Err type
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();
assert!(
err_msg.contains("Result") && err_msg.contains("Type mismatch"),
"Expected Result type mismatch, got: {}",
err_msg
);
}
#[tokio::test]
async fn test_option_with_wrong_inner_type() {
// Test Option with wrong inner type
// Try to extract as Option<i32> - wrong inner type
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();
assert!(
err_msg.contains("Option") && err_msg.contains("Type mismatch"),
"Expected Option type mismatch, got: {}",
err_msg
);
}
#[tokio::test]
async fn test_vec_with_wrong_element_type() {
// Test Vec with wrong element type
// Try to extract as Vec<i32> - wrong element type
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();
assert!(
err_msg.contains("Vec") && err_msg.contains("Type mismatch"),
"Expected Vec type mismatch, got: {}",
err_msg
);
}
#[tokio::test]
async fn test_hashmap_with_wrong_key_type() {
// Test HashMap with wrong key type
let mut map = HashMap::new();
map.insert(1, "one".to_string());
// Try to extract as HashMap<String, String> - wrong key type
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();
assert!(
err_msg.contains("HashMap") && err_msg.contains("Type mismatch"),
"Expected HashMap type mismatch, got: {}",
err_msg
);
}
#[tokio::test]
async fn test_hashmap_with_wrong_value_type() {
// Test HashMap with wrong value type
let mut map = HashMap::new();
map.insert("key".to_string(), 42);
// Try to extract as HashMap<String, String> - wrong value type
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();
assert!(
err_msg.contains("HashMap") && err_msg.contains("Type mismatch"),
"Expected HashMap type mismatch, got: {}",
err_msg
);
}
#[tokio::test]
async fn test_arc_with_wrong_inner_type() {
// Test Arc with completely wrong inner type
// Send Arc<i32> but expect Arc<String>
// Try to extract as Arc<String> - wrong inner type
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();
assert!(
err_msg.contains("Arc") && err_msg.contains("Type mismatch"),
"Expected Arc type mismatch, got: {}",
err_msg
);
}
#[tokio::test]
async fn test_primitive_type_mismatch_for_completeness() {
// Test primitive type mismatch (already covered in existing tests but good for completeness)
// Try to extract as i32 - type mismatch
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();
assert!(
err_msg.contains("Type mismatch"),
"Expected type mismatch error, got: {}",
err_msg
);
}
#[tokio::test]
async fn test_complex_nested_result_option() {
// Test complex nested type: send Result<String, String>, expect Result<Option<i32>, String>
// Try to extract as Result<Option<i32>, String> - nested type mismatch
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();
assert!(
err_msg.contains("Result") && err_msg.contains("Type mismatch"),
"Expected Result type mismatch, got: {}",
err_msg
);
}
-296
View File
@@ -1,296 +0,0 @@
//! Unit tests for extract module
#![allow(dead_code)] // Test helpers generate unused code via macro
use crate::extract::ExtractInput;
use std::any::Any;
use std::sync::Arc;
// Test task for error scenarios
#[allow(dead_code)]
struct TestTask {
value: i32,
}
#[crate::task]
#[allow(dead_code)]
impl TestTask {
#[cfg(not(tarpaulin_include))]
async fn run(&self) -> i32 {
self.value
}
}
#[allow(dead_code)]
struct TestTaskWithInput;
#[crate::task]
#[allow(dead_code)]
impl TestTaskWithInput {
#[cfg(not(tarpaulin_include))]
async fn run(input: &i32) -> i32 {
input * 2
}
}
#[tokio::test]
async fn test_extract_result_type_single_dependency_error() {
// Test Result<T,E> with wrong dependency count
let deps = vec![
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_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 2"));
}
#[tokio::test]
async fn test_extract_result_type_success() {
// Test Result<T,E> successful extraction
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));
}
#[tokio::test]
async fn test_extract_option_type_single_dependency_error() {
// Test Option<T> with wrong dependency count
let deps = vec![
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_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 3"));
}
#[tokio::test]
async fn test_extract_option_type_success() {
// Test Option<T> successful extraction
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));
}
#[tokio::test]
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_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 0"));
}
#[tokio::test]
async fn test_extract_vec_type_success() {
// Test Vec<T> successful extraction
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]);
}
#[tokio::test]
async fn test_extract_primitive_single_dependency_error() {
// Test primitive types with wrong dependency count
let deps = vec![
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_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 2"));
// Test with String
let deps = vec![
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_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 2"));
// Test with f64
let deps = vec![
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_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 2"));
}
#[tokio::test]
async fn test_extract_unit_type() {
// Test that unit type always succeeds regardless of dependencies
let deps = vec![];
let result = <()>::extract_from_deps(deps).await;
assert!(result.is_ok());
// Even with dependencies (ignored), unit type succeeds
let deps = vec![
Arc::new(0) as Arc<dyn Any + Send + Sync>,
Arc::new(0) as Arc<dyn Any + Send + Sync>,
];
let result = <()>::extract_from_deps(deps).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_extract_tuple_basic() {
// Test tuple extraction
let deps = vec![
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_deps(deps).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), (42, 100));
}
#[tokio::test]
async fn test_extract_type_mismatch() {
// Test type mismatch during downcast
// 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());
assert!(result.unwrap_err().contains("Type mismatch"));
}
#[tokio::test]
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 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);
};
}
test_type!(i8, 42i8);
test_type!(i16, 42i16);
test_type!(i32, 42i32);
test_type!(i64, 42i64);
test_type!(String, "test".to_string());
}
#[tokio::test]
async fn test_extract_larger_tuples() {
// Test extraction for larger tuple sizes
let deps = vec![
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_deps(deps).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), (0, 1, 2, 3, 4));
}
#[tokio::test]
async fn test_extract_arc_type() {
// Test Arc<T> extraction
let test_arc = Arc::new("test".to_string());
let test_arc_clone = test_arc.clone();
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);
}
// Error path tests for ExtractInput implementations
use std::collections::HashMap;
#[tokio::test]
async fn test_hashmap_extract_wrong_dependency_count() {
let deps = vec![
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_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 2"));
}
#[tokio::test]
async fn test_hashmap_extract_type_mismatch() {
let deps = vec![Arc::new("wrong type".to_string()) as Arc<dyn Any + Send + Sync>];
let result = HashMap::<String, i32>::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Type mismatch"));
}
#[tokio::test]
async fn test_result_extract_downcast_failure() {
let deps = vec![Arc::new("wrong".to_string()) as Arc<dyn Any + Send + Sync>];
let result = Result::<i32, String>::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Type mismatch"));
}
#[tokio::test]
async fn test_option_extract_downcast_failure() {
let deps = vec![Arc::new("wrong".to_string()) as Arc<dyn Any + Send + Sync>];
let result = Option::<i32>::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Type mismatch"));
}
#[tokio::test]
async fn test_vec_extract_downcast_failure() {
let deps = vec![Arc::new("wrong".to_string()) as Arc<dyn Any + Send + Sync>];
let result = Vec::<i32>::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Type mismatch"));
}
#[tokio::test]
async fn test_arc_extract_wrong_dependency_count() {
let deps = vec![];
let result = Arc::<i32>::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Expected 1 dependency, got 0"));
}
#[tokio::test]
async fn test_arc_extract_downcast_failure() {
let deps = vec![Arc::new("wrong".to_string()) as Arc<dyn Any + Send + Sync>];
let result = Arc::<i32>::extract_from_deps(deps).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Type mismatch"));
}
#[tokio::test]
async fn test_tuple_wrong_dependency_count() {
let deps = vec![
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_deps(deps).await;
assert!(result.is_err());
assert!(result
.unwrap_err()
.contains("Expected 2 dependencies, got 3"));
}
+4 -23
View File
@@ -3,7 +3,6 @@
use std::sync::Arc;
use crate::node::{ExecutableNode, TypedNode};
use crate::types::NodeId;
// Simple test task
struct TestTask {
@@ -26,29 +25,11 @@ impl TaskWithDependency {
}
}
#[tokio::test]
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 dependencies
let dependencies = vec![];
let result = node.execute_with_deps(dependencies).await;
assert!(result.is_err());
if let Err(crate::error::DagError::InvalidDependency { task_id }) = result {
assert_eq!(task_id, 0);
} else {
panic!("Expected InvalidDependency error, got: {:?}", result);
}
}
#[tokio::test]
async fn test_execute_with_deps_success() {
// Test successful execution with channels
let dependency_node = Box::new(TypedNode::new(NodeId(0), TestTask { value: 21 }));
let dependent_node = Box::new(TypedNode::new(NodeId(1), TaskWithDependency));
let dependency_node = Box::new(TypedNode::new(TestTask { value: 21 }));
let dependent_node = Box::new(TypedNode::new(TaskWithDependency));
// Execute dependency node with sender
let dep_dependencies = vec![];
@@ -71,7 +52,7 @@ async fn test_execute_with_deps_success() {
#[tokio::test]
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 }));
let node = Box::new(TypedNode::new(TestTask { value: 42 }));
// No senders means this is a sink node
let dependencies = vec![];
@@ -88,7 +69,7 @@ async fn test_execute_with_deps_sink_node() {
#[tokio::test]
async fn test_execute_with_deps_non_sink_node() {
// Test that non-sink nodes send output via channels
let node = TypedNode::new(NodeId(0), TestTask { value: 42 });
let node = TypedNode::new(TestTask { value: 42 });
// Execute with senders (non-sink node)
let node = Box::new(node);
-237
View File
@@ -1,237 +0,0 @@
//! Coverage tests for runner module - specifically targeting uncovered error paths
//!
//! These tests ensure we hit the type mismatch error path in get() (line 601)
use crate::error::DagError;
use crate::runner::DagRunner;
use crate::types::TaskHandle;
use futures::FutureExt;
use std::collections::HashMap;
#[tokio::test]
async fn test_get_type_mismatch_error() {
// Test the type mismatch error path in get() - line 601 in runner.rs
// This occurs when Arc downcast fails due to type mismatch
use crate::task_fn;
let dag = DagRunner::new();
// Task outputs i32
let source = dag.add_task(task_fn(|_: ()| async { 42i32 }));
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
let wrong_handle: TaskHandle<String> = TaskHandle {
id: source.id,
_phantom: std::marker::PhantomData,
};
let result = dag.get(wrong_handle);
assert!(result.is_err());
let err = result.unwrap_err();
match err {
DagError::TypeMismatch { expected, found } => {
assert_eq!(expected, std::any::type_name::<String>());
assert_eq!(found, "unknown"); // This covers line 601!
}
_ => panic!("Expected TypeMismatch error, got: {:?}", err),
}
}
#[tokio::test]
async fn test_get_type_mismatch_i32_to_vec() {
// Different type mismatch: i32 to Vec<u8>
use crate::task_fn;
let dag = DagRunner::new();
// Task outputs i32
let int_task = dag.add_task(task_fn(|_: ()| async { 123i32 }));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Try to get as Vec<u8>
let wrong_handle: TaskHandle<Vec<u8>> = TaskHandle {
id: int_task.id,
_phantom: std::marker::PhantomData,
};
let result = dag.get(wrong_handle);
assert!(result.is_err());
match result.unwrap_err() {
DagError::TypeMismatch { expected, found } => {
assert_eq!(expected, std::any::type_name::<Vec<u8>>());
assert_eq!(found, "unknown");
}
_ => panic!("Expected TypeMismatch error"),
}
}
#[tokio::test]
async fn test_get_type_mismatch_struct_to_hashmap() {
// Complex type mismatch: custom struct to HashMap
#[derive(Clone)]
#[allow(dead_code)]
struct CustomOutput {
value: i32,
}
struct CustomTask;
#[crate::task]
impl CustomTask {
async fn run(&self) -> CustomOutput {
CustomOutput { value: 42 }
}
}
let dag = DagRunner::new();
let task_handle = dag.add_task(CustomTask);
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Try to get as HashMap
let wrong_handle: TaskHandle<HashMap<String, i32>> = TaskHandle {
id: task_handle.id,
_phantom: std::marker::PhantomData,
};
let result = dag.get(wrong_handle);
assert!(result.is_err());
match result.unwrap_err() {
DagError::TypeMismatch { expected, found } => {
assert!(expected.contains("HashMap"));
assert_eq!(found, "unknown");
}
_ => panic!("Expected TypeMismatch error"),
}
}
#[tokio::test]
async fn test_get_type_mismatch_vec_different_types() {
// Type mismatch between Vec<i32> and Vec<String>
use crate::task_fn;
let dag = DagRunner::new();
// Task outputs Vec<i32>
let vec_task = dag.add_task(task_fn(|_: ()| async { vec![1, 2, 3] }));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Try to get as Vec<String>
let wrong_handle: TaskHandle<Vec<String>> = TaskHandle {
id: vec_task.id,
_phantom: std::marker::PhantomData,
};
let result = dag.get(wrong_handle);
assert!(result.is_err());
match result.unwrap_err() {
DagError::TypeMismatch { expected, found } => {
assert_eq!(expected, std::any::type_name::<Vec<String>>());
assert_eq!(found, "unknown");
}
_ => panic!("Expected TypeMismatch error"),
}
}
#[tokio::test]
async fn test_get_type_mismatch_option_types() {
// Type mismatch with Option types
use crate::task_fn;
let dag = DagRunner::new();
// Task outputs Option<i32>
let opt_task = dag.add_task(task_fn(|_: ()| async { Some(42) }));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Try to get as Option<String>
let wrong_handle: TaskHandle<Option<String>> = TaskHandle {
id: opt_task.id,
_phantom: std::marker::PhantomData,
};
let result = dag.get(wrong_handle);
assert!(result.is_err());
match result.unwrap_err() {
DagError::TypeMismatch { expected, found } => {
assert_eq!(expected, std::any::type_name::<Option<String>>());
assert_eq!(found, "unknown");
}
_ => panic!("Expected TypeMismatch error"),
}
}
#[tokio::test]
async fn test_multiple_type_mismatches_same_dag() {
// Test multiple type mismatches in the same DAG
use crate::task_fn;
let dag = DagRunner::new();
// Create tasks with different output types
let int_task = dag.add_task(task_fn(|_: ()| async { 100i32 }));
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).map(Result::unwrap))
.await
.unwrap();
// Try to get int_task as String
let wrong_int_handle: TaskHandle<String> = TaskHandle {
id: int_task.id,
_phantom: std::marker::PhantomData,
};
assert!(matches!(
dag.get(wrong_int_handle).unwrap_err(),
DagError::TypeMismatch { found, .. } if found == "unknown"
));
// Try to get string_task as bool
let wrong_string_handle: TaskHandle<bool> = TaskHandle {
id: string_task.id,
_phantom: std::marker::PhantomData,
};
assert!(matches!(
dag.get(wrong_string_handle).unwrap_err(),
DagError::TypeMismatch { found, .. } if found == "unknown"
));
// Try to get bool_task as i32
let wrong_bool_handle: TaskHandle<i32> = TaskHandle {
id: bool_task.id,
_phantom: std::marker::PhantomData,
};
assert!(matches!(
dag.get(wrong_bool_handle).unwrap_err(),
DagError::TypeMismatch { found, .. } if found == "unknown"
));
// Verify correct types still work
assert_eq!(dag.get(int_task).unwrap(), 100);
assert_eq!(dag.get(string_task).unwrap(), "hello");
assert!(dag.get(bool_task).unwrap());
}
-21
View File
@@ -185,27 +185,6 @@ fn test_add_task_increments_id() {
assert_eq!(dag.nodes.lock().len(), 3);
}
#[tokio::test]
async fn test_multiple_get_calls() {
init_tracing();
// Test that get() can be called multiple times for the same handle
let dag = DagRunner::new();
let handle: TaskHandle<_> = dag.add_task(TestTask { value: 100 }).into();
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();
let result2 = dag.get(handle).unwrap();
let result3 = dag.get(handle).unwrap();
assert_eq!(result1, 100);
assert_eq!(result2, 100);
assert_eq!(result3, 100);
}
#[tokio::test]
async fn test_invalid_node_id_in_get() {
init_tracing();
+84 -43
View File
@@ -1,6 +1,7 @@
//! Unit tests for task module
use crate::task::{task_fn, Task};
use crate::task::{task_fn, Task, TaskInput};
use std::any::Any;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
@@ -11,50 +12,74 @@ async fn test_task_fn_creation_and_execution() {
// Test line 111 in task.rs - Task implementation for TaskFn
// Create a simple task function
let task = task_fn(|input: i32| async move { input * 2 });
let task = task_fn::<i32, _, _>(|input: &i32| input * 2);
// Test the Task trait implementation
let result = task.run(42).await;
let result = task
.run(TaskInput::new(
vec![Arc::new(42) as Arc<dyn Any + Send + Sync + 'static>].iter(),
))
.await;
assert_eq!(result, 84);
}
#[tokio::test]
async fn test_task_fn_with_unit_input() {
// Test task_fn with unit input
let task = task_fn(|_: ()| async { 100 });
let task = task_fn::<(), _, _>(|_: ()| 100);
let result = task.run(()).await;
let result = task
.run(TaskInput::new(
vec![Arc::new(()) as Arc<dyn Any + Send + Sync + 'static>].iter(),
))
.await;
assert_eq!(result, 100);
}
#[tokio::test]
async fn test_task_fn_with_tuple_input() {
// Test task_fn with tuple input
let task = task_fn(|(a, b): (i32, i32)| async move { a + b });
let task = task_fn::<(i32, i32), _, _>(|(a, b): (&i32, &i32)| a + b);
let result = task.run((10, 20)).await;
let result = task
.run(TaskInput::new(
vec![
Arc::new(10) as Arc<dyn Any + Send + Sync + 'static>,
Arc::new(20) as Arc<dyn Any + Send + Sync + 'static>,
]
.iter(),
))
.await;
assert_eq!(result, 30);
}
#[tokio::test]
async fn test_task_fn_with_string_input() {
// Test task_fn with String input
let task = task_fn(|input: String| {
let task = task_fn::<String, _, _>(|input: &String| {
let len = input.len();
async move { len }
len
});
let test_string = String::from("hello");
let result = task.run(test_string).await;
let result = task
.run(TaskInput::new(
vec![Arc::new(test_string) as Arc<dyn Any + Send + Sync + 'static>].iter(),
))
.await;
assert_eq!(result, 5);
}
#[tokio::test]
async fn test_task_fn_with_complex_output() {
// Test task_fn with complex output type
let task = task_fn(|input: i32| async move { vec![input, input * 2, input * 3] });
let task = task_fn::<i32, _, _>(|&input: &i32| vec![input, input * 2, input * 3]);
let result = task.run(5).await;
let result = task
.run(TaskInput::new(
vec![Arc::new(5) as Arc<dyn Any + Send + Sync + 'static>].iter(),
))
.await;
assert_eq!(result, vec![5, 10, 15]);
}
@@ -62,9 +87,13 @@ async fn test_task_fn_with_complex_output() {
async fn test_task_fn_stateful_closure() {
// Test that task_fn can capture state
let multiplier = 3;
let task = task_fn(move |input: i32| async move { input * multiplier });
let task = task_fn::<i32, _, _>(move |input: &i32| input * multiplier);
let result = task.run(7).await;
let result = task
.run(TaskInput::new(
vec![Arc::new(7) as Arc<dyn Any + Send + Sync + 'static>].iter(),
))
.await;
assert_eq!(result, 21);
}
@@ -74,34 +103,30 @@ struct CustomTask {
}
impl Task for CustomTask {
type Input = i32;
type Input = (i32,);
type Output = i32;
#[allow(refining_impl_trait)]
fn run(mut self, input: Self::Input) -> Pin<Box<dyn Future<Output = Self::Output> + Send>> {
fn run(
mut self,
input: TaskInput<'_, Self::Input>,
) -> Pin<Box<dyn Future<Output = Self::Output> + Send + '_>> {
Box::pin(async move {
self.state += input;
self.state += input.next().0;
self.state
})
}
#[allow(refining_impl_trait)]
fn extract_and_run(
self,
_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
Err("extract_and_run not implemented for CustomTask (test helper)".to_string())
})
}
}
#[tokio::test]
async fn test_custom_task_implementation() {
let task = CustomTask { state: 10 };
let result1 = task.run(5).await;
let result1 = task
.run(TaskInput::new(
vec![Arc::new(5) as Arc<dyn Any + Send + Sync + 'static>].iter(),
))
.await;
assert_eq!(result1, 15);
// Can't call run again since task was consumed
@@ -131,15 +156,21 @@ fn test_task_trait_bounds() {
#[tokio::test]
async fn test_task_fn_single_call() {
// Test that task_fn works (can only call once due to ownership)
let task = task_fn(|x: i32| async move { x + 1 });
let task = task_fn::<i32, _, _>(|x: &i32| x + 1);
assert_eq!(task.run(1).await, 2);
assert_eq!(
task.run(TaskInput::new(
vec![Arc::new(1) as Arc<dyn Any + Send + Sync + 'static>].iter()
))
.await,
2
);
}
#[tokio::test]
async fn test_task_fn_with_result_type() {
// Test task_fn returning Result
let task = task_fn(|x: i32| async move {
let task = task_fn::<i32, _, _>(|&x: &i32| {
if x > 0 {
Ok(x * 2)
} else {
@@ -147,30 +178,40 @@ async fn test_task_fn_with_result_type() {
}
});
assert_eq!(task.run(5).await, Ok(10));
assert_eq!(
task.run(TaskInput::new(
vec![Arc::new(5) as Arc<dyn Any + Send + Sync + 'static>].iter()
))
.await,
Ok(10)
);
}
#[tokio::test]
async fn test_task_fn_with_option_type() {
// Test task_fn returning Option
let task = task_fn(|x: i32| async move {
if x > 0 {
Some(x * 2)
} else {
None
}
});
let task = task_fn::<i32, _, _>(|&x: &i32| if x > 0 { Some(x * 2) } else { None });
assert_eq!(task.run(5).await, Some(10));
assert_eq!(
task.run(TaskInput::new(
vec![Arc::new(5) as Arc<dyn Any + Send + Sync + 'static>].iter()
))
.await,
Some(10)
);
}
#[tokio::test]
async fn test_task_fn_explicit_trait_call() {
// Test explicit Task trait method call to cover line 121
let task = task_fn(|x: i32| async move { x * 3 });
let task = task_fn::<i32, _, _>(|x: &i32| x * 3);
// Explicitly call the Task trait's run method
let result = Task::run(task, 10).await;
let result = Task::run(
task,
TaskInput::new(vec![Arc::new(10) as Arc<dyn Any + Send + Sync + 'static>].iter()),
)
.await;
assert_eq!(result, 30);
}
+7 -15
View File
@@ -29,7 +29,7 @@ async fn test_unit_type_taskbuilder_to_handle_conversion() {
// Explicitly convert to TaskHandle using From implementation
// This triggers the From<TaskBuilder> implementation at line 69-74,
// specifically covering line 71
let handle: TaskHandle<i32> = builder.into();
let handle: TaskHandle<_> = builder.into();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
@@ -76,7 +76,7 @@ async fn test_unit_type_conversion_with_multiple_tasks() {
// Convert each builder to handle explicitly
let first_handle: TaskHandle<String> = first_builder.into();
let second_handle: TaskHandle<i32> = second_builder.into();
let second_handle: TaskHandle<_> = second_builder.into();
let third_handle: TaskHandle<bool> = third_builder.into();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
@@ -116,7 +116,7 @@ async fn test_unit_struct_with_state() {
let builder = dag.add_task(StatefulTask::new(21));
// Explicit conversion - triggers line 71
let handle: TaskHandle<i32> = builder.into();
let handle: TaskHandle<_> = builder.into();
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
@@ -143,7 +143,6 @@ async fn test_taskbuilder_conversion_assignment() {
// Conversion through type annotation and assignment
// This also uses From<TaskBuilder> implementation
let handle: TaskHandle<Vec<i32>> = builder.into();
let id = handle.id;
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
@@ -151,13 +150,6 @@ async fn test_taskbuilder_conversion_assignment() {
let result = dag.get(handle).unwrap();
assert_eq!(result, vec![1, 2, 3]);
// Verify the ID was preserved correctly through conversion
let another_handle = TaskHandle::<Vec<i32>> {
id,
_phantom: std::marker::PhantomData,
};
assert_eq!(dag.get(another_handle).unwrap(), vec![1, 2, 3]);
}
#[tokio::test]
@@ -184,7 +176,7 @@ async fn test_mixed_unit_and_dependent_tasks() {
// Source is unit-type, can be converted
let source_builder = dag.add_task(Source);
let source_handle: TaskHandle<i32> = source_builder.into(); // Covers line 71
let source_handle: TaskHandle<_> = source_builder.into(); // Covers line 71
// Doubler needs dependencies, so we use it differently
let doubler = dag.add_task(Doubler).depends_on(source_handle);
@@ -260,9 +252,9 @@ fn test_multiple_conversions_preserve_ids() {
let id3 = builder3.id;
// Convert all builders
let handle1: TaskHandle<i32> = builder1.into();
let handle2: TaskHandle<i32> = builder2.into();
let handle3: TaskHandle<i32> = builder3.into();
let handle1: TaskHandle<_> = builder1.into();
let handle2: TaskHandle<_> = builder2.into();
let handle3: TaskHandle<_> = builder3.into();
// Verify IDs are preserved and sequential
assert_eq!(handle1.id, id1);
+2 -2
View File
@@ -40,7 +40,7 @@ fn test_node_id_operations() {
#[test]
fn test_task_handle_clone() {
let handle: TaskHandle<i32> = TaskHandle {
let handle: TaskHandle<(i32,)> = TaskHandle {
id: NodeId(42),
_phantom: PhantomData,
};
@@ -129,7 +129,7 @@ fn test_task_builder_from_conversion() {
let builder_id = builder.id;
// This conversion uses the From implementation
let handle: TaskHandle<i32> = builder.into();
let handle: TaskHandle<_> = builder.into();
assert_eq!(handle.id, builder_id);
}