mod tools; use tools::*; #[test] fn missing_closure() { let source = program!( { #![allow(compile_fail)] use spork::*; pub fn main() { assert_eq!(spawn!(add(1, 2)).unwrap().join().unwrap(), 3); } } ); let (stdout, stderr) = should_fail_compile(source); if !stderr.contains("Expected closure syntax. Help: try `spawn!(|| add(1, 2))`") { panic!( "Missing error message. stdout: {}\nstderr: {}", stdout, stderr ); } } #[test] fn bad_spawn_expression() { let source = program!( { #![allow(compile_fail)] use spork::*; pub fn main() { assert_eq!(spawn!(|| 1 + 2).unwrap().join().unwrap(), 3); } } ); let (stdout, stderr) = should_fail_compile(source); if !stderr.contains("Expected function call or `ref` before a function call. Cannot spawn closure with arbitrary expression `1 + 2`.") { panic!( "Missing error message. stdout: {}\nstderr: {}", stdout, stderr ); } } #[test] fn missing_spork() { let source = program!({ use spork::errors::*; use spork::*; fn add(a: i32, b: i32) -> i32 { a + b } pub fn main() { assert_eq!( spawn!(|| add(1, 2)).unwrap_err(), SpawnError::SporkNeverCalled ); } }); let (stdout, stderr) = run_program(source, ""); assert_eq!(stdout, ""); assert_eq!(stderr, ""); } #[test] fn missing_dispatch_entry() { let source = program!({ use spork::errors::*; use spork::*; fn add(a: i32, b: i32) -> i32 { a + b } pub fn main() { spork(DispatchTable::new()); assert_eq!( spawn!(|| add(1, 2)).unwrap().join().unwrap_err(), JoinError::Child(ChildError::Dispatch( DispatchError::CallableNotInDispatchTable )) ); } }); let (stdout, stderr) = run_program(source, ""); assert_eq!(stdout, ""); assert_eq!(stderr, ""); } #[test] fn child_panic() { let source = program!({ use spork::*; use spork::errors::*; fn add(_a: i32, _b: i32) -> i32 { panic!("test panic"); } pub fn main() { let mut table = DispatchTable::new(); table.insert(add); spork(table); let err = spawn!(|| add(1, 2)).unwrap().join().unwrap_err(); match err { JoinError::Child(ChildError::Panic(panic)) => { assert_eq!(panic.message(), "test panic"); } err => panic!("{}", err), } } }); let (stdout, stderr) = run_program(source, ""); assert_eq!(stdout, ""); assert_eq!(stderr, ""); } #[test] fn child_panic_but_caught() { let source = program!({ use spork::*; use spork::errors::*; fn add(a: i32, b: i32) -> i32 { let _ = std::panic::catch_unwind(|| { panic!("test panic"); }); a + b } pub fn main() { let mut table = DispatchTable::new(); table.insert(add); spork(table); let err = spawn!(|| add(1, 2)).unwrap().join().unwrap_err(); match err { JoinError::Child(ChildError::Panic(panic)) => { assert_eq!(panic.message(), "test panic"); } err => panic!("{}", err), } } }); let (stdout, stderr) = run_program(source, ""); if !stderr.contains("sent panic to parent process, but also got a result from the callable") { panic!( "Missing error message. stdout: {}\nstderr: {}", stdout, stderr ); } } #[test] fn child_panic_before_spork() { let source = program!({ use spork::*; use spork::errors::*; fn add(a: i32, b: i32) -> i32 { a + b } pub fn main() { if let ProcessType::Child = current_process_type() { panic!("is child"); } spork(DispatchTable::new()); let err = spawn!(|| add(1, 2)).unwrap_err(); match err { SpawnError::FailedToSendArgs(err) => { assert_eq!(err.to_string(), "child process did not connect to parent process"); } err => panic!("{}", err), } } }); let (stdout, stderr) = run_program(source, ""); if !stderr.contains("is child") { panic!( "Missing error message. stdout: {}\nstderr: {}", stdout, stderr ); } } #[test] fn child_crash() { let source = program!({ use spork::*; use spork::errors::*; fn add(_a: i32, _b: i32) -> i32 { std::process::exit(0); } pub fn main() { let mut table = DispatchTable::new(); table.insert(add); spork(table); let err = spawn!(|| add(1, 2)).unwrap().join().unwrap_err(); match err { JoinError::ReturnMissing(err) => { assert_eq!(err.to_string(), "IPC channel disconnected"); } err => panic!("{}", err), } } }); let (stdout, stderr) = run_program(source, ""); assert_eq!(stdout, ""); assert_eq!(stderr, ""); }