#[cfg(unix)]
pub mod platform {
use std::io;
pub unsafe fn setup() -> io::Result<()> {
Ok(())
}
pub unsafe fn cleanup() -> io::Result<()> {
Ok(())
}
pub unsafe fn raise_ctrl_c() {
nix::sys::signal::raise(nix::sys::signal::SIGINT).unwrap();
}
pub unsafe fn print(fmt: ::std::fmt::Arguments) {
use self::io::Write;
let stdout = ::std::io::stdout();
stdout.lock().write_fmt(fmt).unwrap();
}
}
#[cfg(windows)]
pub mod platform {
use std::io;
use std::ptr;
use windows_sys::Win32::Foundation::{GENERIC_READ, GENERIC_WRITE, HANDLE, INVALID_HANDLE_VALUE};
use windows_sys::Win32::Storage::FileSystem::{CreateFileA, FILE_SHARE_WRITE, OPEN_EXISTING, WriteFile};
use windows_sys::Win32::System::Console::{
ATTACH_PARENT_PROCESS, AllocConsole, AttachConsole, CTRL_C_EVENT, FreeConsole, GenerateConsoleCtrlEvent, GetConsoleMode,
GetStdHandle, STD_ERROR_HANDLE, STD_OUTPUT_HANDLE, SetStdHandle,
};
enum Output {
Pipe(HANDLE),
Cached(Vec<u8>),
}
static mut OLD_OUT: *mut Output = 0 as *mut Output;
impl io::Write for Output {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match *self {
Output::Pipe(handle) => {
let mut n = 0u32;
if unsafe { WriteFile(handle, buf.as_ptr(), buf.len() as u32, &mut n as *mut u32, ptr::null_mut()) } == 0 {
Err(io::Error::last_os_error())
} else {
Ok(n as usize)
}
}
Output::Cached(ref mut s) => s.write(buf),
}
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Output {
fn new() -> io::Result<Output> {
let stdout = unsafe { GetStdHandle(STD_OUTPUT_HANDLE) };
if stdout.is_null() || stdout == INVALID_HANDLE_VALUE {
return Err(io::Error::last_os_error());
}
let mut out = 0u32;
match unsafe { GetConsoleMode(stdout, &mut out as *mut u32) } {
0 => Ok(Output::Pipe(stdout)),
_ => Ok(Output::Cached(Vec::new())),
}
}
unsafe fn set_as_std(self) -> io::Result<()> {
let stdout = match self {
Output::Pipe(h) => h,
Output::Cached(_) => unsafe { get_stdout() }?,
};
if unsafe { SetStdHandle(STD_OUTPUT_HANDLE, stdout) } == 0 {
return Err(io::Error::last_os_error());
}
if unsafe { SetStdHandle(STD_ERROR_HANDLE, stdout) } == 0 {
return Err(io::Error::last_os_error());
}
match self {
Output::Pipe(_) => Ok(()),
Output::Cached(ref s) => {
use self::io::Write;
let out = io::stdout();
out.lock().write_all(&s[..])?;
Ok(())
}
}
}
}
unsafe fn get_stdout() -> io::Result<HANDLE> {
let stdout = unsafe {
CreateFileA(
c"CONOUT$".as_ptr() as *const u8,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE,
ptr::null_mut(),
OPEN_EXISTING,
0,
0 as HANDLE,
)
};
if stdout.is_null() || stdout == INVALID_HANDLE_VALUE {
Err(io::Error::last_os_error())
} else {
Ok(stdout)
}
}
pub unsafe fn setup() -> io::Result<()> {
let old_out = Output::new()?;
if unsafe { FreeConsole() } == 0 {
return Err(io::Error::last_os_error());
}
if unsafe { AllocConsole() } == 0 {
return Err(io::Error::last_os_error());
}
let stdout = unsafe { get_stdout() }?;
if unsafe { SetStdHandle(STD_OUTPUT_HANDLE, stdout) } == 0 {
return Err(io::Error::last_os_error());
}
if unsafe { SetStdHandle(STD_ERROR_HANDLE, stdout) } == 0 {
return Err(io::Error::last_os_error());
}
unsafe { OLD_OUT = Box::into_raw(Box::new(old_out)) };
Ok(())
}
pub unsafe fn cleanup() -> io::Result<()> {
if unsafe { FreeConsole() } == 0 {
return Err(io::Error::last_os_error());
}
if unsafe { AttachConsole(ATTACH_PARENT_PROCESS) } == 0 {
return Err(io::Error::last_os_error());
}
unsafe { Box::from_raw(OLD_OUT).set_as_std() }?;
Ok(())
}
pub unsafe fn raise_ctrl_c() {
assert!(unsafe { GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0) } != 0);
}
pub unsafe fn print(fmt: ::std::fmt::Arguments) {
use self::io::Write;
{
let stdout = io::stdout();
stdout.lock().write_fmt(fmt).unwrap();
}
{
assert!(!unsafe { OLD_OUT.is_null() });
unsafe { (*OLD_OUT).write_fmt(fmt) }.unwrap();
}
}
}
macro_rules! run_tests {
( $($test_fn:ident),* ) => {
unsafe {
$(
harness::platform::print(format_args!("test {} ... ", stringify!($test_fn)));
$test_fn();
harness::platform::print(format_args!("ok\n"));
)*
}
}
}
pub fn run_harness(f: fn()) {
unsafe { platform::setup() }.unwrap();
let default = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
unsafe { platform::cleanup() }.unwrap();
(default)(info);
}));
println!(" ");
f();
println!(" ");
unsafe { platform::cleanup() }.unwrap();
}