aboutsummaryrefslogtreecommitdiff
path: root/src/fs/mocks.rs
blob: 68ef4f3a7a454ef624f91b63d38d5274b2c982b2 (plain)
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! Mock version of std::fs::File;
use mockall::mock;

use crate::sync::oneshot;
use std::{
    cell::RefCell,
    collections::VecDeque,
    fs::{Metadata, Permissions},
    future::Future,
    io::{self, Read, Seek, SeekFrom, Write},
    path::PathBuf,
    pin::Pin,
    task::{Context, Poll},
};

mock! {
    #[derive(Debug)]
    pub File {
        pub fn create(pb: PathBuf) -> io::Result<Self>;
        // These inner_ methods exist because std::fs::File has two
        // implementations for each of these methods: one on "&mut self" and
        // one on "&&self".  Defining both of those in terms of an inner_ method
        // allows us to specify the expectation the same way, regardless of
        // which method is used.
        pub fn inner_flush(&self) -> io::Result<()>;
        pub fn inner_read(&self, dst: &mut [u8]) -> io::Result<usize>;
        pub fn inner_seek(&self, pos: SeekFrom) -> io::Result<u64>;
        pub fn inner_write(&self, src: &[u8]) -> io::Result<usize>;
        pub fn metadata(&self) -> io::Result<Metadata>;
        pub fn open(pb: PathBuf) -> io::Result<Self>;
        pub fn set_len(&self, size: u64) -> io::Result<()>;
        pub fn set_permissions(&self, _perm: Permissions) -> io::Result<()>;
        pub fn sync_all(&self) -> io::Result<()>;
        pub fn sync_data(&self) -> io::Result<()>;
        pub fn try_clone(&self) -> io::Result<Self>;
    }
    #[cfg(windows)]
    impl std::os::windows::io::AsRawHandle for File {
        fn as_raw_handle(&self) -> std::os::windows::io::RawHandle;
    }
    #[cfg(windows)]
    impl std::os::windows::io::FromRawHandle for File {
        unsafe fn from_raw_handle(h: std::os::windows::io::RawHandle) -> Self;
    }
    #[cfg(unix)]
    impl std::os::unix::io::AsRawFd for File {
        fn as_raw_fd(&self) -> std::os::unix::io::RawFd;
    }

    #[cfg(unix)]
    impl std::os::unix::io::FromRawFd for File {
        unsafe fn from_raw_fd(h: std::os::unix::io::RawFd) -> Self;
    }
}

impl Read for MockFile {
    fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
        self.inner_read(dst)
    }
}

impl Read for &'_ MockFile {
    fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
        self.inner_read(dst)
    }
}

impl Seek for &'_ MockFile {
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
        self.inner_seek(pos)
    }
}

impl Write for &'_ MockFile {
    fn write(&mut self, src: &[u8]) -> io::Result<usize> {
        self.inner_write(src)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.inner_flush()
    }
}

thread_local! {
    static QUEUE: RefCell<VecDeque<Box<dyn FnOnce() + Send>>> = RefCell::new(VecDeque::new())
}

#[derive(Debug)]
pub(super) struct JoinHandle<T> {
    rx: oneshot::Receiver<T>,
}

pub(super) fn spawn_blocking<F, R>(f: F) -> JoinHandle<R>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    let (tx, rx) = oneshot::channel();
    let task = Box::new(move || {
        let _ = tx.send(f());
    });

    QUEUE.with(|cell| cell.borrow_mut().push_back(task));

    JoinHandle { rx }
}

impl<T> Future for JoinHandle<T> {
    type Output = Result<T, io::Error>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        use std::task::Poll::*;

        match Pin::new(&mut self.rx).poll(cx) {
            Ready(Ok(v)) => Ready(Ok(v)),
            Ready(Err(e)) => panic!("error = {:?}", e),
            Pending => Pending,
        }
    }
}

pub(super) mod pool {
    use super::*;

    pub(in super::super) fn len() -> usize {
        QUEUE.with(|cell| cell.borrow().len())
    }

    pub(in super::super) fn run_one() {
        let task = QUEUE
            .with(|cell| cell.borrow_mut().pop_front())
            .expect("expected task to run, but none ready");

        task();
    }
}