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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use once_cell::sync::Lazy;
use std::cell::RefCell;
use std::fs::{File, OpenOptions};
use std::io::{BufReader, BufWriter, Read, Result, Write};
use std::path::Path;
use std::rc::Rc;
use std::sync::{Mutex, MutexGuard};

static SEQUENTIAL: Lazy<Mutex<()>> = Lazy::new(Mutex::default);

/// Allow tests with global variables to run without interfering with each other. The
/// lock is released when the MutexGuard variable goes out of scope.
///
/// # Example
/// ```
/// use common_testing::setup;
///
/// #[test]
/// fn test_1() {
///  let _lock = setup::sequential();
///  // test code
/// }
///
/// #[test]
/// fn test_2() {
///  let _lock = setup::sequential();
///  // test code
/// }
/// ```
///
/// # See Also
///
/// [std::sync::Mutex::lock](https://doc.rust-lang.org/std/sync/struct.Mutex.html#method.lock)
///
/// [std::sync::PoisonError](https://doc.rust-lang.org/std/sync/struct.PoisonError.html)
///
pub fn sequential<'a>() -> MutexGuard<'a, ()> {
  SEQUENTIAL.lock().unwrap()
}

/// Use to avoid random dependencies in test files for rare test cases.
///
/// # Example
///
/// ```
/// use common_testing::setup::get_rc_ref_cell_empty_vec;
///
/// #[test]
/// fn test_1() {
///  let vec = get_rc_ref_cell_empty_vec::<u8>();
///  // test code
/// }
/// ```
pub fn get_rc_ref_cell_empty_vec<T>() -> Rc<RefCell<std::vec::Vec<T>>> {
  Rc::new(RefCell::new(vec![]))
}

/// Get a read-only file handle. Use to avoid random dependencies in test
/// files for rare test cases.
pub fn get_read_only_file(path: &str) -> Result<File> {
  OpenOptions::new().read(true).open(path)
}

/// Get a reader for a file. Use to avoid random dependencies in test files
/// for rare test cases.
pub fn get_reader_for_file(path: &str) -> Result<BufReader<File>> {
  let file: File = get_read_only_file(path)?;
  Ok(BufReader::new(file))
}

/// Read the contents of a file into a vector of bytes.
///
/// # Example
///
/// ```
/// use common_testing::setup::get_file_contents;
///
/// #[test]
/// fn test_1() {
///  let contents = get_file_contents("test_file").unwrap();
/// }
/// ```
pub fn get_file_contents(path: &str) -> Result<Vec<u8>> {
  let mut buf = Vec::new();
  get_reader_for_file(path)?.read_to_end(&mut buf)?;
  Ok(buf.to_owned())
}

/// Get a read and write file handle. Use this to create temporary files for
/// testing and comparison.
pub fn get_read_and_write_file(path: &str) -> Result<File> {
  remove_file(path)?;
  let file = OpenOptions::new()
    .write(true)
    .create(true)
    .truncate(false)
    .read(true)
    .open(path)?;

  Ok(file)
}

/// Get a writer for a file, creating the file if it does not exist.
/// Use this to create temporary files for testing and comparison.
pub fn get_writer_for_file(path: &str) -> Result<BufWriter<File>> {
  let file: File = get_read_and_write_file(path)?;
  Ok(BufWriter::new(file))
}

/// Write bytes to a file, creating the file if it does not exist.
/// Use this to create temporary files for testing and comparison.
pub fn write_file_contents(path: &str, contents: &[u8]) -> Result<()> {
  let file: File = get_read_and_write_file(path)?;
  BufWriter::new(file).write_all(contents)
}

/// Create a directory path if it does not exist.
///
/// # Example
///
/// ```
/// use common_testing::setup::create_dir_all;
///
/// #[test]
/// fn test_1() {
///  create_dir_all("test_dir").unwrap();
/// }
/// ```
pub fn create_dir_all(path_dir: &str) -> Result<()> {
  if !Path::new(path_dir).is_dir() {
    std::fs::create_dir_all(path_dir)?;
  }
  Ok(())
}

/// Remove a file if it exists.
///
/// # Example
///
/// ```
/// use common_testing::setup::remove_file;
///
/// #[test]
/// fn test_1() {
///  remove_file("test_file").unwrap();
/// }
/// ```
pub fn remove_file(file_path: &str) -> Result<()> {
  if Path::new(file_path).is_file() {
    std::fs::remove_file(file_path)?
  }
  Ok(())
}