use std::fs; use tempfile::NamedTempFile; use gluon::{ new_vm, vm::api::{Hole, OpaqueValue, OwnedFunction, ValueRef, IO}, Thread, ThreadExt, }; use tokio::runtime::Runtime; #[macro_use] mod support; use crate::support::*; #[test] fn read_file() { let _ = ::env_logger::try_init(); let thread = new_vm(); thread.get_database_mut().run_io(true); let text = r#" let prelude = import! std.prelude let array = import! std.array let { assert } = import! std.test let io @ { ? } = import! std.io let { wrap } = io.applicative let { ? } = import! std.byte let { unwrap } = import! std.option let { flat_map, (>>=) } = import! std.monad do file = io.open_file "Cargo.toml" do bytes = io.read_file file 9 let bytes = unwrap bytes let _ = assert (array.len bytes == 9) let _ = assert (array.index bytes 0 == 91b) // [ let _ = assert (array.index bytes 1 == 112b) // p wrap (array.index bytes 8) "#; let result = thread.run_expr::>("", text); match result { Ok((IO::Value(value), _)) => assert_eq!(value, b']'), Ok((IO::Exception(err), _)) => assert!(false, "{}", err), Err(err) => assert!(false, "{}", err), } } #[test] fn write_and_flush_file() { let _ = ::env_logger::try_init(); let file = NamedTempFile::new().unwrap(); let thread = new_vm(); thread.get_database_mut().run_io(true); let text = r#" let { assert } = import! std.test let { OpenOptions, open_file_with, write_slice_file, flush_file, ? } = import! std.io \path -> do file = open_file_with path [Write] do bytes_written = write_slice_file file [1b, 2b, 3b, 4b] 0 4 let _ = assert (bytes_written == 4) flush_file file "#; let (mut test, _) = thread .run_expr:: IO<()>>>("", &text) .unwrap_or_else(|err| panic!("{}", err)); test.call(file.path().to_str().unwrap().to_owned()) .unwrap_or_else(|err| panic!("{}", err)); let content = fs::read(file.path()).unwrap(); assert_eq!(content, vec![1, 2, 3, 4]); } test_expr! { no_io_eval, r#" let { error } = import! std.prim let io = import! std.io let x = io.flat_map (\x -> error "NOOOOOOOO") (io.println "1") in { x } "# } test_expr! { io_print, r#" let io = import! std.io io.print "123" "# } #[test] fn run_expr_int() { let _ = ::env_logger::try_init(); let text = r#" let io = import! std.io let { flat_map } = io.monad do result = io.run_expr "123" io.applicative.wrap result.value "#; let vm = make_vm(); vm.get_database_mut().run_io(true); let (result, _) = vm.run_expr::>("", text).unwrap(); match result { IO::Value(result) => { let expected = "123"; assert_eq!(result, expected); } IO::Exception(err) => panic!("{}", err), } } test_expr! { io run_expr_io, r#" let io = import! std.io io.flat_map (\x -> io.wrap 100) (io.run_expr " let io = import! \"std/io.glu\" io.print \"123\" ") "#, 100i32 } #[test] fn dont_execute_io_in_run_expr_async() { let _ = ::env_logger::try_init(); let vm = make_vm(); let expr = r#" let prelude = import! std.prelude let io = import! std.io let { wrap } = io.applicative wrap 123 "#; let value = vm .run_expr::>("example", expr) .unwrap_or_else(|err| panic!("{}", err)); assert!( value.0.get_ref() != ValueRef::Int(123), "Unexpected {:?}", value.0 ); } #[test] fn spawn_on_twice() { let _ = ::env_logger::try_init(); let text = r#" let { applicative = { wrap }, monad = { flat_map } } = import! std.io let thread = import! std.thread do child = thread.new_thread () do action = thread.spawn_on child (wrap "abc") action "#; let runtime = Runtime::new().unwrap(); let vm = make_vm(); vm.get_database_mut().run_io(true); let (result, _) = runtime .block_on(vm.run_expr_async::>("", text)) .unwrap_or_else(|err| panic!("{}", err)); match result { IO::Value(result) => { assert_eq!(result, "abc"); } IO::Exception(err) => panic!("{}", err), } let (result, _) = runtime .block_on(vm.run_expr_async::>("", text)) .unwrap_or_else(|err| panic!("{}", err)); match result { IO::Value(result) => { assert_eq!(result, "abc"); } IO::Exception(err) => panic!("{}", err), } } #[test] fn spawn_on_runexpr() { let _ = ::env_logger::try_init(); let text = r#" let io@{ applicative = applicative@{ wrap }, monad = { flat_map }, ? } = import! std.io let thread = import! std.thread do child = thread.new_thread () do action = thread.spawn_on child (io.run_expr "123") do x = action seq io.println x.value wrap x.value "#; let runtime = Runtime::new().unwrap(); let vm = make_vm(); vm.get_database_mut().run_io(true); let (result, _) = runtime .block_on(vm.run_expr_async::>("", text)) .unwrap_or_else(|err| panic!("{}", err)); match result { IO::Value(result) => { assert_eq!(result, "123"); } IO::Exception(err) => panic!("{}", err), } } #[test] fn spawn_on_do_action_twice() { let _ = ::env_logger::try_init(); let text = r#" let { ? } = import! std.io let { ref, modify, load } = import! std.reference let thread = import! std.thread let { wrap } = import! std.applicative let { join } = import! std.monad do counter = ref 0 do child = thread.new_thread () let action = thread.spawn_on child (modify counter (\x -> x + 1)) seq join action seq join action load counter "#; let runtime = Runtime::new().unwrap(); let vm = make_vm(); vm.get_database_mut().run_io(true); let (result, _) = runtime .block_on(vm.run_expr_async::>("", text)) .unwrap_or_else(|err| panic!("{}", err)); assert_eq!(result, IO::Value(2)); } #[test] fn spawn_on_force_action_twice() { let _ = ::env_logger::try_init(); let text = r#" let { ? } = import! std.io let { ref, modify, load } = import! std.reference let thread = import! std.thread let { wrap } = import! std.applicative do counter = ref 0 do child = thread.new_thread () do action = thread.spawn_on child (modify counter (\x -> x + 1)) seq action seq action load counter "#; let runtime = Runtime::new().unwrap(); let vm = make_vm(); vm.get_database_mut().run_io(true); let (result, _) = runtime .block_on(vm.run_expr_async::>("", text)) .unwrap_or_else(|err| panic!("{}", err)); assert_eq!(result, IO::Value(1)); } #[test] fn spawn_on_runexpr_in_catch() { let _ = ::env_logger::try_init(); let text = r#" let prelude = import! std.prelude let io @ { ? } = import! std.io let { Applicative, (*>), wrap } = import! std.applicative let { flat_map, (>>=) } = import! std.monad let thread = import! std.thread let action = do eval_thread = thread.new_thread () do a = thread.spawn_on eval_thread (io.run_expr "123") do result = a wrap result.value (io.catch action wrap >>= io.println) *> wrap "123" "#; let runtime = Runtime::new().unwrap(); let vm = make_vm(); vm.get_database_mut().run_io(true); let (result, _) = runtime .block_on(vm.run_expr_async::>("", text)) .unwrap_or_else(|err| panic!("{}", err)); match result { IO::Value(result) => { assert_eq!(result, "123"); } IO::Exception(err) => panic!("{}", err), } let (result, _) = runtime .block_on(vm.run_expr_async::>("", text)) .unwrap_or_else(|err| panic!("{}", err)); match result { IO::Value(result) => { assert_eq!(result, "123"); } IO::Exception(err) => panic!("{}", err), } } #[test] fn io_error_in_catch1() { let _ = ::env_logger::try_init(); let expr = r#" let io = import! std.io io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error") "#; let vm = make_vm(); vm.get_database_mut().implicit_prelude(false).run_io(true); let (result, _) = vm .run_expr::>("", expr) .unwrap_or_else(|err| panic!("{}", err)); let expected = IO::Value("error".to_string()); assert_eq!(result, expected); } #[test] fn io_error_in_catch2() { let _ = ::env_logger::try_init(); let expr = r#" let { (*>) } = import! std.applicative let io @ { ? } = import! std.io io.catch (io.println "asd" *> io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error") "#; let vm = make_vm(); vm.get_database_mut().implicit_prelude(false).run_io(true); let (result, _) = vm .run_expr::>("", expr) .unwrap_or_else(|err| panic!("{}", err)); let expected = IO::Value("error".to_string()); assert_eq!(result, expected); } #[test] fn io_error_in_catch3() { let _ = ::env_logger::try_init(); let expr = r#" let io @ { ? } = import! std.io let { flat_map } = import! std.monad do _ = io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error") io.catch (io.read_file_to_string "doesnotexist") (\_ -> io.applicative.wrap "error2") "#; let vm = make_vm(); vm.get_database_mut().implicit_prelude(false).run_io(true); let (result, _) = vm .run_expr::>("", expr) .unwrap_or_else(|err| panic!("{}", err)); let expected = IO::Value("error2".to_string()); assert_eq!(result, expected); }