@@ -289,3 +289,109 @@ fn test_debug() {
289289 let actual_active = format ! ( "{causaloid:?}" ) ;
290290 assert_eq ! ( actual_active, expected_active) ;
291291}
292+
293+ #[ test]
294+ fn test_evaluate_collection_with_halting_effect ( ) {
295+ // Setup: A collection where a Halting causaloid appears before a 'true' one.
296+ let halting_causaloid = test_utils:: get_test_causaloid_halting ( ) ;
297+ let true_causaloid = test_utils:: get_test_causaloid_deterministic_true ( ) ;
298+ let causal_coll = vec ! [ halting_causaloid, true_causaloid] ;
299+ let collection_causaloid =
300+ Causaloid :: from_causal_collection ( 100 , Arc :: new ( causal_coll) , "Halting Collection" ) ;
301+
302+ // Act
303+ let evidence = Evidence :: Numerical ( 0.0 ) ;
304+ let effect = collection_causaloid. evaluate ( & evidence) . unwrap ( ) ;
305+
306+ // Assert: The Halting effect should short-circuit the evaluation.
307+ assert_eq ! ( effect, PropagatingEffect :: Halting ) ;
308+ }
309+
310+ #[ test]
311+ fn test_evaluate_collection_without_true_effect ( ) {
312+ // Setup: A collection with only 'false' causaloids.
313+ let false_causaloid1 = test_utils:: get_test_causaloid_deterministic_false ( ) ;
314+ let false_causaloid2 = test_utils:: get_test_causaloid_deterministic_false ( ) ;
315+ let causal_coll = vec ! [ false_causaloid1, false_causaloid2] ;
316+ let collection_causaloid =
317+ Causaloid :: from_causal_collection ( 101 , Arc :: new ( causal_coll) , "All False Collection" ) ;
318+
319+ // Act
320+ let evidence = Evidence :: Numerical ( 0.0 ) ;
321+ let effect = collection_causaloid. evaluate ( & evidence) . unwrap ( ) ;
322+
323+ // Assert: Since no causaloid is true, the aggregated effect should be false.
324+ assert_eq ! ( effect, PropagatingEffect :: Deterministic ( false ) ) ;
325+ }
326+
327+ #[ test]
328+ fn test_evaluate_collection_with_sub_evaluation_error ( ) {
329+ // Setup: A collection containing a causaloid that will return an error.
330+ let error_causaloid = test_utils:: get_test_error_causaloid ( ) ;
331+ let true_causaloid = test_utils:: get_test_causaloid_deterministic_true ( ) ;
332+
333+ // The error_causaloid must come first to ensure it gets evaluated.
334+ let causal_coll = vec ! [ error_causaloid, true_causaloid] ; // <-- The order is swapped here.
335+
336+ let collection_causaloid =
337+ Causaloid :: from_causal_collection ( 102 , Arc :: new ( causal_coll) , "Error Collection" ) ;
338+
339+ // Act
340+ let evidence = Evidence :: Numerical ( 0.0 ) ;
341+ let result = collection_causaloid. evaluate ( & evidence) ;
342+
343+ // Assert: The error from the sub-causaloid should now be propagated up.
344+ assert ! ( result. is_err( ) ) ;
345+ let err = result. unwrap_err ( ) ;
346+ assert ! ( err. to_string( ) . contains( "Test error" ) ) ;
347+ }
348+ #[ test]
349+ fn test_explain_collection_success ( ) {
350+ // Setup: A collection causaloid that has been evaluated.
351+ let true_causaloid = test_utils:: get_test_causaloid_deterministic_true ( ) ;
352+ let false_causaloid = test_utils:: get_test_causaloid_deterministic_false ( ) ;
353+
354+ // The `false` causaloid must come first to ensure the `evaluate` loop
355+ // does not short-circuit before evaluating both.
356+ let causal_coll = vec ! [ false_causaloid, true_causaloid] ; // <-- Swapped order
357+
358+ let collection_causaloid =
359+ Causaloid :: from_causal_collection ( 104 , Arc :: new ( causal_coll) , "Explainable Collection" ) ;
360+
361+ // Act: Evaluate the collection. Now both members will be evaluated.
362+ let evidence = Evidence :: Numerical ( 0.0 ) ;
363+ collection_causaloid. evaluate ( & evidence) . unwrap ( ) ;
364+
365+ // Now, call explain.
366+ let explanation = collection_causaloid. explain ( ) . unwrap ( ) ;
367+
368+ // Assert: The explanation should contain the results from both sub-causaloids.
369+ assert ! ( explanation. contains( "evaluated to: Deterministic(true)" ) ) ;
370+ assert ! ( explanation. contains( "evaluated to: Deterministic(false)" ) ) ;
371+ }
372+ // This test covers an error path in explain() for a Collection Causaloid.
373+ #[ test]
374+ fn test_explain_collection_with_sub_explain_error ( ) {
375+ // Setup: A collection where one causaloid will not be evaluated due to short-circuiting.
376+ let true_causaloid = test_utils:: get_test_causaloid_deterministic_true ( ) ;
377+ let unevaluated_causaloid = test_utils:: get_test_causaloid ( ) ; // This one will remain unevaluated.
378+
379+ let causal_coll = vec ! [ true_causaloid, unevaluated_causaloid] ;
380+ let collection_causaloid = Causaloid :: from_causal_collection (
381+ 105 ,
382+ Arc :: new ( causal_coll) ,
383+ "Sub-explain Error Collection" ,
384+ ) ;
385+
386+ // Act: Evaluate the collection. The evaluation will stop after the first `true` effect.
387+ let evidence = Evidence :: Numerical ( 0.0 ) ;
388+ collection_causaloid. evaluate ( & evidence) . unwrap ( ) ;
389+
390+ // Now, call explain. This will fail because the second causaloid was never evaluated.
391+ let result = collection_causaloid. explain ( ) ;
392+
393+ // Assert: The result should be an error.
394+ assert ! ( result. is_err( ) ) ;
395+ let err_msg = result. unwrap_err ( ) . to_string ( ) ;
396+ assert ! ( err_msg. contains( "has not been evaluated" ) ) ;
397+ }
0 commit comments