1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! Property-based tests for traversable laws
//!
//! This file contains property-based tests (using proptest) to verify:
//! - Identity law: traverse(id) == id
//! - Composition law: traverse(compose(f, g)) == compose(traverse(f), traverse(g))
//! - Naturality law: t . traverse(f) == traverse(t . f) for natural transformation t
//! - Structure preservation: traverse preserves pattern structure (size, depth, length)
//!
//! Tests run 100+ random cases per law to ensure correctness
use pattern_core::Pattern;
use proptest::prelude::*;
// ====================================================================================
// Identity Law Tests for Option (T014)
// ====================================================================================
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
/// Identity law for Option: traverse(Some) should wrap the original pattern
///
/// For any pattern p, traverse_option(|v| Some(v.clone())) should equal Some(p.clone())
#[test]
fn identity_law_option(value in any::<i32>()) {
let pattern = Pattern::point(value);
let result = pattern.traverse_option(|v| Some(*v));
prop_assert_eq!(result, Some(pattern.clone()));
}
}
// ====================================================================================
// Structure Preservation Tests for Option (T015)
// ====================================================================================
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
/// Structure preservation for Option: traverse should not change pattern structure
///
/// If traverse succeeds, the resulting pattern should have:
/// - Same size (number of nodes)
/// - Same depth (nesting levels)
/// - Same length (number of direct elements)
#[test]
fn structure_preservation_option(value in -1000i32..1000i32) {
let pattern = Pattern::pattern(value, vec![
Pattern::point(value + 1),
Pattern::point(value + 2),
]);
let original_size = pattern.size();
let original_depth = pattern.depth();
let original_length = pattern.length();
// Use a simple transformation that won't overflow
let result = pattern.traverse_option(|v| Some(v + 1));
if let Some(new_pattern) = result {
prop_assert_eq!(new_pattern.size(), original_size);
prop_assert_eq!(new_pattern.depth(), original_depth);
prop_assert_eq!(new_pattern.length(), original_length);
}
}
}
// ====================================================================================
// Identity Law Tests for Result (T025)
// ====================================================================================
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
/// Identity law for Result: traverse(Ok) should wrap the original pattern
///
/// For any pattern p, traverse_result(|v| Ok(v.clone())) should equal Ok(p.clone())
#[test]
fn identity_law_result(value in any::<i32>()) {
let pattern = Pattern::point(value);
let result: Result<Pattern<i32>, String> = pattern.traverse_result(|v| Ok(*v));
prop_assert_eq!(result, Ok(pattern.clone()));
}
}
// ====================================================================================
// Structure Preservation Tests for Result (T026)
// ====================================================================================
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
/// Structure preservation for Result: traverse should not change pattern structure
///
/// If traverse succeeds, the resulting pattern should have:
/// - Same size (number of nodes)
/// - Same depth (nesting levels)
/// - Same length (number of direct elements)
#[test]
fn structure_preservation_result(value in -1000i32..1000i32) {
let pattern = Pattern::pattern(value, vec![
Pattern::point(value + 1),
Pattern::point(value + 2),
]);
let original_size = pattern.size();
let original_depth = pattern.depth();
let original_length = pattern.length();
// Use a simple transformation that won't overflow or fail
let result: Result<Pattern<i32>, String> = pattern.traverse_result(|v| Ok(v + 1));
if let Ok(new_pattern) = result {
prop_assert_eq!(new_pattern.size(), original_size);
prop_assert_eq!(new_pattern.depth(), original_depth);
prop_assert_eq!(new_pattern.length(), original_length);
}
}
}