@@ -37,6 +37,54 @@ fn test_all_non_inferable() {
3737 assert ! ( !col. all_non_inferable( ) ) ;
3838}
3939
40+ /// A mock `Inferable` whose `is_inferable` and `is_inverse_inferable` both
41+ /// return `true`. A real `Inference` can never be both (observation cannot be
42+ /// simultaneously above and below the threshold), so this mock is the only way
43+ /// to drive the "undecidable, hence non-inferable" `true` arm of
44+ /// `all_non_inferable`.
45+ #[ derive( Debug ) ]
46+ struct UndecidableInferable {
47+ id : u64 ,
48+ }
49+
50+ impl Identifiable for UndecidableInferable {
51+ fn id ( & self ) -> u64 {
52+ self . id
53+ }
54+ }
55+
56+ impl Inferable for UndecidableInferable {
57+ fn question ( & self ) -> DescriptionValue {
58+ DescriptionValue :: from ( "undecidable" )
59+ }
60+ fn observation ( & self ) -> NumericalValue {
61+ 1.0
62+ }
63+ fn threshold ( & self ) -> NumericalValue {
64+ 0.5
65+ }
66+ fn effect ( & self ) -> NumericalValue {
67+ 1.0
68+ }
69+ fn target ( & self ) -> NumericalValue {
70+ 1.0
71+ }
72+ fn is_inferable ( & self ) -> bool {
73+ true
74+ }
75+ fn is_inverse_inferable ( & self ) -> bool {
76+ true
77+ }
78+ }
79+
80+ #[ test]
81+ fn test_all_non_inferable_true_for_undecidable_item ( ) {
82+ let col: Vec < UndecidableInferable > = vec ! [ UndecidableInferable { id: 1 } ] ;
83+ // The single item is both inferable and inverse-inferable, so
84+ // `all_non_inferable` short-circuits and returns true.
85+ assert ! ( col. all_non_inferable( ) ) ;
86+ }
87+
4088#[ test]
4189fn test_conjoint_delta ( ) {
4290 let col = get_test_inf_vec ( ) ;
@@ -126,3 +174,63 @@ fn test_is_empty() {
126174 let col = get_test_inf_vec ( ) ;
127175 assert ! ( !InferableReasoning :: is_empty( & col) ) ;
128176}
177+
178+ // --- Per-item Inferable default-method coverage ---
179+
180+ #[ test]
181+ fn test_item_conjoint_delta ( ) {
182+ // Exercises the per-item `Inferable::conjoint_delta` default implementation.
183+ let inf = get_test_inferable ( 0 , false ) ;
184+ // conjoint_delta = abs(1.0 - observation)
185+ let expected = ( 1.0 - inf. observation ( ) ) . abs ( ) ;
186+ assert_eq ! ( inf. conjoint_delta( ) , expected) ;
187+ }
188+
189+ #[ test]
190+ fn test_item_is_inferable_true ( ) {
191+ let inf = get_test_inferable ( 0 , false ) ;
192+ assert ! ( inf. is_inferable( ) ) ;
193+ assert ! ( !inf. is_inverse_inferable( ) ) ;
194+ }
195+
196+ #[ test]
197+ fn test_item_is_inverse_inferable_true ( ) {
198+ let inf = get_test_inferable ( 0 , true ) ;
199+ assert ! ( inf. is_inverse_inferable( ) ) ;
200+ assert ! ( !inf. is_inferable( ) ) ;
201+ }
202+
203+ // --- Early-return branches in all_inferable / all_inverse_inferable ---
204+
205+ #[ test]
206+ fn test_all_inferable_returns_false_on_mixed_collection ( ) {
207+ // The first item is inverse-inferable (not inferable), so `all_inferable`
208+ // must short-circuit and return false on the first element.
209+ let col: Vec < Inference > =
210+ Vec :: from_iter ( [ get_test_inferable ( 0 , true ) , get_test_inferable ( 1 , false ) ] ) ;
211+ assert ! ( !col. all_inferable( ) ) ;
212+ }
213+
214+ #[ test]
215+ fn test_all_inverse_inferable_returns_false_on_mixed_collection ( ) {
216+ // The first item is inferable (not inverse-inferable), so
217+ // `all_inverse_inferable` must short-circuit and return false.
218+ let col: Vec < Inference > =
219+ Vec :: from_iter ( [ get_test_inferable ( 0 , false ) , get_test_inferable ( 1 , true ) ] ) ;
220+ assert ! ( !col. all_inverse_inferable( ) ) ;
221+ }
222+
223+ // --- Empty-collection guard branches ---
224+
225+ #[ test]
226+ fn test_empty_collection_metrics_short_circuit ( ) {
227+ let col: Vec < Inference > = Vec :: new ( ) ;
228+
229+ // conjoint_delta on an empty collection returns the neutral 1.0.
230+ assert_eq ! ( col. conjoint_delta( ) , 1.0 ) ;
231+
232+ // All percentage metrics return 0.0 on an empty collection.
233+ assert_eq ! ( col. percent_inferable( ) , 0.0 ) ;
234+ assert_eq ! ( col. percent_inverse_inferable( ) , 0.0 ) ;
235+ assert_eq ! ( col. percent_non_inferable( ) , 0.0 ) ;
236+ }
0 commit comments