use myc::ast::environment::Environment; use myc::ast::types::Value; #[test] fn test_rtl_operators() { let env = Environment::new(); // --- Arithmetic --- assert_eq!(format!("{}", env.run_script("(+ 10 20)").unwrap()), "30"); assert_eq!(format!("{}", env.run_script("(- 20 10)").unwrap()), "10"); assert_eq!(format!("{}", env.run_script("(* 10 20)").unwrap()), "200"); assert_eq!(format!("{}", env.run_script("(/ 20 10)").unwrap()), "2"); assert_eq!(format!("{}", env.run_script("(// 20 3)").unwrap()), "6"); assert_eq!(format!("{}", env.run_script("(% 20 3)").unwrap()), "2"); // --- Logic / Bitwise --- assert_eq!( format!("{}", env.run_script("(and true false)").unwrap()), "false" ); assert_eq!( format!("{}", env.run_script("(or true false)").unwrap()), "true" ); assert_eq!( format!("{}", env.run_script("(xor true false)").unwrap()), "true" ); assert_eq!( format!("{}", env.run_script("(not true)").unwrap()), "false" ); assert_eq!(format!("{}", env.run_script("(<< 1 2)").unwrap()), "4"); assert_eq!(format!("{}", env.run_script("(>> 4 1)").unwrap()), "2"); assert_eq!(format!("{}", env.run_script("(and 3 1)").unwrap()), "1"); // --- Comparison --- assert_eq!(format!("{}", env.run_script("(= 10 10)").unwrap()), "true"); assert_eq!(format!("{}", env.run_script("(= 10 20)").unwrap()), "false"); assert_eq!(format!("{}", env.run_script("(<> 10 20)").unwrap()), "true"); assert_eq!(format!("{}", env.run_script("(< 10 20)").unwrap()), "true"); assert_eq!(format!("{}", env.run_script("(> 10 20)").unwrap()), "false"); assert_eq!(format!("{}", env.run_script("(<= 10 10)").unwrap()), "true"); assert_eq!(format!("{}", env.run_script("(>= 10 10)").unwrap()), "true"); // --- NaN --- assert_eq!(format!("{}", env.run_script("NaN").unwrap()), "NaN"); } #[test] fn test_random_isolation_between_environments() { let env1 = Environment::new(); let env2 = Environment::new(); let val1_a = env1.run_script("(do (def rand (make-random 123)) (rand))").unwrap(); let val2_a = env2.run_script("(do (def rand (make-random)) (rand))").unwrap(); assert_ne!( val1_a, val2_a, "Environments must have isolated PRNG states" ); let val2_b = env2.run_script("(do (def rand-same (make-random 123)) (rand-same))").unwrap(); assert_eq!( val1_a, val2_b, "Different environments with the same seed must produce the same sequence" ); } #[test] fn test_random_seeding_determinism() { let env = Environment::new(); let val1 = env.run_script("(do (def rand1 (make-random 42)) (rand1))").unwrap(); let val2 = env.run_script("(do (def rand2 (make-random 42)) (rand2))").unwrap(); assert_eq!(val1, val2, "Random results must be identical for the same seed"); let val3 = env.run_script("(do (def rand3 (make-random 123)) (rand3))").unwrap(); assert_ne!(val1, val3, "Random results must differ for different seeds"); } #[test] fn test_now_function_not_folded() { let env = Environment::new(); let source = "(now)"; let result = env.run_script(source).expect("Failed to run script"); if let Value::DateTime(ts) = result { let current = chrono::Utc::now().timestamp_millis(); assert!(ts > 0); assert!(ts <= current); } else { panic!("Expected DateTime, got {:?}", result); } let dump = env.dump_ast(source).expect("Failed to dump AST"); assert!( dump.contains("Call"), "now() should remain a Call, not a Constant. Dump: \n{}", dump ); assert!( !dump.contains("Constant: #"), "now() should NOT be folded into a specific timestamp constant. Dump: \n{}", dump ); } #[test] fn test_date_parsing() { let env = Environment::new(); let res = env.run_script("(date \"2023-01-01\")").unwrap(); if let Value::DateTime(_) = res { // OK } else { panic!("Expected DateTime, got {:?}", res); } }