1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct FalsificationTemplate {
10 pub categories: Vec<CategoryTemplate>,
12}
13
14impl Default for FalsificationTemplate {
15 fn default() -> Self {
16 Self {
17 categories: vec![
18 CategoryTemplate::boundary(),
19 CategoryTemplate::invariant(),
20 CategoryTemplate::numerical(),
21 CategoryTemplate::concurrency(),
22 CategoryTemplate::resource(),
23 CategoryTemplate::parity(),
24 ],
25 }
26 }
27}
28
29impl FalsificationTemplate {
30 pub fn total_points(&self) -> u32 {
32 self.categories.iter().map(|c| c.total_points()).sum()
33 }
34
35 pub fn scale_to_points(&self, target: u32) -> Self {
37 let current = self.total_points();
38 if current == target {
39 return self.clone();
40 }
41
42 let scale = target as f64 / current as f64;
43 let mut scaled = self.clone();
44
45 for category in &mut scaled.categories {
46 for test in &mut category.tests {
47 test.points = ((test.points as f64) * scale).round() as u32;
48 }
49 }
50
51 scaled
52 }
53
54 pub fn get_category(&self, name: &str) -> Option<&CategoryTemplate> {
56 self.categories.iter().find(|c| c.name == name)
57 }
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct CategoryTemplate {
63 pub name: String,
65 pub id_prefix: String,
67 pub description: String,
69 pub tests: Vec<TestTemplate>,
71}
72
73impl CategoryTemplate {
74 pub fn total_points(&self) -> u32 {
76 self.tests.iter().map(|t| t.points).sum()
77 }
78
79 pub fn boundary() -> Self {
81 Self {
82 name: "boundary".to_string(),
83 id_prefix: "BC".to_string(),
84 description: "Boundary condition tests".to_string(),
85 tests: vec![
86 TestTemplate {
87 id: "BC-001".to_string(),
88 name: "Empty input".to_string(),
89 description: "Handle empty/null input gracefully".to_string(),
90 severity: TestSeverity::Critical,
91 points: 4,
92 rust_template: Some(RUST_EMPTY_INPUT.to_string()),
93 python_template: Some(PYTHON_EMPTY_INPUT.to_string()),
94 },
95 TestTemplate {
96 id: "BC-002".to_string(),
97 name: "Maximum size input".to_string(),
98 description: "Handle maximum allowed input size".to_string(),
99 severity: TestSeverity::Critical,
100 points: 4,
101 rust_template: Some(RUST_MAX_INPUT.to_string()),
102 python_template: Some(PYTHON_MAX_INPUT.to_string()),
103 },
104 TestTemplate {
105 id: "BC-003".to_string(),
106 name: "Negative values".to_string(),
107 description: "Handle negative values where positive expected".to_string(),
108 severity: TestSeverity::High,
109 points: 4,
110 rust_template: Some(RUST_NEGATIVE.to_string()),
111 python_template: Some(PYTHON_NEGATIVE.to_string()),
112 },
113 TestTemplate {
114 id: "BC-004".to_string(),
115 name: "Unicode edge cases".to_string(),
116 description: "Handle combining chars, RTL, zero-width".to_string(),
117 severity: TestSeverity::Medium,
118 points: 4,
119 rust_template: Some(RUST_UNICODE.to_string()),
120 python_template: Some(PYTHON_UNICODE.to_string()),
121 },
122 TestTemplate {
123 id: "BC-005".to_string(),
124 name: "Numeric limits".to_string(),
125 description: "Handle MAX_INT, MIN_INT, NaN, Inf".to_string(),
126 severity: TestSeverity::Critical,
127 points: 4,
128 rust_template: Some(RUST_NUMERIC_LIMITS.to_string()),
129 python_template: Some(PYTHON_NUMERIC_LIMITS.to_string()),
130 },
131 ],
132 }
133 }
134
135 pub fn invariant() -> Self {
137 Self {
138 name: "invariant".to_string(),
139 id_prefix: "INV".to_string(),
140 description: "Mathematical invariant tests".to_string(),
141 tests: vec![
142 TestTemplate {
143 id: "INV-001".to_string(),
144 name: "Idempotency".to_string(),
145 description: "f(f(x)) == f(x) for idempotent operations".to_string(),
146 severity: TestSeverity::High,
147 points: 5,
148 rust_template: Some(RUST_IDEMPOTENT.to_string()),
149 python_template: Some(PYTHON_IDEMPOTENT.to_string()),
150 },
151 TestTemplate {
152 id: "INV-002".to_string(),
153 name: "Commutativity".to_string(),
154 description: "f(a,b) == f(b,a) for commutative operations".to_string(),
155 severity: TestSeverity::High,
156 points: 5,
157 rust_template: Some(RUST_COMMUTATIVE.to_string()),
158 python_template: Some(PYTHON_COMMUTATIVE.to_string()),
159 },
160 TestTemplate {
161 id: "INV-003".to_string(),
162 name: "Associativity".to_string(),
163 description: "(a+b)+c == a+(b+c) for floating point".to_string(),
164 severity: TestSeverity::Medium,
165 points: 5,
166 rust_template: Some(RUST_ASSOCIATIVE.to_string()),
167 python_template: Some(PYTHON_ASSOCIATIVE.to_string()),
168 },
169 TestTemplate {
170 id: "INV-004".to_string(),
171 name: "Symmetry (encode/decode)".to_string(),
172 description: "decode(encode(x)) == x for serialization".to_string(),
173 severity: TestSeverity::Critical,
174 points: 5,
175 rust_template: Some(RUST_ROUNDTRIP.to_string()),
176 python_template: Some(PYTHON_ROUNDTRIP.to_string()),
177 },
178 ],
179 }
180 }
181
182 pub fn numerical() -> Self {
184 Self {
185 name: "numerical".to_string(),
186 id_prefix: "NUM".to_string(),
187 description: "Numerical stability tests".to_string(),
188 tests: vec![
189 TestTemplate {
190 id: "NUM-001".to_string(),
191 name: "Catastrophic cancellation".to_string(),
192 description: "1e10 + 1 - 1e10 should not lose precision".to_string(),
193 severity: TestSeverity::High,
194 points: 7,
195 rust_template: Some(RUST_CANCELLATION.to_string()),
196 python_template: Some(PYTHON_CANCELLATION.to_string()),
197 },
198 TestTemplate {
199 id: "NUM-002".to_string(),
200 name: "Accumulation order".to_string(),
201 description: "sum(shuffled) vs sum(sorted) within tolerance".to_string(),
202 severity: TestSeverity::Medium,
203 points: 7,
204 rust_template: Some(RUST_ACCUMULATION.to_string()),
205 python_template: Some(PYTHON_ACCUMULATION.to_string()),
206 },
207 TestTemplate {
208 id: "NUM-003".to_string(),
209 name: "Denormalized numbers".to_string(),
210 description: "Handle subnormal/denormalized floats".to_string(),
211 severity: TestSeverity::Medium,
212 points: 6,
213 rust_template: Some(RUST_DENORMAL.to_string()),
214 python_template: Some(PYTHON_DENORMAL.to_string()),
215 },
216 ],
217 }
218 }
219
220 pub fn concurrency() -> Self {
222 Self {
223 name: "concurrency".to_string(),
224 id_prefix: "CONC".to_string(),
225 description: "Concurrency and race condition tests".to_string(),
226 tests: vec![
227 TestTemplate {
228 id: "CONC-001".to_string(),
229 name: "Data race".to_string(),
230 description: "No data races under parallel iteration".to_string(),
231 severity: TestSeverity::Critical,
232 points: 5,
233 rust_template: Some(RUST_DATA_RACE.to_string()),
234 python_template: Some(PYTHON_DATA_RACE.to_string()),
235 },
236 TestTemplate {
237 id: "CONC-002".to_string(),
238 name: "Deadlock".to_string(),
239 description: "No deadlock potential in lock ordering".to_string(),
240 severity: TestSeverity::Critical,
241 points: 5,
242 rust_template: Some(RUST_DEADLOCK.to_string()),
243 python_template: Some(PYTHON_DEADLOCK.to_string()),
244 },
245 TestTemplate {
246 id: "CONC-003".to_string(),
247 name: "ABA problem".to_string(),
248 description: "Lock-free structures handle ABA".to_string(),
249 severity: TestSeverity::High,
250 points: 5,
251 rust_template: Some(RUST_ABA.to_string()),
252 python_template: Some(PYTHON_ABA.to_string()),
253 },
254 ],
255 }
256 }
257
258 pub fn resource() -> Self {
260 Self {
261 name: "resource".to_string(),
262 id_prefix: "RES".to_string(),
263 description: "Resource exhaustion tests".to_string(),
264 tests: vec![
265 TestTemplate {
266 id: "RES-001".to_string(),
267 name: "Memory exhaustion".to_string(),
268 description: "Controlled behavior under OOM".to_string(),
269 severity: TestSeverity::High,
270 points: 5,
271 rust_template: Some(RUST_MEMORY.to_string()),
272 python_template: Some(PYTHON_MEMORY.to_string()),
273 },
274 TestTemplate {
275 id: "RES-002".to_string(),
276 name: "File descriptor exhaustion".to_string(),
277 description: "Handle FD limits gracefully".to_string(),
278 severity: TestSeverity::Medium,
279 points: 5,
280 rust_template: Some(RUST_FD.to_string()),
281 python_template: Some(PYTHON_FD.to_string()),
282 },
283 TestTemplate {
284 id: "RES-003".to_string(),
285 name: "Stack overflow".to_string(),
286 description: "Deep recursion handled".to_string(),
287 severity: TestSeverity::High,
288 points: 5,
289 rust_template: Some(RUST_STACK.to_string()),
290 python_template: Some(PYTHON_STACK.to_string()),
291 },
292 ],
293 }
294 }
295
296 pub fn parity() -> Self {
298 Self {
299 name: "parity".to_string(),
300 id_prefix: "PAR".to_string(),
301 description: "Cross-implementation parity tests".to_string(),
302 tests: vec![
303 TestTemplate {
304 id: "PAR-001".to_string(),
305 name: "Python/Rust parity".to_string(),
306 description: "Output matches reference Python implementation".to_string(),
307 severity: TestSeverity::High,
308 points: 5,
309 rust_template: Some(RUST_PYTHON_PARITY.to_string()),
310 python_template: Some(PYTHON_RUST_PARITY.to_string()),
311 },
312 TestTemplate {
313 id: "PAR-002".to_string(),
314 name: "CPU/GPU parity".to_string(),
315 description: "CPU and GPU kernels produce same results".to_string(),
316 severity: TestSeverity::High,
317 points: 5,
318 rust_template: Some(RUST_GPU_PARITY.to_string()),
319 python_template: Some(PYTHON_GPU_PARITY.to_string()),
320 },
321 ],
322 }
323 }
324}
325
326#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct TestTemplate {
329 pub id: String,
331 pub name: String,
333 pub description: String,
335 pub severity: TestSeverity,
337 pub points: u32,
339 pub rust_template: Option<String>,
341 pub python_template: Option<String>,
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
347pub enum TestSeverity {
348 Critical,
349 High,
350 Medium,
351 Low,
352}
353
354const RUST_EMPTY_INPUT: &str = r#"#[test]
356fn falsify_{{id_lower}}_empty_input() {
357 let result = {{module}}::{{function}}(&[]);
358 assert!(result.is_err() || result.expect("operation failed").is_empty(),
359 "Should handle empty input gracefully");
360}"#;
361
362const RUST_MAX_INPUT: &str = r#"#[test]
363fn falsify_{{id_lower}}_max_input() {
364 let large_input = vec![0u8; {{max_size}}];
365 let result = {{module}}::{{function}}(&large_input);
366 assert!(result.is_ok(), "Should handle maximum size input");
367}"#;
368
369const RUST_NEGATIVE: &str = r#"#[test]
370fn falsify_{{id_lower}}_negative_values() {
371 let result = {{module}}::{{function}}(-1);
372 assert!(result.is_err(), "Should reject negative values");
373}"#;
374
375const RUST_UNICODE: &str = r#"#[test]
376fn falsify_{{id_lower}}_unicode() {
377 // Combining characters, RTL, zero-width
378 let inputs = ["café\u{0301}", "\u{202E}test", "a\u{200B}b"];
379 for input in inputs {
380 let result = {{module}}::{{function}}(input);
381 assert!(result.is_ok(), "Should handle Unicode: {}", input);
382 }
383}"#;
384
385const RUST_NUMERIC_LIMITS: &str = r#"#[test]
386fn falsify_{{id_lower}}_numeric_limits() {
387 let values = [f64::MAX, f64::MIN, f64::NAN, f64::INFINITY, f64::NEG_INFINITY];
388 for val in values {
389 let result = {{module}}::{{function}}(val);
390 assert!(!result.is_nan() || val.is_nan(), "Should handle {:?}", val);
391 }
392}"#;
393
394const RUST_IDEMPOTENT: &str = r#"proptest! {
395 #[test]
396 fn falsify_{{id_lower}}_idempotent(x in any::<{{type}}>()) {
397 let once = {{module}}::{{function}}(&x);
398 let twice = {{module}}::{{function}}(&once);
399 prop_assert_eq!(once, twice, "f(f(x)) should equal f(x)");
400 }
401}"#;
402
403const RUST_COMMUTATIVE: &str = r#"proptest! {
404 #[test]
405 fn falsify_{{id_lower}}_commutative(
406 a in any::<{{type}}>(),
407 b in any::<{{type}}>()
408 ) {
409 let ab = {{module}}::{{function}}(&a, &b);
410 let ba = {{module}}::{{function}}(&b, &a);
411 prop_assert_eq!(ab, ba, "f(a,b) should equal f(b,a)");
412 }
413}"#;
414
415const RUST_ASSOCIATIVE: &str = r#"proptest! {
416 #[test]
417 fn falsify_{{id_lower}}_associative(
418 a in -1e6f64..1e6,
419 b in -1e6f64..1e6,
420 c in -1e6f64..1e6
421 ) {
422 let ab_c = (a + b) + c;
423 let a_bc = a + (b + c);
424 // Allow small tolerance for floating point
425 prop_assert!((ab_c - a_bc).abs() < 1e-10,
426 "(a+b)+c vs a+(b+c): {} vs {}", ab_c, a_bc);
427 }
428}"#;
429
430const RUST_ROUNDTRIP: &str = r#"proptest! {
431 #[test]
432 fn falsify_{{id_lower}}_roundtrip(x in any::<{{type}}>()) {
433 let encoded = {{module}}::encode(&x);
434 let decoded = {{module}}::decode(&encoded).expect("unexpected failure");
435 prop_assert_eq!(x, decoded, "decode(encode(x)) should equal x");
436 }
437}"#;
438
439const RUST_CANCELLATION: &str = r#"#[test]
440fn falsify_{{id_lower}}_cancellation() {
441 let big = 1e15f64;
442 let small = 1.0;
443 let result = (big + small) - big;
444 assert!((result - small).abs() < 1e-10,
445 "Catastrophic cancellation: expected {}, got {}", small, result);
446}"#;
447
448const RUST_ACCUMULATION: &str = r#"proptest! {
449 #[test]
450 fn falsify_{{id_lower}}_accumulation(mut values in prop::collection::vec(-1e6f64..1e6, 10..100)) {
451 let sum_original: f64 = values.iter().sum();
452 values.sort_by(|a, b| a.partial_cmp(b).expect("comparison failed"));
453 let sum_sorted: f64 = values.iter().sum();
454 prop_assert!((sum_original - sum_sorted).abs() < 1e-6,
455 "Accumulation order matters: {} vs {}", sum_original, sum_sorted);
456 }
457}"#;
458
459const RUST_DENORMAL: &str = r#"#[test]
460fn falsify_{{id_lower}}_denormal() {
461 let denormal = 1e-308f64 * 1e-10;
462 assert!(denormal > 0.0 || denormal == 0.0,
463 "Should handle denormalized numbers");
464 let result = {{module}}::{{function}}(denormal);
465 assert!(!result.is_nan(), "Should not produce NaN from denormal");
466}"#;
467
468const RUST_DATA_RACE: &str = r#"#[test]
469fn falsify_{{id_lower}}_data_race() {
470 use std::sync::Arc;
471 use std::thread;
472
473 let data = Arc::new({{module}}::{{type}}::new());
474 let handles: Vec<_> = (0..10).map(|i| {
475 let data = Arc::clone(&data);
476 thread::spawn(move || {
477 data.operation(i);
478 })
479 }).collect();
480
481 for h in handles {
482 h.join().expect("Thread should not panic");
483 }
484}"#;
485
486const RUST_DEADLOCK: &str = r#"#[test]
487fn falsify_{{id_lower}}_deadlock() {
488 use std::time::Duration;
489 use std::sync::mpsc::channel;
490
491 let (tx, rx) = channel();
492 let handle = std::thread::spawn(move || {
493 {{module}}::potentially_blocking_operation();
494 tx.send(()).expect("channel send failed");
495 });
496
497 // Should complete within timeout
498 let result = rx.recv_timeout(Duration::from_secs(5));
499 assert!(result.is_ok(), "Operation should complete without deadlock");
500 handle.join().expect("thread join failed");
501}"#;
502
503const RUST_ABA: &str = r#"#[test]
504fn falsify_{{id_lower}}_aba() {
505 use std::sync::atomic::{AtomicUsize, Ordering};
506
507 let counter = AtomicUsize::new(0);
508 // Simulate ABA: 0 -> 1 -> 0
509 counter.store(1, Ordering::SeqCst);
510 counter.store(0, Ordering::SeqCst);
511
512 // CAS should detect the change
513 let result = {{module}}::atomic_operation(&counter);
514 assert!(result.is_ok(), "Should handle ABA problem");
515}"#;
516
517const RUST_MEMORY: &str = r"#[test]
518fn falsify_{{id_lower}}_memory() {
519 // Attempt allocation that might fail
520 let result = std::panic::catch_unwind(|| {
521 let _large = vec![0u8; 1_000_000_000]; // 1GB
522 });
523 // Should either succeed or fail gracefully
524 // (this test mainly verifies no UB on allocation failure)
525}";
526
527const RUST_FD: &str = r#"#[test]
528fn falsify_{{id_lower}}_fd_exhaustion() {
529 use std::fs::File;
530
531 let mut files = Vec::new();
532 for i in 0..10000 {
533 match File::create(format!("/tmp/test_{}", i)) {
534 Ok(f) => files.push(f),
535 Err(_) => break,
536 }
537 }
538 // Cleanup
539 for (i, _) in files.iter().enumerate() {
540 let _ = std::fs::remove_file(format!("/tmp/test_{}", i));
541 }
542 // Main test: verify module handles FD limits
543 let result = {{module}}::{{function}}();
544 assert!(result.is_ok(), "Should handle FD exhaustion gracefully");
545}"#;
546
547const RUST_STACK: &str = r#"#[test]
548fn falsify_{{id_lower}}_stack_overflow() {
549 // Test with iterative approach that might overflow naive recursion
550 let result = std::panic::catch_unwind(|| {
551 {{module}}::deep_operation(10000);
552 });
553 assert!(result.is_ok(), "Should handle deep recursion without stack overflow");
554}"#;
555
556const RUST_PYTHON_PARITY: &str = r#"#[test]
557fn falsify_{{id_lower}}_python_parity() {
558 // Reference values from Python implementation
559 let test_cases = vec![
560 (vec![1.0, 2.0, 3.0], vec![1.0, 2.0, 3.0]), // Expected output
561 ];
562
563 for (input, expected) in test_cases {
564 let result = {{module}}::{{function}}(&input);
565 assert_eq!(result, expected, "Should match Python reference");
566 }
567}"#;
568
569const RUST_GPU_PARITY: &str = r#"#[test]
570fn falsify_{{id_lower}}_gpu_parity() {
571 let input = vec![1.0f32; 1024];
572
573 let cpu_result = {{module}}::cpu_{{function}}(&input);
574 let gpu_result = {{module}}::gpu_{{function}}(&input);
575
576 for (cpu, gpu) in cpu_result.iter().zip(gpu_result.iter()) {
577 assert!((cpu - gpu).abs() < 1e-5,
578 "CPU/GPU mismatch: {} vs {}", cpu, gpu);
579 }
580}"#;
581
582const PYTHON_EMPTY_INPUT: &str = r#" result = module.function([])
584 assert result is None or len(result) == 0, "Should handle empty input"
585"#;
586
587const PYTHON_MAX_INPUT: &str = r#" large_input = [0] * {{max_size}}
588 result = module.function(large_input)
589 assert result is not None, "Should handle maximum size input"
590"#;
591
592const PYTHON_NEGATIVE: &str = r" with pytest.raises(ValueError):
593 module.function(-1)
594";
595
596const PYTHON_UNICODE: &str = r#" inputs = ["café\u0301", "\u202Etest", "a\u200Bb"]
597 for inp in inputs:
598 result = module.function(inp)
599 assert result is not None, f"Should handle Unicode: {inp}"
600"#;
601
602const PYTHON_NUMERIC_LIMITS: &str = r" import math
603 values = [float('inf'), float('-inf'), float('nan'), 1e308, -1e308]
604 for val in values:
605 result = module.function(val)
606 assert not math.isnan(result) or math.isnan(val)
607";
608
609const PYTHON_IDEMPOTENT: &str = r#"@given(st.{{strategy}}())
610def test_{{id_lower}}_idempotent(x):
611 once = module.function(x)
612 twice = module.function(once)
613 assert once == twice, "f(f(x)) should equal f(x)"
614"#;
615
616const PYTHON_COMMUTATIVE: &str = r#"@given(st.{{strategy}}(), st.{{strategy}}())
617def test_{{id_lower}}_commutative(a, b):
618 ab = module.function(a, b)
619 ba = module.function(b, a)
620 assert ab == ba, "f(a,b) should equal f(b,a)"
621"#;
622
623const PYTHON_ASSOCIATIVE: &str = r#"@given(st.floats(-1e6, 1e6), st.floats(-1e6, 1e6), st.floats(-1e6, 1e6))
624def test_{{id_lower}}_associative(a, b, c):
625 ab_c = (a + b) + c
626 a_bc = a + (b + c)
627 assert abs(ab_c - a_bc) < 1e-10, f"(a+b)+c vs a+(b+c): {ab_c} vs {a_bc}"
628"#;
629
630const PYTHON_ROUNDTRIP: &str = r#"@given(st.{{strategy}}())
631def test_{{id_lower}}_roundtrip(x):
632 encoded = module.encode(x)
633 decoded = module.decode(encoded)
634 assert x == decoded, "decode(encode(x)) should equal x"
635"#;
636
637const PYTHON_CANCELLATION: &str = r#" big = 1e15
638 small = 1.0
639 result = (big + small) - big
640 assert abs(result - small) < 1e-10, f"Catastrophic cancellation: expected {small}, got {result}"
641"#;
642
643const PYTHON_ACCUMULATION: &str = r"@given(st.lists(st.floats(-1e6, 1e6), min_size=10, max_size=100))
644def test_{{id_lower}}_accumulation(values):
645 sum_original = sum(values)
646 sum_sorted = sum(sorted(values))
647 assert abs(sum_original - sum_sorted) < 1e-6
648";
649
650const PYTHON_DENORMAL: &str = r#" denormal = 1e-308 * 1e-10
651 result = module.function(denormal)
652 assert not math.isnan(result), "Should not produce NaN from denormal"
653"#;
654
655const PYTHON_DATA_RACE: &str = r#" import threading
656
657 errors = []
658 def worker(i):
659 try:
660 module.operation(i)
661 except Exception as e:
662 errors.append(e)
663
664 threads = [threading.Thread(target=worker, args=(i,)) for i in range(10)]
665 for t in threads:
666 t.start()
667 for t in threads:
668 t.join()
669
670 assert len(errors) == 0, f"Data race detected: {errors}"
671"#;
672
673const PYTHON_DEADLOCK: &str = r#" import threading
674 import queue
675
676 q = queue.Queue()
677
678 def worker():
679 module.potentially_blocking_operation()
680 q.put(True)
681
682 t = threading.Thread(target=worker)
683 t.start()
684
685 try:
686 q.get(timeout=5)
687 except queue.Empty:
688 pytest.fail("Operation deadlocked")
689 t.join()
690"#;
691
692const PYTHON_ABA: &str = r#" # ABA problem test
693 counter = [0]
694 counter[0] = 1
695 counter[0] = 0
696 result = module.atomic_operation(counter)
697 assert result is not None, "Should handle ABA problem"
698"#;
699
700const PYTHON_MEMORY: &str = r" try:
701 large = [0] * 1_000_000_000
702 except MemoryError:
703 pass # Expected
704 # Main test: verify module handles memory limits
705 result = module.function_with_limit()
706 assert result is not None
707";
708
709const PYTHON_FD: &str = r#" import os
710 files = []
711 try:
712 for i in range(10000):
713 f = open(f'/tmp/test_{i}', 'w')
714 files.append(f)
715 except OSError:
716 pass # FD limit reached
717
718 for f in files:
719 f.close()
720 for i in range(len(files)):
721 os.remove(f'/tmp/test_{i}')
722
723 result = module.function()
724 assert result is not None, "Should handle FD exhaustion"
725"#;
726
727const PYTHON_STACK: &str = r" import sys
728 old_limit = sys.getrecursionlimit()
729 sys.setrecursionlimit(100000)
730
731 try:
732 result = module.deep_operation(10000)
733 assert result is not None
734 finally:
735 sys.setrecursionlimit(old_limit)
736";
737
738const PYTHON_RUST_PARITY: &str = r#" # Reference values from Rust implementation
739 test_cases = [
740 ([1.0, 2.0, 3.0], [1.0, 2.0, 3.0]),
741 ]
742
743 for input_data, expected in test_cases:
744 result = module.function(input_data)
745 assert result == expected, "Should match Rust reference"
746"#;
747
748const PYTHON_GPU_PARITY: &str = r#" import numpy as np
749
750 input_data = np.ones(1024, dtype=np.float32)
751
752 cpu_result = module.cpu_function(input_data)
753 gpu_result = module.gpu_function(input_data)
754
755 np.testing.assert_allclose(cpu_result, gpu_result, rtol=1e-5,
756 err_msg="CPU/GPU mismatch")
757"#;
758
759#[cfg(test)]
760mod tests {
761 use super::*;
762
763 #[test]
764 fn test_default_template() {
765 let template = FalsificationTemplate::default();
766 assert_eq!(template.total_points(), 100);
767 }
768
769 #[test]
770 fn test_category_points() {
771 let boundary = CategoryTemplate::boundary();
772 assert_eq!(boundary.total_points(), 20);
773
774 let invariant = CategoryTemplate::invariant();
775 assert_eq!(invariant.total_points(), 20);
776
777 let numerical = CategoryTemplate::numerical();
778 assert_eq!(numerical.total_points(), 20);
779
780 let concurrency = CategoryTemplate::concurrency();
781 assert_eq!(concurrency.total_points(), 15);
782
783 let resource = CategoryTemplate::resource();
784 assert_eq!(resource.total_points(), 15);
785
786 let parity = CategoryTemplate::parity();
787 assert_eq!(parity.total_points(), 10);
788 }
789
790 #[test]
791 fn test_scale_template() {
792 let template = FalsificationTemplate::default();
793 let scaled = template.scale_to_points(50);
794 assert!(
796 (scaled.total_points() as i32 - 50).abs() <= 20,
797 "Scaled to {} points, expected around 50",
798 scaled.total_points()
799 );
800 }
801}