Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 70f4b90

Browse files
committed
add tests to setup; handle poison error for setup::sequential
1 parent a64adca commit 70f4b90

4 files changed

Lines changed: 221 additions & 31 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ sqlite.db
88
sqlite.db*
99
.tmp
1010
.DS_Store
11+
test.txt

common-testing/README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,9 @@ fn test_2() {
116116

117117
### Strict Equality Assertions
118118

119-
This library provides strict equality assertions that will fail if the types are not the same.
119+
This library provides strict equality assertions that will fail at compile time if the types are not comparable with PartialEq.
120120

121-
The marcos for testing equality (assert!, assert_eq!, assert_ne!) are not strict enough regarding types. For example, `assert_eq!(1, 1.0)` will pass. Also, the marcos will sometimes not detect issues until runtime. By providing non-macro functions that require PartialEq, the compiler catches more issues at compile time.
122-
123-
We also standardize the equality assertions to always print comparisons of the values in the error message, and require the values to be references to prevent assertions from taking accidental ownership.
121+
Stardardizes the error message to always print comparisons of the values, and require the values to be references to prevent assertions from taking accidental ownership.
124122

125123
- assert::equal
126124
- assert::not_equal

common-testing/src/assert.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ where
3535
/// Asserts two values are equal using PartialEq, allowing for different
3636
/// types to be compared.
3737
///
38-
/// Error message will show the values that were compared.
38+
/// Error message will show the values that were compared using
39+
/// `pretty_assertions` crate.
3940
///
4041
/// # Example
4142
///
@@ -63,7 +64,8 @@ where
6364
/// Asserts two values are not equal using PartialEq, allowing for
6465
/// different types to be compared.
6566
///
66-
/// Error message will show the values that were compared.
67+
/// Error message will show the values that were compared using
68+
/// `pretty_assertions` crate.
6769
///
6870
/// # Example
6971
///

common-testing/src/setup.rs

Lines changed: 214 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,27 @@ use std::sync::{Mutex, MutexGuard};
88

99
static 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
///
3741
pub 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
/// ```
5463
pub 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+
/// ```
6085
pub 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+
/// ```
66107
pub 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
/// ```
83129
pub 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+
/// ```
91156
pub 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+
/// }
105186
pub 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+
/// ```
112208
pub 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
/// ```
129229
pub 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
/// ```
148255
pub 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

Comments
 (0)