1 use crate::{Error, ErrorCode, FileState, OpenMode, Port, Session};
2 use test::assert_eq;
3
4 /// Tests that connecting to the service works.
5 #[test]
connect()6 fn connect() {
7 let session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
8 session.close();
9 }
10
11 /// Tests successfully connecting to a port when not waiting for the port to be
12 /// available.
13 #[test]
connect_no_wait()14 fn connect_no_wait() {
15 let session = Session::new(Port::TamperDetectEarlyAccess, false).unwrap();
16 session.close();
17 }
18
19 /// Tests that `Session::new` will return an error instead of blocking when
20 /// trying to connect to a port that doesn't exist if not waiting on the port to
21 /// be available.
22 #[test]
connect_fail_no_wait()23 fn connect_fail_no_wait() {
24 let result = Session::new(Port::TestPortNonExistent, false);
25 assert_eq!(Err(Error::Code(ErrorCode::NotFound)), result);
26 }
27
28 /// Tests that reading/writing to a file with the basic file access APIs works.
29 #[test]
read_write()30 fn read_write() {
31 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
32
33 let file_name = "read_write.txt";
34 let file_contents = "Hello, world!";
35
36 // Write the initial contents of the file.
37 session.write(file_name, file_contents.as_bytes()).unwrap();
38
39 // Read the contents of the file.
40 let data = &mut [0; 32];
41 let data = session.read(file_name, data).unwrap();
42
43 // Verify that the contents are as expected.
44 assert_eq!(data.len(), file_contents.len(), "Incorrect number of bytes read");
45 assert_eq!(data, file_contents.as_bytes(), "Incorrect file contents returned");
46 }
47
48 /// Tests that trying to read a full file with a buffer that's too small returns
49 /// an error.
50 #[test]
read_all_buf_too_small()51 fn read_all_buf_too_small() {
52 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
53
54 let file_name = "read_all_buf_too_small.txt";
55 let file_contents = "Hello, world!";
56
57 // Write the initial contents of the file.
58 session.write(file_name, file_contents.as_bytes()).unwrap();
59
60 // Try to read the contents of the file with a buffer that is not large enough
61 // to hold the full file.
62 let data = &mut [0; 5];
63 let result = session.read(file_name, data);
64
65 assert_eq!(Err(Error::Code(ErrorCode::NotEnoughBuffer)), result);
66 }
67
68 /// Tests `read_at`, verifying that it can partially read a file at different
69 /// offsets, and that it does not read past the end of the file.
70 #[test]
read_at()71 fn read_at() {
72 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
73
74 let file_name = "read_at.txt";
75 let file_contents = b"Hello, world!";
76
77 // Write the initial contents of the file.
78 session.write(file_name, file_contents).unwrap();
79
80 let mut file = session.open_file(file_name, OpenMode::Open).unwrap();
81
82 // Read 5 bytes of the file starting at the 4th byte.
83 let data = &mut [0; 5];
84 let result = session.read_at(&mut file, 3, data);
85
86 // Verify that the correct chunk of the file was read.
87 assert_eq!(Ok(&file_contents[3..8]), result);
88
89 // Read past the end of the file to verify that the buffer is only partially
90 // filled.
91 data.fill(0);
92 let result = session.read_at(&mut file, 10, data);
93
94 // Verify that the remaining portion of the file is returned.
95 assert_eq!(Ok(&file_contents[10..]), result);
96
97 // Verify that only a prefix of the buffer was overwritten.
98 let expected = &[file_contents[10], file_contents[11], file_contents[12], 0, 0][..];
99 assert_eq!(expected, data, "Data buffer was not overwritten in expected way");
100 }
101
102 #[test]
write_at()103 fn write_at() {
104 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
105
106 let file_name = "write_at.txt";
107 let file_contents = "Hello, world!";
108
109 // Write the initial contents of the file.
110 let mut file = session.open_file(file_name, OpenMode::Create).unwrap();
111 session.write_all(&mut file, file_contents.as_bytes()).unwrap();
112
113 // Overwrite a portion of the file.
114 let result = session.write_at(&mut file, 7, b"Trusty");
115 assert_eq!(Ok(()), result);
116
117 // Verify that the contents are as expected.
118 let data = &mut [0; 32];
119 let result = session.read_all(&file, data);
120 let expected = b"Hello, Trusty".as_slice();
121 assert_eq!(Ok(expected), result, "Incorrect bytes read");
122
123 // Use `write_at` in a transaction.
124 let mut transaction = session.begin_transaction();
125 assert_eq!(Ok(()), transaction.write_at(&mut file, 7, b"C"));
126 assert_eq!(Ok(()), transaction.commit());
127
128 let result = session.read_all(&file, data);
129 let expected = b"Hello, Crusty".as_slice();
130 assert_eq!(Ok(expected), result);
131
132 // Verify that attempting to write past the end of the file succeeds and expands
133 // the file to fit the new data.
134 let result = session.write_at(&mut file, 7, b"too long data");
135 assert_eq!(Ok(()), result, "Writing past end of file failed");
136
137 let result = session.read_all(&file, data);
138 let expected = b"Hello, too long data".as_slice();
139 assert_eq!(Ok(expected), result);
140 }
141
142 /// Tests that file sizes are reported correctly, and that setting file size
143 /// works both when increasing and decreasing a file's size.
144 #[test]
get_set_size()145 fn get_set_size() {
146 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
147
148 let file_name = "get_set_size.txt";
149 let initial_contents = "Hello, world!";
150 let mut buf = [0; 32];
151
152 // Create the file and set its initial contents.
153 let mut file = session.open_file(file_name, OpenMode::Create).unwrap();
154 session.write_all(&mut file, initial_contents.as_bytes()).unwrap();
155
156 // Verify that the reported size is correct after writing to the file.
157 let size = session.get_size(&file).unwrap();
158 assert_eq!(initial_contents.len(), size, "File has incorrect size after initial creation");
159
160 // Decrease the file's size and verify that the contents are truncated as
161 // expected.
162 session.set_size(&mut file, 5).unwrap();
163 let contents = session.read_all(&file, buf.as_mut_slice()).unwrap();
164 assert_eq!("Hello".as_bytes(), contents, "File has incorrect contents after truncating");
165
166 // Increase the file's size and verify that the contents are 0-extended as
167 // expected.
168 session.set_size(&mut file, 10).unwrap();
169 let contents = session.read_all(&file, buf.as_mut_slice()).unwrap();
170 assert_eq!(
171 "Hello\0\0\0\0\0".as_bytes(),
172 contents,
173 "File has incorrect contents after extending",
174 );
175 }
176
177 /// Tests that files can be renamed and deleted.
178 #[test]
rename_delete()179 fn rename_delete() {
180 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
181
182 let before_name = "before.txt";
183 let after_name = "after.txt";
184 let file_contents = "Hello, world!";
185 let mut buf = [0; 32];
186
187 // Verify that neither of the test files exist before the test runs.
188 session.open_file(before_name, OpenMode::Open).unwrap_err();
189 session.open_file(after_name, OpenMode::Open).unwrap_err();
190
191 // Create the initial file and then rename it.
192 session.write(before_name, file_contents.as_bytes()).unwrap();
193 session.rename(before_name, after_name).unwrap();
194
195 // Verify that the file no longer exists at the original name, and that the new
196 // file has the same contents as the original file.
197 session.open_file(before_name, OpenMode::Open).unwrap_err();
198 let contents = session.read(after_name, buf.as_mut_slice()).unwrap();
199 assert_eq!(file_contents.as_bytes(), contents, "File has incorrect contents after renaming");
200
201 // Delete the file and then verify it no longer exists
202 session.remove(after_name).unwrap();
203 session.open_file(after_name, OpenMode::Open).unwrap_err();
204 }
205
206 /// Tests that a file that is open as a handle cannot be renamed.
207 #[test]
cannot_rename_open_file()208 fn cannot_rename_open_file() {
209 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
210
211 // Clear any leftover files from a previous test run.
212 remove_all(&mut session);
213
214 let file_name = "cannot_rename_or_delete_open_file.txt";
215
216 // Create the file and open a handle for it.
217 let file = session.open_file(file_name, OpenMode::CreateExclusive).unwrap();
218
219 // Verify that renaming the file fails while the handle is open.
220 assert_eq!(
221 Err(Error::Code(ErrorCode::NotAllowed)),
222 session.rename(file_name, "different_file.txt"),
223 "Unexpected result when renaming open file",
224 );
225
226 // Verify that the file can be renamed once the handle is closed.
227 file.close();
228 session.rename(file_name, "different_file.txt").unwrap();
229 }
230
231 /// Tests that multiple files can be modified in a single transaction, and that
232 /// file handles opened as part of a transaction can still be used after the
233 /// transaction is committed.
234 #[test]
multiple_files_in_transaction()235 fn multiple_files_in_transaction() {
236 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
237
238 let file_a = "file_a.txt";
239 let file_b = "file_b.txt";
240 let file_contents = "multiple_files_in_transaction";
241 let mut buf = [0; 32];
242
243 // Clear any leftover files from a previous test run.
244 remove_all(&mut session);
245
246 // Start a transaction, create two files, and then write the contents of those
247 // files before committing the transaction.
248
249 let mut transaction = session.begin_transaction();
250
251 let mut file_a = transaction.open_file(file_a, OpenMode::CreateExclusive).unwrap();
252 let mut file_b = transaction.open_file(file_b, OpenMode::CreateExclusive).unwrap();
253
254 transaction.write_all(&mut file_a, file_contents.as_bytes()).unwrap();
255 transaction.write_all(&mut file_b, file_contents.as_bytes()).unwrap();
256
257 transaction.commit().unwrap();
258
259 // Verify that we can observe the updated file contents. Note that we reuse the
260 // existing file handles to verify that file handles opened in a transaction
261 // remain valid after the transaction is committed.
262
263 let actual_contents = session.read_all(&file_a, &mut buf).unwrap();
264 assert_eq!(
265 file_contents.as_bytes(),
266 actual_contents,
267 "Changes from transaction were not written",
268 );
269
270 let actual_contents = session.read_all(&file_b, &mut buf).unwrap();
271 assert_eq!(
272 file_contents.as_bytes(),
273 actual_contents,
274 "Changes from transaction were not written",
275 );
276 }
277
278 /// Tests that file contents can be read while using a `Transaction`.
279 #[test]
read_in_transaction()280 fn read_in_transaction() {
281 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
282
283 let file_name = "read_in_transaction.txt";
284 let file_contents = "Hello, world!";
285
286 // Write the initial contents of the file.
287 let mut file = session.open_file(file_name, OpenMode::Create).unwrap();
288 session.write_all(&mut file, file_contents.as_bytes()).unwrap();
289
290 // Overwrite a portion of the file in a transaction.
291 let mut transaction = session.begin_transaction();
292 let result = transaction.write_at(&mut file, 7, b"Trusty");
293 assert_eq!(Ok(()), result);
294
295 // Verify that the contents are as expected.
296 let data = &mut [0; 32];
297 let result = transaction.read_all(&file, data);
298 let expected = b"Hello, Trusty".as_slice();
299 assert_eq!(Ok(expected), result, "Incorrect bytes read");
300
301 // Change the contents of the file then verify that we can get the updated length.
302 let long_contents = b"Now for a much much longer file";
303 assert_eq!(Ok(()), transaction.write_all(&mut file, long_contents));
304 assert_eq!(Ok(long_contents.len()), transaction.get_size(&file));
305
306 transaction.discard().unwrap();
307 }
308
309 /// Tests that pending changes in a transaction are not committed if the
310 /// transaction is discarded.
311 #[test]
discard_transaction()312 fn discard_transaction() {
313 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
314
315 let file_name = "discard_transaction.txt";
316 let file_contents = "discard_transaction";
317 let mut buf = [0; 32];
318
319 // Clear any leftover files from a previous test run.
320 remove_all(&mut session);
321
322 // Begin to make changes in a transaction, then discard the transaction without
323 // committing the pending changes.
324 {
325 let mut transaction = session.begin_transaction();
326
327 let mut file = transaction.open_file(file_name, OpenMode::CreateExclusive).unwrap();
328 transaction.write_all(&mut file, file_contents.as_bytes()).unwrap();
329
330 transaction.discard().unwrap();
331 }
332
333 // Verify that the file was never created or written to.
334 assert_eq!(
335 Err(Error::Code(ErrorCode::NotFound)),
336 session.read(file_name, &mut buf),
337 "Unexpected result when renaming open file",
338 );
339 }
340
341 /// Tests that pending changes in a transaction are not committed if the
342 /// transaction is discarded.
343 #[test]
drop_transaction()344 fn drop_transaction() {
345 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
346
347 let file_name = "discard_transaction_on_drop.txt";
348 let file_contents = "discard_transaction_on_drop";
349 let mut buf = [0; 32];
350
351 // Clear any leftover files from a previous test run.
352 remove_all(&mut session);
353
354 // Begin to make changes in a transaction, then drop it without explicitly
355 // committing or discarding it. This should discard any pending changes.
356 {
357 let mut transaction = session.begin_transaction();
358
359 let mut file = transaction.open_file(file_name, OpenMode::CreateExclusive).unwrap();
360 transaction.write_all(&mut file, file_contents.as_bytes()).unwrap();
361 }
362
363 // Verify that the file was never created or written to.
364 assert_eq!(
365 Err(Error::Code(ErrorCode::NotFound)),
366 session.read(file_name, &mut buf),
367 "Unexpected result when renaming open file",
368 );
369 }
370
371 /// Tests directory enumeration in an empty directory.
372 #[test]
list_empty_dir()373 fn list_empty_dir() {
374 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
375
376 // Reset the storage dir so that we can test listing files in an empty dir.
377 remove_all(&mut session);
378
379 // Verify that listing files in an empty dir yields no elements.
380 for entry in session.list_files().unwrap() {
381 let entry = entry.unwrap();
382 panic!("Unexpected file: {:?}", entry);
383 }
384 }
385
386 /// Tests that directory enumeration correctly returns errors if the session is
387 /// closed while directory enumeration is still in progress.
388 #[test]
drop_session_while_listing()389 fn drop_session_while_listing() {
390 // Open a session, start listing files, and then drop the `Session` object
391 // while keeping the `DirIter` object alive.
392 let mut dir_iter = {
393 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
394 session.list_files().unwrap()
395 };
396
397 // Verify that attempting to use the iterator after the session is closed
398 // generates an error and then only yields `None`.
399 assert_eq!(Some(Err(Error::Code(ErrorCode::NotFound))), dir_iter.next());
400 assert_eq!(None, dir_iter.next());
401 assert_eq!(None, dir_iter.next());
402 }
403
404 /// Verifies that directory enumeration lists files correctly, and that that
405 /// states of files are reported correctly.
406 #[test]
list_during_transaction()407 fn list_during_transaction() {
408 let mut session = Session::new(Port::TamperDetectEarlyAccess, true).unwrap();
409
410 // Clear any leftover files from a previous test run.
411 remove_all(&mut session);
412
413 let file_a = "file_a.txt";
414 let file_b = "file_b.txt";
415 let file_contents = "Hello, world!";
416
417 // Create a file and write to it such that it is fully committed.
418 session.write(file_a, file_contents.as_bytes()).unwrap();
419
420 let mut transaction = session.begin_transaction();
421
422 // Create a file as part of `transaction` that will be in the "added" state.
423 let mut file = transaction.open_file(file_b, OpenMode::CreateExclusive).unwrap();
424 transaction.write_all(&mut file, file_contents.as_bytes()).unwrap();
425
426 // Verify that listing files while a transaction is active correctly reports all
427 // expected files and in the correct states.
428 let mut dir_iter = transaction.list_files().unwrap();
429 assert_eq!(Some(Ok((file_a.into(), FileState::Committed))), dir_iter.next());
430 assert_eq!(Some(Ok((file_b.into(), FileState::Added))), dir_iter.next());
431 assert_eq!(None, dir_iter.next());
432
433 transaction.discard().unwrap();
434 }
435
436 /// Removes all files in the storage for `session`.
437 ///
438 /// Useful for resetting the state of the storage for tests that need to iterate
439 /// the contents of storage and so need things to be in a known starting state.
remove_all(session: &mut Session)440 fn remove_all(session: &mut Session) {
441 for entry in session.list_files().unwrap() {
442 let (name, _state) = entry.unwrap();
443 session.remove(&name).unwrap();
444 }
445 }
446
447 test::init!();
448