virtual-io 0.1.0

Mock stdin/out/err for testing
Documentation
use crate::{
    helpers::{append_stdout, VioFakeMessage},
    vio_fake::VioFake,
};

/// Create a new `FakeVio` to be used for testing.
///
/// The builder first lets you specify the expected IO messages, then lets you
/// create the `FakeVio`.
///
/// ```
/// use virtual_io::VirtualIo;
///
/// fn get_name_base(vio: &mut impl VirtualIo) -> String {
///     vio.print("What is your name? ");
///     let name = vio.read_line();
///     vio.println(format!("Hello, {}!", name));
///     name
/// }
///
/// #[cfg(test)]
/// mod test {
///     use super::*;
///     use virtual_io::VioFakeBuilder;
///
///     #[test]
///     fn test_get_name() {
///         // Create a fake version of vio that we can inject into the base
///         // function.
///         let mut vio = VioFakeBuilder::new()
///             // Add the expected io calls.
///             .expect_stdout("What is your name? ")
///             .provide_stdin("John")
///             .expect_stdout("Hello, John!\n")
///             // Build the fake vio.
///             .build();
///         // Assert that the return value is correct.
///         assert_eq!(get_name_base(&mut vio), "John");
///         // Assert that all io operations were identical to what was expected.
///         assert_eq!(vio.get_actual(), vio.get_expected());
///     }
/// }
/// ```
#[derive(PartialEq, Eq, Debug, Default)]
pub struct VioFakeBuilder {
    messages: Vec<VioFakeMessage>,
    environment_variables: Vec<(String, String)>,
}

impl VioFakeBuilder {
    #[must_use]
    pub const fn new() -> Self {
        Self {
            messages: Vec::new(),
            environment_variables: Vec::new(),
        }
    }

    pub fn expect_stdout<S: Into<String>>(&mut self, message: S) -> &mut Self {
        append_stdout(&mut self.messages, message.into());
        self
    }

    pub fn provide_stdin<S: Into<String>>(&mut self, message: S) -> &mut Self {
        self.messages.push(VioFakeMessage::StdIn(message.into()));
        self
    }

    pub fn set_environment_var<S: Into<String>>(&mut self, name: S, value: S) -> &mut Self {
        let name = name.into();
        let value = value.into();
        for var in &mut self.environment_variables {
            if var.0 == name {
                var.1 = value;
                return self;
            }
        }
        self.environment_variables.push((name, value));
        self
    }

    #[must_use]
    pub fn build(&self) -> VioFake {
        VioFake::new(self.messages.clone(), self.environment_variables.clone())
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn vio_fake_builder_accepts_input() {
        assert_eq!(
            VioFakeBuilder::new()
                .provide_stdin("John")
                .build()
                .expected_messages,
            vec![VioFakeMessage::StdIn("John".to_string())]
        );
    }

    #[test]
    fn vio_fake_builder_inputs_do_not_collapse() {
        assert_eq!(
            VioFakeBuilder::new()
                .provide_stdin("John")
                .provide_stdin("Smith")
                .build()
                .expected_messages,
            vec![
                VioFakeMessage::StdIn("John".to_string()),
                VioFakeMessage::StdIn("Smith".to_string())
            ]
        );
    }

    #[test]
    fn vio_fake_builder_prints() {
        assert_eq!(
            VioFakeBuilder::new()
                .expect_stdout("John")
                .build()
                .expected_messages,
            vec![VioFakeMessage::StdOut("John".to_string())]
        );
    }

    #[test]
    fn vio_fake_builder_consecutive_print_statements_collapse_into_single_message() {
        assert_eq!(
            VioFakeBuilder::new()
                .expect_stdout("I'm")
                .expect_stdout(" ")
                .expect_stdout("John")
                .expect_stdout(' ')
                .expect_stdout("Smith\n")
                .expect_stdout("> What's your name? ")
                .provide_stdin("Jane Doe")
                .expect_stdout("Your name is Jane Doe.\n")
                .expect_stdout("Nice to meet you Jane Doe.\n")
                .build()
                .expected_messages,
            vec![
                VioFakeMessage::StdOut("I'm John Smith\n> What's your name? ".to_string()),
                VioFakeMessage::StdIn("Jane Doe".to_string()),
                VioFakeMessage::StdOut(
                    "Your name is Jane Doe.\nNice to meet you Jane Doe.\n".to_string()
                )
            ]
        );
    }

    #[test]
    fn vio_fake_builder_environment_variables() {
        assert_eq!(
            VioFakeBuilder::new()
                .set_environment_var("HELLO", "WORLD")
                .set_environment_var("MY_MOOD", "GOOD")
                // overwrite the value of an existing environment variable
                .set_environment_var("MY_MOOD", "SO_SO")
                .build()
                .environment_variables,
            vec![
                ("HELLO".to_string(), "WORLD".to_string()),
                ("MY_MOOD".to_string(), "SO_SO".to_string()),
            ]
        );
    }
}