unimock 0.6.8

A versatile and developer-friendly trait mocking library
Documentation
#![allow(clippy::write_literal)]
#![allow(clippy::to_string_in_format_args)]

use std::io::{BufRead, BufReader, Write};

use unimock::{
    mock::{
        core::fmt::{DebugMock, DisplayMock},
        std::io::{ReadMock, WriteMock},
    },
    *,
};

#[test]
fn test_display() {
    assert_eq!(
        "u",
        Unimock::new(
            DisplayMock::fmt
                .next_call(matching!(_))
                .answers(&|_, f| write!(f, "u"))
        )
        .to_string()
    );
}

#[test]
#[should_panic = "a Display implementation returned an error unexpectedly: Error"]
fn test_display_error() {
    Unimock::new(
        DisplayMock::fmt
            .next_call(matching!())
            .returns(Err(core::fmt::Error)),
    )
    .to_string();
}

#[test]
fn test_debug() {
    let unimock = Unimock::new(
        DebugMock::fmt
            .next_call(matching!())
            .answers(&|_, f| write!(f, "u")),
    );

    assert_eq!("u", format!("{unimock:?}"));
}

#[test]
fn test_read() {
    let mut reader = BufReader::new(Unimock::new((
        ReadMock::read
            .next_call(matching!(_))
            .answers(&|_, mut f| f.write(b"ok")),
        ReadMock::read
            .next_call(matching!(_))
            .answers(&|_, mut f| f.write(b"\n")),
    )));

    let mut line = String::new();
    let len = reader.read_line(&mut line).unwrap();
    assert_eq!(len, 3);
    assert_eq!("ok\n", line);
}

#[test]
fn test_write() {
    let mut unimock = Unimock::new((
        WriteMock::write_all
            .next_call(matching!(eq!(b"hello ")))
            .returns(Ok(())),
        WriteMock::write_all
            .next_call(matching!(eq!(b"world")))
            .returns(Ok(())),
    ));

    use std::io::Write;
    write!(&mut unimock, "hello {}", "world".to_string()).unwrap();
}

#[test]
#[should_panic = "Write::write_all([119, 111, 114, 108, 100]): Ordered call (2) out of range"]
fn test_write_fail() {
    let mut unimock = Unimock::new(
        WriteMock::write_all
            .next_call(matching!(eq!(b"hello ")))
            .returns(Ok(())),
    );

    use std::io::Write;
    write!(&mut unimock, "hello {}", "world".to_string()).unwrap();
}

#[test]
fn test_fmt_io_duplex_default_impl_implicit() {
    let unimock = Unimock::new((
        DisplayMock::fmt
            .next_call(matching!())
            .answers(&|_, f| write!(f, "hello {}", "unimock".to_string())),
        // NOTE: write! calls `write_all` which should get re-routed to `write`:
        WriteMock::write
            .next_call(matching!(eq!(b"hello ")))
            .returns(Ok(6)),
        WriteMock::write
            .next_call(matching!(eq!(b"unimock")))
            .returns(Ok("uni".len())),
        WriteMock::write
            .next_call(matching!(eq!(b"mock")))
            .returns(Ok("mock".len())),
    ));
    write!(&mut unimock.clone(), "{unimock}").unwrap();
}

#[test]
fn test_fmt_io_duplex_default_impl_explicit() {
    let unimock = Unimock::new((
        DisplayMock::fmt
            .next_call(matching!())
            .answers(&|_, f| write!(f, "hello {}", "unimock".to_string())),
        WriteMock::write_all
            .next_call(matching!(eq!(b"hello ")))
            .applies_default_impl(),
        WriteMock::write
            .next_call(matching!(eq!(b"hello ")))
            .returns(Ok(6)),
        WriteMock::write_all
            .next_call(matching!(eq!(b"unimock")))
            .applies_default_impl(),
        WriteMock::write
            .next_call(matching!(eq!(b"unimock")))
            .returns(Ok("uni".len())),
        WriteMock::write
            .next_call(matching!(eq!(b"mock")))
            .returns(Ok("mock".len())),
    ));
    write!(&mut unimock.clone(), "{unimock}").unwrap();
}

mod termination {
    use std::process::{ExitCode, Termination};

    use unimock::{mock::std::process::TerminationMock, *};

    #[test]
    fn termination_ok() -> Unimock {
        Unimock::new(())
    }

    #[unimock(api=NonsenseMock)]
    trait Nonsense {
        fn nonsense(&self);
    }

    #[test]
    fn unmocked_termination_ok() {
        let exit_code = Unimock::new(()).report();
        assert_eq!("ExitCode(unix_exit_status(0))", format!("{exit_code:?}"));
    }

    #[test]
    fn unmocked_termination_fail() {
        let exit_code =
            Unimock::new(NonsenseMock::nonsense.next_call(matching!(_)).returns(())).report();
        assert_eq!("ExitCode(unix_exit_status(1))", format!("{exit_code:?}"));
    }

    #[test]
    fn mocked_termination() {
        let u = Unimock::new(
            TerminationMock::report
                .next_call(matching!())
                .returns(ExitCode::FAILURE)
                .once()
                .then()
                .returns(ExitCode::SUCCESS),
        );
        assert_eq!(
            "ExitCode(unix_exit_status(1))",
            format!("{:?}", u.clone().report())
        );
        assert_eq!("ExitCode(unix_exit_status(0))", format!("{:?}", u.report()));
    }
}