@@ -8,23 +8,27 @@ use std::sync::{Mutex, MutexGuard};
88
99static SEQUENTIAL : Lazy < Mutex < ( ) > > = Lazy :: new ( Mutex :: default) ;
1010
11- /// Allow tests with global variables to run without interfering with each other. The
12- /// lock is released when the MutexGuard variable goes out of scope.
11+ /// Allow tests with side-effects to run without interfering with each other. The
12+ /// lock is released when the MutexGuard variable goes out of scope. Will ignore
13+ /// poison errors from other tests so that our test can continue even if theirs fails.
14+ ///
15+ /// Use this when working with global variables, OS calls, the file system, or other
16+ /// shared resources.
1317///
1418/// # Example
1519/// ```
1620/// use common_testing::setup;
1721///
1822/// #[test]
1923/// fn test_1() {
20- /// let _lock = setup::sequential();
21- /// // test code
24+ /// let _lock = setup::sequential();
25+ /// // test code
2226/// }
2327///
2428/// #[test]
2529/// fn test_2() {
26- /// let _lock = setup::sequential();
27- /// // test code
30+ /// let _lock = setup::sequential();
31+ /// // test code
2832/// }
2933/// ```
3034///
@@ -35,49 +39,91 @@ static SEQUENTIAL: Lazy<Mutex<()>> = Lazy::new(Mutex::default);
3539/// [std::sync::PoisonError](https://doc.rust-lang.org/std/sync/struct.PoisonError.html)
3640///
3741pub fn sequential < ' a > ( ) -> MutexGuard < ' a , ( ) > {
38- SEQUENTIAL . lock ( ) . unwrap ( )
42+ // If another test panics while holding the lock, the lock will be poisoned.
43+ // We ignore the poison error and return the lock anyway so we can continue
44+ // with other tests.
45+ SEQUENTIAL . lock ( ) . unwrap_or_else ( |e| e. into_inner ( ) )
3946}
4047
48+ /// Get an empty vector wrapped in an Rc<RefCell<>>.
49+ ///
4150/// Use to avoid random dependencies in test files for rare test cases.
4251///
4352/// # Example
4453///
4554/// ```
46- /// use common_testing::setup::get_rc_ref_cell_empty_vec ;
55+ /// use common_testing::setup;
4756///
4857/// #[test]
4958/// fn test_1() {
50- /// let vec = get_rc_ref_cell_empty_vec::<u8>();
51- /// // test code
59+ /// let vec = setup:: get_rc_ref_cell_empty_vec::<u8>();
60+ /// // test code
5261/// }
5362/// ```
5463pub fn get_rc_ref_cell_empty_vec < T > ( ) -> Rc < RefCell < std:: vec:: Vec < T > > > {
5564 Rc :: new ( RefCell :: new ( vec ! [ ] ) )
5665}
5766
58- /// Get a read-only file handle. Use to avoid random dependencies in test
59- /// files for rare test cases.
67+ /// Get a read-only file handle. Use for fixtures you never want to change.
68+ ///
69+ /// Prefer setup::get_file_contents() when you need to compare the contents
70+ /// of a file. Prefer setup::get_reader_for_file() when you need a BufReader.
71+ ///
72+ /// Use to avoid random dependencies in test files for rare test cases.
73+ ///
74+ /// # Example
75+ ///
76+ /// ```
77+ /// use common_testing::setup;
78+ ///
79+ /// #[test]
80+ /// fn test_1() {
81+ /// let mut file = setup::get_read_only_file("./fixtures/test.txt").unwrap();
82+ /// // some test code
83+ /// }
84+ /// ```
6085pub fn get_read_only_file ( path : & str ) -> Result < File > {
6186 OpenOptions :: new ( ) . read ( true ) . open ( path)
6287}
6388
64- /// Get a reader for a file. Use to avoid random dependencies in test files
65- /// for rare test cases.
89+ /// Get a BufReader for a file. Use for fixtures you never want to change.
90+ ///
91+ /// Prefer setup::get_file_contents() when you need to compare the contents
92+ /// of a file. Prefer setup::get_read_only_file() when you need a File handle.
93+ ///
94+ /// Use to avoid random dependencies in test files for rare test cases.
95+ ///
96+ /// # Example
97+ ///
98+ /// ```
99+ /// use common_testing::setup;
100+ ///
101+ /// #[test]
102+ /// fn test_1() {
103+ /// let mut reader = setup::get_reader_for_file("./fixtures/test.txt").unwrap();
104+ /// // some test code
105+ /// }
106+ /// ```
66107pub fn get_reader_for_file ( path : & str ) -> Result < BufReader < File > > {
67108 let file: File = get_read_only_file ( path) ?;
68109 Ok ( BufReader :: new ( file) )
69110}
70111
71- /// Read the contents of a file into a vector of bytes.
112+ /// Read the contents of a file into a vector of bytes. Use for fixtures you
113+ /// never want to change.
114+ ///
115+ /// Prefer this over get_reader_for_file() or get_read_only_file() when you
116+ /// need to compare the contents of a file.
72117///
73118/// # Example
74119///
75120/// ```
76- /// use common_testing::setup::get_file_contents ;
121+ /// use common_testing::setup;
77122///
78123/// #[test]
79124/// fn test_1() {
80- /// let contents = get_file_contents("test_file").unwrap();
125+ /// let contents = setup::get_file_contents("./fixtures/test.txt").unwrap();
126+ /// // some test code
81127/// }
82128/// ```
83129pub fn get_file_contents ( path : & str ) -> Result < Vec < u8 > > {
@@ -87,7 +133,26 @@ pub fn get_file_contents(path: &str) -> Result<Vec<u8>> {
87133}
88134
89135/// Get a read and write file handle. Use this to create temporary files for
90- /// testing and comparison.
136+ /// testing and comparison. The file will be created if it does not exist, and
137+ /// it will be overridden if it does.
138+ ///
139+ /// Prefer this function if you are testing dynamic content being written to a
140+ /// file during the test. Prefer setup::get_file_contents() when you need to
141+ /// get the contents of a file to load a fixture or large data. Prefer
142+ /// assert::equal_file_contents() when you need to compare the contents of a
143+ /// file as the result of a test.
144+ ///
145+ /// # Example
146+ ///
147+ /// ```
148+ /// use common_testing::setup;
149+ ///
150+ /// #[test]
151+ /// fn test_1() {
152+ /// let mut file = setup::get_read_and_write_file("./test.txt").unwrap();
153+ /// // some test code
154+ /// }
155+ /// ```
91156pub fn get_read_and_write_file ( path : & str ) -> Result < File > {
92157 remove_file ( path) ?;
93158 let file = OpenOptions :: new ( )
@@ -102,28 +167,63 @@ pub fn get_read_and_write_file(path: &str) -> Result<File> {
102167
103168/// Get a writer for a file, creating the file if it does not exist.
104169/// Use this to create temporary files for testing and comparison.
170+ ///
171+ /// Prefer this function if you are testing dynamic content being written to a
172+ /// file during the test, and remember to call flush() when you are done. Prefer
173+ /// setup::write_file_contents() when you need to create content in a file for
174+ /// the purpose of a test.
175+ ///
176+ /// # Example
177+ ///
178+ /// ```
179+ /// use common_testing::setup;
180+ ///
181+ /// #[test]
182+ /// fn test_1() {
183+ /// let mut writer = setup::get_writer_for_file("./test.txt").unwrap();
184+ /// // some test code
185+ /// }
105186pub fn get_writer_for_file ( path : & str ) -> Result < BufWriter < File > > {
106187 let file: File = get_read_and_write_file ( path) ?;
107188 Ok ( BufWriter :: new ( file) )
108189}
109190
110191/// Write bytes to a file, creating the file if it does not exist.
111- /// Use this to create temporary files for testing and comparison.
192+ ///
193+ /// Prefer this function if you are creating file content for the purpose of a
194+ /// test. Prefer setup::get_writer_for_file() when you are testing the act
195+ /// of writing to a file.
196+ ///
197+ /// # Example
198+ ///
199+ /// ```
200+ /// use common_testing::setup;
201+ ///
202+ /// #[test]
203+ /// fn test_1() {
204+ /// setup::write_file_contents("./test.txt", &[1, 2, 3]).unwrap();
205+ /// // some test code
206+ /// }
207+ /// ```
112208pub fn write_file_contents ( path : & str , contents : & [ u8 ] ) -> Result < ( ) > {
113209 let file: File = get_read_and_write_file ( path) ?;
114210 BufWriter :: new ( file) . write_all ( contents)
115211}
116212
117- /// Create a directory path if it does not exist.
213+ /// Create a directory path if it does not exist. Will not throw an error if the
214+ /// directory already exists. Use to guarantee the filesystem state before a test
215+ /// runs.
118216///
119217/// # Example
120218///
121219/// ```
122- /// use common_testing::setup::create_dir_all ;
220+ /// use common_testing::setup;
123221///
124222/// #[test]
125223/// fn test_1() {
126- /// create_dir_all("test_dir").unwrap();
224+ /// setup::create_dir_all("./.tmp/tests/test_1").unwrap();
225+ /// setup::write_file_contents("./.tmp/tests/test_1/test.txt", &[1, 2, 3]).unwrap();
226+ /// // some test code
127227/// }
128228/// ```
129229pub fn create_dir_all ( path_dir : & str ) -> Result < ( ) > {
@@ -133,16 +233,23 @@ pub fn create_dir_all(path_dir: &str) -> Result<()> {
133233 Ok ( ( ) )
134234}
135235
136- /// Remove a file if it exists.
236+ /// Remove a file if it exists. Will not throw an error if the file does not exist.
237+ ///
238+ /// Use to clean up temporary files created during a test. Prefer calling this
239+ /// function at the beginning of a test to ensure filesystem state is clean and to
240+ /// make debugging easier.
137241///
138242/// # Example
139243///
140244/// ```
141- /// use common_testing::setup::remove_file ;
245+ /// use common_testing::setup;
142246///
143247/// #[test]
144248/// fn test_1() {
145- /// remove_file("test_file").unwrap();
249+ /// setup::remove_file("./test.txt").unwrap();
250+ /// // some test code
251+ /// setup::write_file_contents("./test.txt", &[1, 2, 3]).unwrap();
252+ /// // some more test code
146253/// }
147254/// ```
148255pub fn remove_file ( file_path : & str ) -> Result < ( ) > {
@@ -151,3 +258,85 @@ pub fn remove_file(file_path: &str) -> Result<()> {
151258 }
152259 Ok ( ( ) )
153260}
261+
262+ #[ cfg( test) ]
263+ mod tests {
264+ use std:: io:: Seek ;
265+
266+ use super :: * ;
267+
268+ #[ test]
269+ fn test_sequential_no_error ( ) {
270+ let _lock = sequential ( ) ;
271+ }
272+
273+ #[ test]
274+ fn test_get_rc_ref_cell_empty_vec ( ) {
275+ let _lock = sequential ( ) ;
276+ let vec = get_rc_ref_cell_empty_vec :: < u8 > ( ) ;
277+ assert_eq ! ( vec. borrow( ) . len( ) , 0 ) ;
278+ }
279+
280+ #[ test]
281+ fn test_get_read_only_file ( ) {
282+ let _lock = sequential ( ) ;
283+ let mut file = get_read_only_file ( "./fixtures/test.txt" ) . unwrap ( ) ;
284+ let mut contents = String :: new ( ) ;
285+ file. read_to_string ( & mut contents) . unwrap ( ) ;
286+ assert_eq ! ( contents, "some file content\n " ) ;
287+ }
288+
289+ #[ test]
290+ fn test_get_reader_for_file ( ) {
291+ let _lock = sequential ( ) ;
292+ let mut reader = get_reader_for_file ( "./fixtures/test.txt" ) . unwrap ( ) ;
293+ let mut contents = String :: new ( ) ;
294+ reader. read_to_string ( & mut contents) . unwrap ( ) ;
295+ assert_eq ! ( contents, "some file content\n " ) ;
296+ }
297+
298+ #[ test]
299+ fn test_get_file_contents ( ) {
300+ let _lock = sequential ( ) ;
301+ let contents = get_file_contents ( "./fixtures/test.txt" ) . unwrap ( ) ;
302+ assert_eq ! ( contents, b"some file content\n " ) ;
303+ }
304+
305+ #[ test]
306+ fn test_get_read_and_write_file ( ) {
307+ let _lock = sequential ( ) ;
308+ let mut file = get_read_and_write_file ( "./test.txt" ) . unwrap ( ) ;
309+
310+ // write some content to the file
311+ file. write_all ( b"test\n " ) . unwrap ( ) ;
312+ file. flush ( ) . unwrap ( ) ;
313+
314+ // read the content back
315+ let mut contents = String :: new ( ) ;
316+ file. seek ( std:: io:: SeekFrom :: Start ( 0 ) ) . unwrap ( ) ;
317+ file. read_to_string ( & mut contents) . unwrap ( ) ;
318+ assert_eq ! ( contents, "test\n " ) ;
319+ }
320+
321+ #[ test]
322+ fn test_get_writer_for_file ( ) {
323+ let _lock = sequential ( ) ;
324+ let mut writer = get_writer_for_file ( "./test.txt" ) . unwrap ( ) ;
325+ writer. write_all ( b"test\n " ) . unwrap ( ) ;
326+ writer. flush ( ) . unwrap ( ) ;
327+ let mut file = get_read_only_file ( "./test.txt" ) . unwrap ( ) ;
328+ let mut contents = String :: new ( ) ;
329+ file. read_to_string ( & mut contents) . unwrap ( ) ;
330+ assert_eq ! ( contents, "test\n " ) ;
331+ }
332+
333+ #[ test]
334+ fn test_write_file_contents ( ) {
335+ let _lock = sequential ( ) ;
336+ write_file_contents ( "./test.txt" , b"test\n " ) . unwrap ( ) ;
337+ let mut file = get_read_only_file ( "./test.txt" ) . unwrap ( ) ;
338+ let mut contents = String :: new ( ) ;
339+ file. read_to_string ( & mut contents) . unwrap ( ) ;
340+ assert_eq ! ( contents, "test\n " ) ;
341+ }
342+ }
0 commit comments