use xbasic::xbasic::XBasic; mod common; #[test] fn function_empty() { common::test_program( " a() function a() end function ", "", ) } #[test] fn function_uncalled() { common::test_program( " function a() print \"Hello world\" end function ", "", ) } #[test] fn function_no_return() { common::test_program( " a() function a() print \"hi from function\" end function ", "hi from function\n", ) } #[test] fn function_parameter() { common::test_program( " a(\"world\", \"hello\") function a(b, c) print c b end function ", "hello world\n", ) } #[test] fn function_variable_same_name() { common::test_program( " a = 3 print a() print a function a() return 3 + 4 end function ", "7\n3\n", ) } #[test] fn function_return() { common::test_program( " print a() function a() return 3 + 4 end function ", "7\n", ) } #[test] fn recursion_ok() { common::test_program( " print fib(20) function fib(n) if n = 1 or n = 2 then return 1 end if return fib(n - 1) + fib(n - 2) end function ", "6765\n", ) } #[test] fn recursion_stack_overflow() { let tio = common::TestIO::new(""); let mut xb = XBasic::new(tio); assert!(xb .run( " function a() a() end function a() " ) .is_err()); assert!(xb.error_handler.had_errors); assert_eq!( xb.error_handler.errors, ["[line 3] Error : Stack overflow."] ); xb.get_io().check(); } #[test] fn function_does_not_exist() { let tio = common::TestIO::new(""); let mut xb = XBasic::new(tio); assert!(xb .run( " a() " ) .is_err()); assert!(xb.error_handler.had_errors); assert_eq!( xb.error_handler.errors, ["[line 2] Error at 'a': Not a function."] ); xb.get_io().check(); } #[test] fn function_already_defined() { let tio = common::TestIO::new(""); let mut xb = XBasic::new(tio); assert!(xb .run( " function a() end function function a() end function " ) .is_err()); assert!(xb.error_handler.had_errors); assert_eq!( xb.error_handler.errors, ["[line 5] Error at 'a': Function is already defined on line 2."] ); xb.get_io().check(); } #[test] fn function_mismatched_arity() { let tio = common::TestIO::new(""); let mut xb = XBasic::new(tio); assert!(xb .run( " a(1, 2, 3, 4) function a(b, c, d) end function " ) .is_err()); assert!(xb.error_handler.had_errors); assert_eq!( xb.error_handler.errors, ["[line 2] Error at 'a': Expected 3 arguments, got 4."] ); xb.get_io().check(); } #[test] fn function_named_same_as_param() { common::test_program( " print a(3) function a(a) return a + 4 end function ", "7\n", ) } #[test] fn nested_functions() { common::test_program( " print a(3) function a(a) return b(a) + 2 end function function b(c) return c + 5 end function ", "10\n", ) } #[test] fn stack_cool_after_function() { common::test_program( " c = 4 print a(3) b = 3 print b print c print b + c function a(a) return b(a) + 2 end function function b(c) return c + 5 end function ", "10\n3\n4\n7\n", ) } #[test] fn return_in_main() { common::test_program( " return ", "", ) } #[test] fn function_across_multiple_calls() { let tio = common::TestIO::new("5\n"); let mut xb = XBasic::new(tio); assert!(xb .run( " function a(b) return b end function " ) .is_ok()); assert!(xb.run("print a(5)\n").is_ok()); assert!(!xb.error_handler.had_errors); xb.get_io().check(); } #[test] fn function_overwrite_across_runs() { let tio = common::TestIO::new("5\n"); let mut xb = XBasic::new(tio); assert!(xb .run( " function a(b) return b end function " ) .is_ok()); assert!(xb .run( " function a(b) return b * 2 end function " ) .is_err()); xb.clear_errors(); assert!(xb.run("print a(5)\n").is_ok()); assert!(!xb.error_handler.had_errors); xb.get_io().check(); } #[test] fn function_variable_edge_case() { let tio = common::TestIO::new("done\n"); let mut xb = XBasic::new(tio); assert!(xb .run( " function func(b) a = 0 end function func(3) z = 0 b = 0 print \"done\" ", ) .is_ok()); assert!(!xb.error_handler.had_errors); xb.get_io().check(); } #[test] fn function_argument_edge_case() { let tio = common::TestIO::new("3\ndone\n"); let mut xb = XBasic::new(tio); assert!(xb .run( " a = test function func(b) c = 0 print b end function func(3) z = 0 print \"done\" ", ) .is_ok()); assert!(!xb.error_handler.had_errors); xb.get_io().check(); } #[test] fn function_doesnt_corrupt_stack() { let tio = common::TestIO::new("3\n0\n1\n2\n3\n4\n5\n"); let mut xb = XBasic::new(tio); assert!(xb .run( " function func(b) print b end function func(3) for x = 0 to 5 print x next x ", ) .is_ok()); assert!(!xb.error_handler.had_errors); xb.get_io().check(); }