test_temp_file/
lib.rs

1//! This crate allows creation and automatic deletion (based on Drop trait) of files.
2//! This is aimed mostly for testing purposes, for example when testing a parser you probably
3//! want to read/write file and validate their content
4
5use rand::Rng;
6use std::io::Read;
7use std::path::Path;
8use std::io::Write;
9use std::fs::OpenOptions;
10use std::io::SeekFrom;
11use std::io::Seek;
12
13#[derive(Debug)]
14pub struct TestTempFile {
15    filename: String,
16    random_number: i32,
17    final_filename: String,
18    file: std::fs::File
19}
20
21impl Drop for TestTempFile {
22    fn drop(&mut self) {
23        self.delete_file();
24    }
25}
26
27impl Write for TestTempFile {
28    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
29        self.file.write(buf)
30    }
31
32    fn flush(&mut self) -> std::io::Result<()> {
33        self.file.flush()
34    }
35
36    fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
37        self.file.write_all(buf)
38    }
39}
40
41impl Read for TestTempFile {
42    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
43        self.file.read(buf)
44    }
45}
46
47impl Seek for TestTempFile {
48    fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> { self.file.seek(pos) }
49}
50
51impl TestTempFile {
52    ///
53    ///  /// # Arguments
54    ///
55    /// * `filename` - A String containing the file name
56    ///
57    /// # Examples
58    /// ```
59    /// use test_temp_file::TestTempFile;
60    /// let mut t = TestTempFile::new(String::from("file_name.txt"));
61    /// ```
62    pub fn new(filename: String) -> TestTempFile {
63        TestTempFile::gen_random_name(filename)
64    }
65
66    fn gen_random_name(filename: String) -> TestTempFile {
67        let mut rng = rand::thread_rng();
68        let random_number = rng.gen_range(0, i32::max_value());
69        let final_filename = format!("_{}_{}", random_number, filename);
70        let file = OpenOptions::new().
71            create(true).
72            write(true).
73            read(true).
74            open(final_filename.clone()).unwrap();
75        TestTempFile {
76            filename,
77            random_number,
78            final_filename,
79            file
80        }
81    }
82
83    fn delete_file(&mut self) {
84        let final_filename = self.final_filename.as_str();
85        if Path::new(final_filename).exists() {
86            std::fs::remove_file(final_filename);
87        }
88    }
89}
90
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    
96    use std::str;
97
98    static FILE_NAME: &'static str = "test_file.txt";
99
100    #[test]
101    fn test_constructor() {
102        let t = TestTempFile::new(String::from(FILE_NAME));
103        assert_eq!(FILE_NAME, t.filename);
104    }
105
106    #[test]
107    fn test_write() {
108        let mut t = TestTempFile::new(String::from(FILE_NAME));
109        match t.write_all(b"some bytes") {
110            Ok(_) => assert!(true),
111            Err(e) => assert!(false, e)
112        }
113    }
114
115    #[test]
116    fn test_write_and_read() {
117        let mut t = TestTempFile::new(String::from(FILE_NAME));
118        let mut buffer = [0; 10];
119        let msg = b"some bytes";
120        match t.write_all(msg) {
121            Ok(_) => assert!(true),
122            Err(e) => assert!(false, e)
123        }
124
125        // Need to rewind pointer inside file, since after the write we're pointing to the end
126
127        t.seek(SeekFrom::Start(0));
128
129        match t.read(&mut buffer[..]) {
130            Ok(n) => {
131                assert_eq!(msg,
132                           &buffer[..n],
133                           "Left:{:#?}\nRight:{:#?}\n{:#?}",
134                           msg, &buffer[..n], t)
135            },
136            Err(e) => assert!(false, format!("Error:{}\n{:#?}", e.to_string(), t))
137        }
138    }
139}