1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! Async testing utilities
//!
//! # Examples
//!
//! ```
//! // tbi
//! ```

#![forbid(unsafe_code, future_incompatible, rust_2018_idioms)]
#![deny(missing_debug_implementations, nonstandard_style)]
#![warn(missing_docs, missing_doc_code_examples, unreachable_pub)]

use async_std::fs::File;
use async_std::io::prelude::*;
use async_std::io::{self, SeekFrom};
use async_std::sync::Arc;
use async_std::task::{Context, Poll};
use std::pin::Pin;
use std::sync::Mutex;

/// An async testcase that can be written to and read from.
#[derive(Clone, Debug)]
pub struct TestCase {
    reader: Arc<Mutex<File>>,
    writer: Arc<Mutex<File>>,
}

impl TestCase {
    /// Create a new instance.
    pub async fn new(reader: &str, writer: &str) -> TestCase {
        use std::io::Write;

        let mut temp = tempfile::tempfile().expect("Failed writer create tempfile");
        temp.write(reader.as_bytes())
            .expect("Could not write writer dest file");
        let mut file: File = temp.into();
        file.seek(SeekFrom::Start(0)).await.unwrap();
        let reader = Arc::new(Mutex::new(file.into()));

        let mut temp = tempfile::tempfile().expect("Failed writer create tempfile");
        temp.write(writer.as_bytes())
            .expect("Could not write writer dest file");
        let mut file: File = temp.into();
        file.seek(SeekFrom::Start(0)).await.unwrap();
        let writer = Arc::new(Mutex::new(file.into()));

        TestCase { reader, writer }
    }

    /// Get the value of the "writer" string.
    pub async fn writer(&self) -> String {
        let mut writer = String::new();
        let mut file = self.writer.lock().unwrap();
        file.seek(SeekFrom::Start(0)).await.unwrap();
        file.read_to_string(&mut writer).await.unwrap();
        writer
    }

    /// Assert the reader.
    pub async fn assert_reader(&self, rhs: &str) {
        let lhs = self.reader().await;
        pretty_assertions::assert_eq!(lhs, rhs);
    }

    /// Assert the reader but pass it through a closure first..
    pub async fn assert_reader_with(&self, rhs: &str, f: impl Fn(&mut String, &mut String)) {
        let mut lhs = self.reader().await;
        let mut rhs = String::from(rhs);
        f(&mut lhs, &mut rhs);
        pretty_assertions::assert_eq!(lhs, rhs);
    }

    /// Assert the writer.
    pub async fn assert_writer(&self, rhs: &str) {
        let lhs = self.writer().await;
        pretty_assertions::assert_eq!(lhs, rhs);
    }

    /// Assert the reader but pass it through a closure first..
    pub async fn assert_writer_with(&self, rhs: &str, f: impl Fn(&mut String, &mut String)) {
        let mut lhs = self.writer().await;
        let mut rhs = String::from(rhs);
        f(&mut lhs, &mut rhs);
        pretty_assertions::assert_eq!(lhs, rhs);
    }

    /// Get the value of the "reader" string.
    pub async fn reader(&self) -> String {
        let mut reader = String::new();
        let mut file = self.reader.lock().unwrap();
        file.seek(SeekFrom::Start(0)).await.unwrap();
        file.read_to_string(&mut reader).await.unwrap();
        reader
    }
}

impl Read for TestCase {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut [u8],
    ) -> Poll<io::Result<usize>> {
        Pin::new(&mut &*self.reader.lock().unwrap()).poll_read(cx, buf)
    }
}

impl Write for TestCase {
    fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {
        Pin::new(&mut &*self.writer.lock().unwrap()).poll_write(cx, buf)
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        Pin::new(&mut &*self.writer.lock().unwrap()).poll_flush(cx)
    }

    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        Pin::new(&mut &*self.writer.lock().unwrap()).poll_close(cx)
    }
}