Skip to main content

verificar/transpiler/
oracle.rs

1//! Transpiler verification oracle
2//!
3//! Provides end-to-end verification of transpiler correctness by:
4//! 1. Generating test programs
5//! 2. Transpiling source to target
6//! 3. Executing both in sandboxed environments
7//! 4. Comparing I/O behavior
8
9use crate::generator::Generator;
10use crate::oracle::{
11    diff_results, DiffOptions, DiffResult, ExecutionResult, Executor, PythonExecutor, RustExecutor,
12    SandboxedPythonExecutor,
13};
14use crate::Language;
15
16use super::Transpiler;
17
18/// Verification result for a single test case
19#[derive(Debug, Clone)]
20pub struct TranspilerVerification {
21    /// Original source code
22    pub source_code: String,
23    /// Transpiled target code (if successful)
24    pub target_code: Option<String>,
25    /// Source execution result
26    pub source_result: Option<ExecutionResult>,
27    /// Target execution result
28    pub target_result: Option<ExecutionResult>,
29    /// Diff result comparing outputs
30    pub diff: Option<DiffResult>,
31    /// Overall verdict
32    pub verdict: TranspilerVerdict,
33    /// Test input used
34    pub input: String,
35}
36
37/// Verdict from transpiler verification
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub enum TranspilerVerdict {
40    /// I/O equivalent - transpilation is correct
41    Pass,
42    /// Transpilation failed
43    TranspileError(String),
44    /// Source execution failed
45    SourceError(String),
46    /// Target execution failed
47    TargetError(String),
48    /// Output mismatch
49    OutputMismatch,
50    /// Timeout during execution
51    Timeout,
52}
53
54/// Statistics from verification run
55#[derive(Debug, Clone, Default)]
56pub struct VerificationStats {
57    /// Total test cases run
58    pub total: usize,
59    /// Passed (I/O equivalent)
60    pub passed: usize,
61    /// Transpilation errors
62    pub transpile_errors: usize,
63    /// Source execution errors
64    pub source_errors: usize,
65    /// Target execution errors
66    pub target_errors: usize,
67    /// Output mismatches
68    pub mismatches: usize,
69    /// Timeouts
70    pub timeouts: usize,
71}
72
73impl VerificationStats {
74    /// Calculate pass rate as percentage
75    #[must_use]
76    pub fn pass_rate(&self) -> f64 {
77        if self.total == 0 {
78            return 0.0;
79        }
80        (self.passed as f64 / self.total as f64) * 100.0
81    }
82
83    /// Calculate transpilation success rate
84    #[must_use]
85    pub fn transpile_rate(&self) -> f64 {
86        if self.total == 0 {
87            return 0.0;
88        }
89        let successful = self.total - self.transpile_errors;
90        (successful as f64 / self.total as f64) * 100.0
91    }
92}
93
94/// Oracle for verifying transpiler correctness
95pub struct TranspilerOracle<T: Transpiler> {
96    transpiler: T,
97    source_executor: Box<dyn Executor>,
98    target_executor: Box<dyn Executor>,
99    diff_options: DiffOptions,
100    timeout_ms: u64,
101    use_sandbox: bool,
102}
103
104impl<T: Transpiler> TranspilerOracle<T> {
105    /// Create a new transpiler oracle
106    pub fn new(transpiler: T) -> Self {
107        let source_executor: Box<dyn Executor> = match transpiler.source_language() {
108            Language::Python => Box::new(SandboxedPythonExecutor::new()),
109            _ => Box::new(PythonExecutor::new()), // Fallback
110        };
111
112        let target_executor: Box<dyn Executor> = match transpiler.target_language() {
113            Language::Python => Box::new(PythonExecutor::new()),
114            // Default to Rust executor for Rust and other languages
115            _ => Box::new(RustExecutor::new()),
116        };
117
118        Self {
119            transpiler,
120            source_executor,
121            target_executor,
122            diff_options: DiffOptions::default(),
123            timeout_ms: 5000,
124            use_sandbox: true,
125        }
126    }
127
128    /// Set custom timeout
129    #[must_use]
130    pub fn with_timeout(mut self, timeout_ms: u64) -> Self {
131        self.timeout_ms = timeout_ms;
132        self
133    }
134
135    /// Set diff options
136    #[must_use]
137    pub fn with_diff_options(mut self, options: DiffOptions) -> Self {
138        self.diff_options = options;
139        self
140    }
141
142    /// Disable sandboxing (use with caution)
143    #[must_use]
144    pub fn without_sandbox(mut self) -> Self {
145        self.use_sandbox = false;
146        if matches!(self.transpiler.source_language(), Language::Python) {
147            self.source_executor = Box::new(PythonExecutor::new());
148        }
149        self
150    }
151
152    /// Verify a single source program
153    pub fn verify(&self, source: &str, input: &str) -> TranspilerVerification {
154        // Step 1: Transpile
155        let target_code = match self.transpiler.transpile(source) {
156            Ok(code) => code,
157            Err(e) => {
158                return TranspilerVerification {
159                    source_code: source.to_string(),
160                    target_code: None,
161                    source_result: None,
162                    target_result: None,
163                    diff: None,
164                    verdict: TranspilerVerdict::TranspileError(e.to_string()),
165                    input: input.to_string(),
166                };
167            }
168        };
169
170        // Step 2: Execute source
171        let source_result = match self.source_executor.execute(source, input, self.timeout_ms) {
172            Ok(result) => result,
173            Err(e) => {
174                let msg = e.to_string();
175                let verdict = if msg.contains("timeout") || msg.contains("timed out") {
176                    TranspilerVerdict::Timeout
177                } else {
178                    TranspilerVerdict::SourceError(msg)
179                };
180                return TranspilerVerification {
181                    source_code: source.to_string(),
182                    target_code: Some(target_code),
183                    source_result: None,
184                    target_result: None,
185                    diff: None,
186                    verdict,
187                    input: input.to_string(),
188                };
189            }
190        };
191
192        // Step 3: Execute target
193        let target_result = match self
194            .target_executor
195            .execute(&target_code, input, self.timeout_ms)
196        {
197            Ok(result) => result,
198            Err(e) => {
199                let msg = e.to_string();
200                let verdict = if msg.contains("timeout") || msg.contains("timed out") {
201                    TranspilerVerdict::Timeout
202                } else {
203                    TranspilerVerdict::TargetError(msg)
204                };
205                return TranspilerVerification {
206                    source_code: source.to_string(),
207                    target_code: Some(target_code),
208                    source_result: Some(source_result),
209                    target_result: None,
210                    diff: None,
211                    verdict,
212                    input: input.to_string(),
213                };
214            }
215        };
216
217        // Step 4: Compare outputs
218        let diff = diff_results(&source_result, &target_result, &self.diff_options);
219        let verdict = if diff.matches {
220            TranspilerVerdict::Pass
221        } else {
222            TranspilerVerdict::OutputMismatch
223        };
224
225        TranspilerVerification {
226            source_code: source.to_string(),
227            target_code: Some(target_code),
228            source_result: Some(source_result),
229            target_result: Some(target_result),
230            diff: Some(diff),
231            verdict,
232            input: input.to_string(),
233        }
234    }
235
236    /// Verify multiple source programs
237    pub fn verify_batch(
238        &self,
239        sources: &[(String, String)],
240    ) -> (Vec<TranspilerVerification>, VerificationStats) {
241        let mut results = Vec::with_capacity(sources.len());
242        let mut stats = VerificationStats::default();
243
244        for (source, input) in sources {
245            let verification = self.verify(source, input);
246            stats.total += 1;
247
248            match &verification.verdict {
249                TranspilerVerdict::Pass => stats.passed += 1,
250                TranspilerVerdict::TranspileError(_) => stats.transpile_errors += 1,
251                TranspilerVerdict::SourceError(_) => stats.source_errors += 1,
252                TranspilerVerdict::TargetError(_) => stats.target_errors += 1,
253                TranspilerVerdict::OutputMismatch => stats.mismatches += 1,
254                TranspilerVerdict::Timeout => stats.timeouts += 1,
255            }
256
257            results.push(verification);
258        }
259
260        (results, stats)
261    }
262
263    /// Generate and verify programs
264    pub fn verify_generated(
265        &self,
266        count: usize,
267        max_depth: usize,
268    ) -> (Vec<TranspilerVerification>, VerificationStats) {
269        let generator = Generator::new(self.transpiler.source_language());
270        let programs = generator.generate_exhaustive(max_depth);
271
272        let sources: Vec<_> = programs
273            .into_iter()
274            .take(count)
275            .map(|p| (p.code, String::new())) // Empty input for generated programs
276            .collect();
277
278        self.verify_batch(&sources)
279    }
280
281    /// Get reference to the transpiler
282    #[must_use]
283    pub fn transpiler(&self) -> &T {
284        &self.transpiler
285    }
286}
287
288impl<T: Transpiler> std::fmt::Debug for TranspilerOracle<T> {
289    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290        f.debug_struct("TranspilerOracle")
291            .field("source_language", &self.transpiler.source_language())
292            .field("target_language", &self.transpiler.target_language())
293            .field("timeout_ms", &self.timeout_ms)
294            .field("use_sandbox", &self.use_sandbox)
295            .field("diff_options", &self.diff_options)
296            .finish_non_exhaustive()
297    }
298}
299
300#[cfg(test)]
301mod tests {
302    use super::*;
303    use crate::grammar::{grammar_for, Grammar};
304    use crate::{Error, Result};
305
306    /// Mock transpiler for testing
307    struct MockTranspiler {
308        source: Language,
309        target: Language,
310    }
311
312    impl MockTranspiler {
313        fn new(source: Language, target: Language) -> Self {
314            Self { source, target }
315        }
316    }
317
318    impl Transpiler for MockTranspiler {
319        fn source_language(&self) -> Language {
320            self.source
321        }
322
323        fn target_language(&self) -> Language {
324            self.target
325        }
326
327        fn transpile(&self, source: &str) -> Result<String> {
328            // Simple mock: Python print -> Rust println
329            if source.contains("print") {
330                // Extract the argument
331                if let Some(start) = source.find("print(") {
332                    let rest = &source[start + 6..];
333                    if let Some(end) = rest.find(')') {
334                        let arg = &rest[..end];
335                        // Handle string literals
336                        if arg.starts_with('\'') || arg.starts_with('"') {
337                            let content = &arg[1..arg.len() - 1];
338                            return Ok(format!("fn main() {{\n    println!(\"{}\");\n}}", content));
339                        }
340                        // Handle numeric expressions
341                        return Ok(format!(
342                            "fn main() {{\n    println!(\"{{}}\", {});\n}}",
343                            arg
344                        ));
345                    }
346                }
347            }
348            // Simple pass-through for other code
349            Ok(format!("fn main() {{\n    // {}\n}}", source))
350        }
351
352        fn grammar(&self) -> &dyn Grammar {
353            // Return a boxed grammar and leak it to get a static reference
354            // This is acceptable in tests
355            Box::leak(grammar_for(self.source))
356        }
357
358        fn version(&self) -> &str {
359            "0.1.0-mock"
360        }
361    }
362
363    #[test]
364    fn test_verification_stats() {
365        let stats = VerificationStats {
366            total: 100,
367            passed: 80,
368            transpile_errors: 5,
369            source_errors: 5,
370            target_errors: 5,
371            mismatches: 3,
372            timeouts: 2,
373        };
374
375        assert!((stats.pass_rate() - 80.0).abs() < 0.001);
376        assert!((stats.transpile_rate() - 95.0).abs() < 0.001);
377    }
378
379    #[test]
380    fn test_verification_stats_empty() {
381        let stats = VerificationStats::default();
382        assert!((stats.pass_rate() - 0.0).abs() < 0.001);
383    }
384
385    #[test]
386    fn test_transpiler_verdict_eq() {
387        assert_eq!(TranspilerVerdict::Pass, TranspilerVerdict::Pass);
388        assert_ne!(TranspilerVerdict::Pass, TranspilerVerdict::OutputMismatch);
389    }
390
391    #[test]
392    fn test_mock_transpiler() {
393        let transpiler = MockTranspiler::new(Language::Python, Language::Rust);
394        assert_eq!(transpiler.source_language(), Language::Python);
395        assert_eq!(transpiler.target_language(), Language::Rust);
396
397        let result = transpiler.transpile("print('hello')");
398        assert!(result.is_ok());
399        let code = result.unwrap();
400        assert!(code.contains("println!"));
401        assert!(code.contains("hello"));
402    }
403
404    #[test]
405    fn test_transpiler_oracle_creation() {
406        let transpiler = MockTranspiler::new(Language::Python, Language::Rust);
407        let oracle = TranspilerOracle::new(transpiler);
408
409        assert_eq!(oracle.transpiler().source_language(), Language::Python);
410        assert_eq!(oracle.transpiler().target_language(), Language::Rust);
411    }
412
413    #[test]
414    fn test_transpiler_oracle_verify_transpile_error() {
415        struct FailingTranspiler;
416
417        impl Transpiler for FailingTranspiler {
418            fn source_language(&self) -> Language {
419                Language::Python
420            }
421            fn target_language(&self) -> Language {
422                Language::Rust
423            }
424            fn transpile(&self, _source: &str) -> Result<String> {
425                Err(Error::Transpile("Unsupported syntax".to_string()))
426            }
427            fn grammar(&self) -> &dyn Grammar {
428                Box::leak(grammar_for(Language::Python))
429            }
430            fn version(&self) -> &str {
431                "0.0.0"
432            }
433        }
434
435        let oracle = TranspilerOracle::new(FailingTranspiler);
436        let result = oracle.verify("invalid code", "");
437
438        assert!(matches!(
439            result.verdict,
440            TranspilerVerdict::TranspileError(_)
441        ));
442    }
443
444    #[test]
445    fn test_transpiler_oracle_with_timeout() {
446        let transpiler = MockTranspiler::new(Language::Python, Language::Rust);
447        let oracle = TranspilerOracle::new(transpiler).with_timeout(1000);
448        assert_eq!(oracle.timeout_ms, 1000);
449    }
450
451    #[test]
452    fn test_transpiler_oracle_with_diff_options() {
453        let transpiler = MockTranspiler::new(Language::Python, Language::Rust);
454        let options = DiffOptions {
455            normalize_whitespace: true,
456            ignore_trailing_whitespace: true,
457            ignore_case: false,
458            float_tolerance: Some(0.001),
459            ignore_stderr: false,
460            ignore_exit_code: false,
461        };
462        let oracle = TranspilerOracle::new(transpiler).with_diff_options(options);
463        assert!(oracle.diff_options.normalize_whitespace);
464    }
465
466    #[test]
467    fn test_transpiler_oracle_without_sandbox() {
468        let transpiler = MockTranspiler::new(Language::Python, Language::Rust);
469        let oracle = TranspilerOracle::new(transpiler).without_sandbox();
470        assert!(!oracle.use_sandbox);
471    }
472
473    #[test]
474    fn test_transpiler_oracle_debug() {
475        let transpiler = MockTranspiler::new(Language::Python, Language::Rust);
476        let oracle = TranspilerOracle::new(transpiler);
477        let debug = format!("{:?}", oracle);
478        assert!(debug.contains("TranspilerOracle"));
479        assert!(debug.contains("Python"));
480        assert!(debug.contains("Rust"));
481    }
482
483    #[test]
484    fn test_transpiler_verification_clone() {
485        let verification = TranspilerVerification {
486            source_code: "print(1)".to_string(),
487            target_code: Some("fn main() {}".to_string()),
488            source_result: None,
489            target_result: None,
490            diff: None,
491            verdict: TranspilerVerdict::Pass,
492            input: String::new(),
493        };
494        let cloned = verification.clone();
495        assert_eq!(cloned.source_code, verification.source_code);
496        assert_eq!(cloned.verdict, verification.verdict);
497    }
498
499    #[test]
500    fn test_transpiler_verdict_debug() {
501        let verdict = TranspilerVerdict::TranspileError("test error".to_string());
502        let debug = format!("{:?}", verdict);
503        assert!(debug.contains("TranspileError"));
504    }
505
506    #[test]
507    fn test_transpiler_verdict_source_error() {
508        let verdict = TranspilerVerdict::SourceError("runtime error".to_string());
509        assert!(matches!(verdict, TranspilerVerdict::SourceError(_)));
510    }
511
512    #[test]
513    fn test_transpiler_verdict_target_error() {
514        let verdict = TranspilerVerdict::TargetError("compilation error".to_string());
515        assert!(matches!(verdict, TranspilerVerdict::TargetError(_)));
516    }
517
518    #[test]
519    fn test_transpiler_verdict_output_mismatch() {
520        let verdict = TranspilerVerdict::OutputMismatch;
521        assert_eq!(verdict, TranspilerVerdict::OutputMismatch);
522    }
523
524    #[test]
525    fn test_transpiler_verdict_timeout() {
526        let verdict = TranspilerVerdict::Timeout;
527        assert_eq!(verdict, TranspilerVerdict::Timeout);
528    }
529
530    #[test]
531    fn test_verification_stats_default() {
532        let stats = VerificationStats::default();
533        assert_eq!(stats.total, 0);
534        assert_eq!(stats.passed, 0);
535        assert_eq!(stats.transpile_errors, 0);
536    }
537
538    #[test]
539    fn test_verification_stats_transpile_rate() {
540        let stats = VerificationStats {
541            total: 100,
542            passed: 80,
543            transpile_errors: 10,
544            source_errors: 5,
545            target_errors: 3,
546            mismatches: 1,
547            timeouts: 1,
548        };
549        assert!((stats.transpile_rate() - 90.0).abs() < 0.001);
550    }
551
552    #[test]
553    fn test_verification_stats_debug() {
554        let stats = VerificationStats {
555            total: 10,
556            passed: 8,
557            transpile_errors: 1,
558            source_errors: 0,
559            target_errors: 0,
560            mismatches: 1,
561            timeouts: 0,
562        };
563        let debug = format!("{:?}", stats);
564        assert!(debug.contains("VerificationStats"));
565    }
566
567    #[test]
568    fn test_verification_stats_clone() {
569        let stats = VerificationStats {
570            total: 50,
571            passed: 40,
572            transpile_errors: 5,
573            source_errors: 2,
574            target_errors: 1,
575            mismatches: 1,
576            timeouts: 1,
577        };
578        let cloned = stats.clone();
579        assert_eq!(cloned.total, stats.total);
580        assert_eq!(cloned.passed, stats.passed);
581    }
582
583    #[test]
584    fn test_transpiler_oracle_verify_batch_empty() {
585        let transpiler = MockTranspiler::new(Language::Python, Language::Rust);
586        let oracle = TranspilerOracle::new(transpiler);
587        let (results, stats) = oracle.verify_batch(&[]);
588        assert!(results.is_empty());
589        assert_eq!(stats.total, 0);
590    }
591
592    #[test]
593    fn test_mock_transpiler_numeric_expression() {
594        let transpiler = MockTranspiler::new(Language::Python, Language::Rust);
595        let result = transpiler.transpile("print(1 + 2)");
596        assert!(result.is_ok());
597        let code = result.unwrap();
598        assert!(code.contains("println!"));
599        assert!(code.contains("1 + 2"));
600    }
601
602    #[test]
603    fn test_mock_transpiler_non_print() {
604        let transpiler = MockTranspiler::new(Language::Python, Language::Rust);
605        let result = transpiler.transpile("x = 5");
606        assert!(result.is_ok());
607        let code = result.unwrap();
608        assert!(code.contains("fn main()"));
609        assert!(code.contains("x = 5"));
610    }
611
612    #[test]
613    fn test_mock_transpiler_version() {
614        let transpiler = MockTranspiler::new(Language::Python, Language::Rust);
615        assert_eq!(transpiler.version(), "0.1.0-mock");
616    }
617
618    #[test]
619    fn test_mock_transpiler_grammar() {
620        let transpiler = MockTranspiler::new(Language::Python, Language::Rust);
621        let grammar = transpiler.grammar();
622        assert_eq!(grammar.language(), Language::Python);
623    }
624
625    #[test]
626    fn test_verification_stats_transpile_rate_zero_total() {
627        let stats = VerificationStats::default();
628        assert!((stats.transpile_rate() - 0.0).abs() < 0.001);
629    }
630
631    #[test]
632    fn test_transpiler_oracle_verify_pass() {
633        use crate::oracle::PythonExecutor;
634        let executor = PythonExecutor::new();
635        if !executor.is_available() {
636            eprintln!("Python not available, skipping test");
637            return;
638        }
639
640        // Create a mock transpiler that produces working code
641        struct PassingTranspiler;
642
643        impl Transpiler for PassingTranspiler {
644            fn source_language(&self) -> Language {
645                Language::Python
646            }
647            fn target_language(&self) -> Language {
648                Language::Python
649            }
650            fn transpile(&self, source: &str) -> Result<String> {
651                // Just return the same code - it's Python to Python
652                Ok(source.to_string())
653            }
654            fn grammar(&self) -> &dyn Grammar {
655                Box::leak(grammar_for(Language::Python))
656            }
657            fn version(&self) -> &str {
658                "1.0.0"
659            }
660        }
661
662        let oracle = TranspilerOracle::new(PassingTranspiler);
663        let result = oracle.verify("print('hello')", "");
664
665        assert_eq!(result.verdict, TranspilerVerdict::Pass);
666        assert!(result.source_result.is_some());
667        assert!(result.target_result.is_some());
668        assert!(result.diff.is_some());
669    }
670
671    #[test]
672    fn test_transpiler_oracle_verify_mismatch() {
673        use crate::oracle::PythonExecutor;
674        let executor = PythonExecutor::new();
675        if !executor.is_available() {
676            eprintln!("Python not available, skipping test");
677            return;
678        }
679
680        // Create a transpiler that produces different output
681        struct MismatchTranspiler;
682
683        impl Transpiler for MismatchTranspiler {
684            fn source_language(&self) -> Language {
685                Language::Python
686            }
687            fn target_language(&self) -> Language {
688                Language::Python
689            }
690            fn transpile(&self, _source: &str) -> Result<String> {
691                // Always return code that prints 'goodbye'
692                Ok("print('goodbye')".to_string())
693            }
694            fn grammar(&self) -> &dyn Grammar {
695                Box::leak(grammar_for(Language::Python))
696            }
697            fn version(&self) -> &str {
698                "1.0.0"
699            }
700        }
701
702        let oracle = TranspilerOracle::new(MismatchTranspiler);
703        let result = oracle.verify("print('hello')", "");
704
705        assert_eq!(result.verdict, TranspilerVerdict::OutputMismatch);
706    }
707
708    #[test]
709    fn test_transpiler_oracle_verify_target_execution_error() {
710        use crate::oracle::PythonExecutor;
711        let executor = PythonExecutor::new();
712        if !executor.is_available() {
713            eprintln!("Python not available, skipping test");
714            return;
715        }
716
717        // Transpiler that returns invalid Python for target
718        struct TargetErrorTranspiler;
719
720        impl Transpiler for TargetErrorTranspiler {
721            fn source_language(&self) -> Language {
722                Language::Python
723            }
724            fn target_language(&self) -> Language {
725                Language::Python
726            }
727            fn transpile(&self, _source: &str) -> Result<String> {
728                // Return code with a syntax error
729                Ok("this is not valid python syntax !!@#$".to_string())
730            }
731            fn grammar(&self) -> &dyn Grammar {
732                Box::leak(grammar_for(Language::Python))
733            }
734            fn version(&self) -> &str {
735                "1.0.0"
736            }
737        }
738
739        let oracle = TranspilerOracle::new(TargetErrorTranspiler);
740        let result = oracle.verify("print('hello')", "");
741
742        // The source runs fine, but target fails to execute
743        // Due to different error handling, may result in OutputMismatch or TargetError
744        assert!(matches!(
745            result.verdict,
746            TranspilerVerdict::TargetError(_) | TranspilerVerdict::OutputMismatch
747        ));
748    }
749
750    #[test]
751    fn test_transpiler_oracle_verify_batch() {
752        use crate::oracle::PythonExecutor;
753        let executor = PythonExecutor::new();
754        if !executor.is_available() {
755            eprintln!("Python not available, skipping test");
756            return;
757        }
758
759        // Simple pass-through transpiler
760        struct PassthroughTranspiler;
761
762        impl Transpiler for PassthroughTranspiler {
763            fn source_language(&self) -> Language {
764                Language::Python
765            }
766            fn target_language(&self) -> Language {
767                Language::Python
768            }
769            fn transpile(&self, source: &str) -> Result<String> {
770                Ok(source.to_string())
771            }
772            fn grammar(&self) -> &dyn Grammar {
773                Box::leak(grammar_for(Language::Python))
774            }
775            fn version(&self) -> &str {
776                "1.0.0"
777            }
778        }
779
780        let oracle = TranspilerOracle::new(PassthroughTranspiler);
781        let sources = vec![
782            ("print(1)".to_string(), String::new()),
783            ("print(2)".to_string(), String::new()),
784            ("print(3)".to_string(), String::new()),
785        ];
786        let (results, stats) = oracle.verify_batch(&sources);
787
788        assert_eq!(results.len(), 3);
789        assert_eq!(stats.total, 3);
790        assert_eq!(stats.passed, 3);
791    }
792
793    #[test]
794    fn test_transpiler_oracle_verify_batch_mixed() {
795        // Transpiler that fails on specific inputs
796        struct MixedTranspiler;
797
798        impl Transpiler for MixedTranspiler {
799            fn source_language(&self) -> Language {
800                Language::Python
801            }
802            fn target_language(&self) -> Language {
803                Language::Python
804            }
805            fn transpile(&self, source: &str) -> Result<String> {
806                if source.contains("FAIL") {
807                    Err(Error::Transpile("intentional failure".to_string()))
808                } else {
809                    Ok(source.to_string())
810                }
811            }
812            fn grammar(&self) -> &dyn Grammar {
813                Box::leak(grammar_for(Language::Python))
814            }
815            fn version(&self) -> &str {
816                "1.0.0"
817            }
818        }
819
820        let oracle = TranspilerOracle::new(MixedTranspiler);
821        let sources = vec![
822            ("print(1)".to_string(), String::new()),
823            ("FAIL".to_string(), String::new()),
824        ];
825        let (results, stats) = oracle.verify_batch(&sources);
826
827        assert_eq!(results.len(), 2);
828        assert_eq!(stats.total, 2);
829        assert_eq!(stats.transpile_errors, 1);
830    }
831
832    #[test]
833    fn test_transpiler_oracle_verify_generated() {
834        use crate::oracle::PythonExecutor;
835        let executor = PythonExecutor::new();
836        if !executor.is_available() {
837            eprintln!("Python not available, skipping test");
838            return;
839        }
840
841        struct GeneratedTranspiler;
842
843        impl Transpiler for GeneratedTranspiler {
844            fn source_language(&self) -> Language {
845                Language::Python
846            }
847            fn target_language(&self) -> Language {
848                Language::Python
849            }
850            fn transpile(&self, source: &str) -> Result<String> {
851                Ok(source.to_string())
852            }
853            fn grammar(&self) -> &dyn Grammar {
854                Box::leak(grammar_for(Language::Python))
855            }
856            fn version(&self) -> &str {
857                "1.0.0"
858            }
859        }
860
861        let oracle = TranspilerOracle::new(GeneratedTranspiler);
862        let (results, stats) = oracle.verify_generated(5, 2);
863
864        // Should have generated some test cases
865        assert!(results.len() <= 5);
866        assert!(stats.total <= 5);
867    }
868
869    #[test]
870    fn test_transpiler_oracle_new_with_rust_source() {
871        // Test creating oracle with Rust source language (falls back to PythonExecutor)
872        struct RustSourceTranspiler;
873
874        impl Transpiler for RustSourceTranspiler {
875            fn source_language(&self) -> Language {
876                Language::Rust
877            }
878            fn target_language(&self) -> Language {
879                Language::Python
880            }
881            fn transpile(&self, _source: &str) -> Result<String> {
882                Ok("print('hi')".to_string())
883            }
884            fn grammar(&self) -> &dyn Grammar {
885                Box::leak(grammar_for(Language::Python))
886            }
887            fn version(&self) -> &str {
888                "1.0.0"
889            }
890        }
891
892        let oracle = TranspilerOracle::new(RustSourceTranspiler);
893        assert_eq!(oracle.transpiler().source_language(), Language::Rust);
894    }
895}