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