1use crate::{Benchmark, CacheClearableFilesystem, Filesystem, OperationDuration, OperationTimer};
6use async_trait::async_trait;
7use std::collections::VecDeque;
8use std::ffi::{CStr, CString};
9use std::mem::MaybeUninit;
10use std::ops::Range;
11use std::os::fd::RawFd;
12use std::os::unix::ffi::OsStringExt;
13use std::path::{Path, PathBuf};
14
15#[derive(Copy, Clone)]
17pub struct DirectoryTreeStructure {
18 pub files_per_directory: u64,
20
21 pub directories_per_directory: u64,
24
25 pub max_depth: u32,
27}
28
29impl DirectoryTreeStructure {
30 fn max_pending(&self) -> u64 {
33 self.directories_per_directory.pow(self.max_depth)
34 }
35
36 fn create_directory_tree(&self, root: PathBuf) {
38 struct DirInfo {
39 path: PathBuf,
40 depth: u32,
41 }
42 let mut pending = VecDeque::new();
43 pending.push_back(DirInfo { path: root, depth: 0 });
44 while let Some(dir) = pending.pop_front() {
45 for i in 0..self.files_per_directory {
46 let path = dir.path.join(file_name(i));
47 std::fs::File::create(path).unwrap();
48 }
49 if self.max_depth == dir.depth {
50 continue;
51 }
52 for i in 0..self.directories_per_directory {
53 let path = dir.path.join(dir_name(i));
54 std::fs::create_dir(&path).unwrap();
55 pending.push_back(DirInfo { path, depth: dir.depth + 1 });
56 }
57 }
58 }
59
60 fn enumerate_paths(&self) -> Vec<PathBuf> {
62 let mut paths = Vec::new();
63 self.enumerate_paths_impl(Path::new(""), &mut paths);
64 paths
65 }
66
67 fn enumerate_paths_impl(&self, base: &Path, paths: &mut Vec<PathBuf>) {
68 for i in 0..self.files_per_directory {
69 paths.push(base.join(file_name(i)));
70 }
71 if self.max_depth == 0 {
72 return;
73 }
74 for i in 0..self.directories_per_directory {
75 let path = base.join(dir_name(i));
76 paths.push(path.clone());
77 Self { max_depth: self.max_depth - 1, ..*self }.enumerate_paths_impl(&path, paths);
78 }
79 }
80}
81
82#[derive(Clone)]
85pub struct WalkDirectoryTreeCold {
86 dts: DirectoryTreeStructure,
87 iterations: u64,
88 read_only: bool,
89}
90
91impl WalkDirectoryTreeCold {
92 pub fn new(dts: DirectoryTreeStructure, iterations: u64) -> Self {
93 Self { dts, iterations, read_only: false }
94 }
95
96 pub fn new_read_only(dts: DirectoryTreeStructure, iterations: u64) -> Self {
97 Self { dts, iterations, read_only: true }
98 }
99}
100
101#[async_trait]
102impl<T: CacheClearableFilesystem> Benchmark<T> for WalkDirectoryTreeCold {
103 async fn run(&self, fs: &mut T) -> Vec<OperationDuration> {
104 storage_trace::duration!(
105 "benchmark",
106 "WalkDirectoryTreeCold",
107 "files_per_directory" => self.dts.files_per_directory,
108 "directories_per_directory" => self.dts.directories_per_directory,
109 "max_depth" => self.dts.max_depth
110 );
111 let root = fs.benchmark_dir().to_path_buf();
112
113 if !self.read_only {
114 self.dts.create_directory_tree(root.clone());
115 }
116 let max_pending = self.dts.max_pending() as usize;
117 let mut durations = Vec::new();
118 for i in 0..self.iterations {
119 fs.clear_cache().await;
120 storage_trace::duration!("benchmark", "WalkDirectoryTree", "iteration" => i);
121 let timer = OperationTimer::start();
122 walk_directory_tree(root.clone(), max_pending);
123 durations.push(timer.stop());
124 }
125 durations
126 }
127
128 fn name(&self) -> String {
129 format!(
130 "WalkDirectoryTreeCold/Files{}/Dirs{}/Depth{}",
131 self.dts.files_per_directory, self.dts.directories_per_directory, self.dts.max_depth
132 )
133 }
134}
135
136#[derive(Clone)]
139pub struct WalkDirectoryTreeWarm {
140 dts: DirectoryTreeStructure,
141 iterations: u64,
142 read_only: bool,
143}
144
145impl WalkDirectoryTreeWarm {
146 pub fn new(dts: DirectoryTreeStructure, iterations: u64) -> Self {
147 Self { dts, iterations, read_only: false }
148 }
149
150 pub fn new_read_only(dts: DirectoryTreeStructure, iterations: u64) -> Self {
151 Self { dts, iterations, read_only: true }
152 }
153}
154
155#[async_trait]
156impl<T: Filesystem> Benchmark<T> for WalkDirectoryTreeWarm {
157 async fn run(&self, fs: &mut T) -> Vec<OperationDuration> {
158 storage_trace::duration!(
159 "benchmark",
160 "WalkDirectoryTreeWarm",
161 "files_per_directory" => self.dts.files_per_directory,
162 "directories_per_directory" => self.dts.directories_per_directory,
163 "max_depth" => self.dts.max_depth
164 );
165 let root = fs.benchmark_dir().to_path_buf();
166
167 let max_pending = self.dts.max_pending() as usize;
168 if self.read_only {
169 walk_directory_tree(root.clone(), max_pending);
170 } else {
171 self.dts.create_directory_tree(root.clone());
172 }
173 let mut durations = Vec::new();
174 for i in 0..self.iterations {
175 storage_trace::duration!("benchmark", "WalkDirectoryTree", "iteration" => i);
176 let timer = OperationTimer::start();
177 walk_directory_tree(root.clone(), max_pending);
178 durations.push(timer.stop());
179 }
180 durations
181 }
182
183 fn name(&self) -> String {
184 format!(
185 "WalkDirectoryTreeWarm/Files{}/Dirs{}/Depth{}",
186 self.dts.files_per_directory, self.dts.directories_per_directory, self.dts.max_depth
187 )
188 }
189}
190
191fn walk_directory_tree(root: PathBuf, max_pending: usize) {
194 let mut pending = VecDeque::new();
195 pending.reserve(max_pending);
196 pending.push_back(root);
197 while let Some(dir) = pending.pop_front() {
198 storage_trace::duration!("benchmark", "read_dir");
199 for entry in std::fs::read_dir(&dir).unwrap() {
200 let entry = entry.unwrap();
201 if entry.file_type().unwrap().is_dir() {
202 pending.push_back(entry.path());
203 }
204 }
205 }
206}
207
208#[derive(Clone)]
211pub struct StatPath {
212 file_count: u64,
213 read_only: bool,
214}
215
216impl StatPath {
217 pub fn new() -> Self {
218 Self { file_count: 100, read_only: false }
219 }
220
221 pub fn new_read_only() -> Self {
222 Self { file_count: 100, read_only: true }
223 }
224}
225
226#[async_trait]
227impl<T: Filesystem> Benchmark<T> for StatPath {
228 async fn run(&self, fs: &mut T) -> Vec<OperationDuration> {
229 storage_trace::duration!("benchmark", "StatPath");
230
231 let root = fs.benchmark_dir().to_path_buf();
232 if !self.read_only {
233 for i in 0..self.file_count {
234 std::fs::File::create(root.join(file_name(i))).unwrap();
235 }
236 }
237
238 let root_fd =
239 open_path(&path_buf_to_c_string(root), libc::O_DIRECTORY | libc::O_RDONLY).unwrap();
240
241 let mut durations = Vec::with_capacity(self.file_count as usize);
242 for i in 0..self.file_count {
243 let path = path_buf_to_c_string(file_name(i));
244 storage_trace::duration!("benchmark", "stat", "file" => i);
245 let timer = OperationTimer::start();
246 stat_path_at(&root_fd, &path).unwrap();
247 durations.push(timer.stop());
248 }
249 durations
250 }
251
252 fn name(&self) -> String {
253 "StatPath".to_string()
254 }
255}
256
257#[derive(Clone)]
260pub struct OpenFile {
261 file_count: u64,
262 read_only: bool,
263}
264
265impl OpenFile {
266 pub fn new() -> Self {
267 Self { file_count: 100, read_only: false }
268 }
269
270 pub fn new_read_only() -> Self {
271 Self { file_count: 100, read_only: true }
272 }
273}
274
275#[async_trait]
276impl<T: Filesystem> Benchmark<T> for OpenFile {
277 async fn run(&self, fs: &mut T) -> Vec<OperationDuration> {
278 storage_trace::duration!("benchmark", "OpenFile");
279
280 let root = fs.benchmark_dir().to_path_buf();
281 let open_flags = if self.read_only {
282 libc::O_RDONLY
283 } else {
284 for i in 0..self.file_count {
285 std::fs::File::create(root.join(file_name(i))).unwrap();
286 }
287 libc::O_RDWR
288 };
289
290 let root_fd =
291 open_path(&path_buf_to_c_string(root), libc::O_DIRECTORY | libc::O_RDONLY).unwrap();
292
293 let mut durations = Vec::with_capacity(self.file_count as usize);
294 for i in 0..self.file_count {
295 let path = path_buf_to_c_string(file_name(i));
296 let _file = {
298 storage_trace::duration!("benchmark", "open", "file" => i);
299 let timer = OperationTimer::start();
300 let file = open_path_at(&root_fd, &path, open_flags);
301 durations.push(timer.stop());
302 file.unwrap()
303 };
304 }
305 durations
306 }
307
308 fn name(&self) -> String {
309 "OpenFile".to_string()
310 }
311}
312
313#[derive(Clone)]
315pub struct CreateFile {
316 file_count: u64,
317}
318
319impl CreateFile {
320 pub fn new() -> Self {
321 Self { file_count: 100 }
322 }
323}
324
325#[async_trait]
326impl<T: Filesystem> Benchmark<T> for CreateFile {
327 async fn run(&self, fs: &mut T) -> Vec<OperationDuration> {
328 storage_trace::duration!("benchmark", "CreateFile");
329
330 let root = fs.benchmark_dir().to_path_buf();
331 let root_fd =
332 open_path(&path_buf_to_c_string(root), libc::O_DIRECTORY | libc::O_RDONLY).unwrap();
333
334 let mut durations = Vec::with_capacity(self.file_count as usize);
335 for i in 0..self.file_count {
336 let path = path_buf_to_c_string(file_name(i));
337 let _file = {
339 storage_trace::duration!("benchmark", "create", "file" => i);
340 let timer = OperationTimer::start();
341 let file = open_path_at(&root_fd, &path, libc::O_CREAT | libc::O_RDWR);
342 durations.push(timer.stop());
343 file
344 };
345 }
346 durations
347 }
348
349 fn name(&self) -> String {
350 "CreateFile".to_string()
351 }
352}
353
354#[derive(Clone)]
356pub struct UnlinkFile {
357 file_count: u64,
358}
359
360impl UnlinkFile {
361 pub fn new() -> Self {
362 Self { file_count: 100 }
363 }
364}
365
366#[async_trait]
367impl<T: Filesystem> Benchmark<T> for UnlinkFile {
368 async fn run(&self, fs: &mut T) -> Vec<OperationDuration> {
369 storage_trace::duration!("benchmark", "UnlinkFile");
370
371 let root = fs.benchmark_dir().to_path_buf();
372 let root_fd =
373 open_path(&path_buf_to_c_string(root), libc::O_DIRECTORY | libc::O_RDONLY).unwrap();
374
375 for i in 0..self.file_count {
376 let path = path_buf_to_c_string(file_name(i));
377 let _file = open_path_at(&root_fd, &path, libc::O_CREAT | libc::O_RDWR).unwrap();
378 }
379
380 let mut durations = Vec::with_capacity(self.file_count as usize);
381 for i in 0..self.file_count {
382 let path = path_buf_to_c_string(file_name(i));
383 storage_trace::duration!("benchmark", "unlink", "file" => i);
384 let timer = OperationTimer::start();
385 unlink_path_at(&root_fd, &path).unwrap();
386 durations.push(timer.stop());
387 }
388 durations
389 }
390
391 fn name(&self) -> String {
392 "UnlinkFile".to_string()
393 }
394}
395
396#[derive(Clone)]
399pub struct OpenDeeplyNestedFile {
400 file_count: u64,
401 depth: u64,
402 read_only: bool,
403}
404
405impl OpenDeeplyNestedFile {
406 pub fn new() -> Self {
407 Self { file_count: 100, depth: 5, read_only: false }
408 }
409
410 pub fn new_read_only() -> Self {
411 Self { file_count: 100, depth: 5, read_only: true }
412 }
413
414 fn dir_path(&self, n: u64) -> PathBuf {
415 let mut path = PathBuf::new();
416 for i in 0..self.depth {
417 path = path.join(format!("dir-{:03}-{:03}", n, i));
418 }
419 path
420 }
421}
422
423#[async_trait]
424impl<T: Filesystem> Benchmark<T> for OpenDeeplyNestedFile {
425 async fn run(&self, fs: &mut T) -> Vec<OperationDuration> {
426 storage_trace::duration!("benchmark", "OpenDeeplyNestedFile");
427
428 let root = fs.benchmark_dir().to_path_buf();
429 let open_flags = if self.read_only {
430 libc::O_RDONLY
431 } else {
432 for i in 0..self.file_count {
433 let dir_path = root.join(self.dir_path(i));
434 std::fs::create_dir_all(&dir_path).unwrap();
435 std::fs::File::create(dir_path.join(file_name(i))).unwrap();
436 }
437 libc::O_RDWR
438 };
439
440 let root_fd =
441 open_path(&path_buf_to_c_string(root), libc::O_DIRECTORY | libc::O_RDONLY).unwrap();
442
443 let mut durations = Vec::with_capacity(self.file_count as usize);
444 for i in 0..self.file_count {
445 let path = path_buf_to_c_string(self.dir_path(i).join(file_name(i)));
446 let _file = {
448 storage_trace::duration!("benchmark", "open", "file" => i);
449 let timer = OperationTimer::start();
450 let file = open_path_at(&root_fd, &path, open_flags);
451 durations.push(timer.stop());
452 file.unwrap()
453 };
454 }
455 durations
456 }
457
458 fn name(&self) -> String {
459 "OpenDeeplyNestedFile".to_string()
460 }
461}
462
463#[derive(Clone)]
465pub struct GitStatus {
466 dts: DirectoryTreeStructure,
467 iterations: u64,
468
469 stat_threads: usize,
471 read_only: bool,
472}
473
474impl GitStatus {
475 pub fn new() -> Self {
476 let dts = DirectoryTreeStructure {
479 files_per_directory: 4,
480 directories_per_directory: 2,
481 max_depth: 7,
482 };
483
484 Self { dts, iterations: 5, stat_threads: 1, read_only: false }
487 }
488
489 pub fn new_read_only() -> Self {
490 Self { read_only: true, ..Self::new() }
491 }
492
493 fn stat_paths(&self, root: &OpenFd, paths: &Vec<CString>) {
496 storage_trace::duration!("benchmark", "GitStatus::stat_paths");
497 std::thread::scope(|scope| {
498 for thread in 0..self.stat_threads {
499 scope.spawn(move || {
500 let paths = &paths;
501 for path in batch_range(paths.len(), self.stat_threads, thread) {
502 storage_trace::duration!("benchmark", "stat");
503 stat_path_at(root, &paths[path]).unwrap();
504 }
505 });
506 }
507 });
508 }
509
510 fn walk_repo(&self, dir: &Path) {
512 storage_trace::duration!("benchmark", "GitStatus::walk_repo");
513 for entry in std::fs::read_dir(&dir).unwrap() {
514 let entry = entry.unwrap();
515 if entry.file_type().unwrap().is_dir() {
516 self.walk_repo(&entry.path());
517 }
518 }
519 }
520}
521
522#[async_trait]
523impl<T: Filesystem> Benchmark<T> for GitStatus {
524 async fn run(&self, fs: &mut T) -> Vec<OperationDuration> {
525 let root = &fs.benchmark_dir().to_path_buf();
526
527 if !self.read_only {
528 self.dts.create_directory_tree(root.clone());
529 }
530 let paths = self.dts.enumerate_paths().into_iter().map(path_buf_to_c_string).collect();
531
532 let root_fd =
533 open_path(&path_buf_to_c_string(root.clone()), libc::O_DIRECTORY | libc::O_RDONLY)
534 .unwrap();
535
536 let mut durations = Vec::new();
537 for i in 0..self.iterations {
538 storage_trace::duration!("benchmark", "GitStatus", "iteration" => i);
539 let timer = OperationTimer::start();
540 self.stat_paths(&root_fd, &paths);
541 self.walk_repo(&root);
542 durations.push(timer.stop());
543 }
544 durations
545 }
546
547 fn name(&self) -> String {
548 "GitStatus".to_owned()
549 }
550}
551
552pub fn file_name(n: u64) -> PathBuf {
553 format!("file-{:03}.txt", n).into()
554}
555
556pub fn dir_name(n: u64) -> PathBuf {
557 format!("dir-{:03}", n).into()
558}
559
560pub fn path_buf_to_c_string(path: PathBuf) -> CString {
561 CString::new(path.into_os_string().into_vec()).unwrap()
562}
563
564pub fn stat_path_at(dir: &OpenFd, path: &CStr) -> Result<libc::stat, std::io::Error> {
565 unsafe {
566 let mut stat = MaybeUninit::uninit();
567 let result =
570 libc::fstatat(dir.0, path.as_ptr(), stat.as_mut_ptr(), libc::AT_SYMLINK_NOFOLLOW);
571
572 if result == 0 { Ok(stat.assume_init()) } else { Err(std::io::Error::last_os_error()) }
573 }
574}
575
576fn batch_range(item_count: usize, batch_count: usize, batch_num: usize) -> Range<usize> {
579 let items_per_batch = (item_count + (batch_count - 1)) / batch_count;
580 let start = items_per_batch * batch_num;
581 let end = std::cmp::min(item_count, start + items_per_batch);
582 start..end
583}
584
585pub struct OpenFd(RawFd);
586
587impl Drop for OpenFd {
588 fn drop(&mut self) {
589 unsafe { libc::close(self.0) };
590 }
591}
592
593pub fn open_path(path: &CStr, flags: libc::c_int) -> Result<OpenFd, std::io::Error> {
594 let result = unsafe { libc::open(path.as_ptr(), flags) };
595 if result >= 0 { Ok(OpenFd(result)) } else { Err(std::io::Error::last_os_error()) }
596}
597
598pub fn open_path_at(
599 dir: &OpenFd,
600 path: &CStr,
601 flags: libc::c_int,
602) -> Result<OpenFd, std::io::Error> {
603 let result = unsafe { libc::openat(dir.0, path.as_ptr(), flags) };
604 if result >= 0 { Ok(OpenFd(result)) } else { Err(std::io::Error::last_os_error()) }
605}
606
607pub fn unlink_path_at(dir: &OpenFd, path: &CStr) -> Result<(), std::io::Error> {
608 let result = unsafe { libc::unlinkat(dir.0, path.as_ptr(), 0) };
609 if result == 0 { Ok(()) } else { Err(std::io::Error::last_os_error()) }
610}
611
612#[cfg(test)]
613mod tests {
614 use super::*;
615 use crate::testing::TestFilesystem;
616
617 const ITERATION_COUNT: u64 = 3;
618
619 #[derive(PartialEq, Debug)]
620 struct DirectoryTree {
621 files: u64,
622 directories: Vec<DirectoryTree>,
623 }
624
625 fn read_in_directory_tree(root: PathBuf) -> DirectoryTree {
626 let mut tree = DirectoryTree { files: 0, directories: vec![] };
627 for entry in std::fs::read_dir(root).unwrap() {
628 let entry = entry.unwrap();
629 if entry.file_type().unwrap().is_dir() {
630 tree.directories.push(read_in_directory_tree(entry.path()));
631 } else {
632 tree.files += 1;
633 }
634 }
635 tree
636 }
637
638 #[fuchsia::test]
639 async fn create_directory_tree_with_depth_of_zero_test() {
640 let test_fs = Box::new(TestFilesystem::new());
641 let dts = DirectoryTreeStructure {
642 files_per_directory: 3,
643 directories_per_directory: 2,
644 max_depth: 0,
645 };
646 let root = test_fs.benchmark_dir().to_owned();
647 dts.create_directory_tree(root.clone());
648
649 let directory_tree = read_in_directory_tree(root);
650 assert_eq!(directory_tree, DirectoryTree { files: 3, directories: vec![] });
651 test_fs.shutdown().await;
652 }
653
654 #[fuchsia::test]
655 async fn create_directory_tree_with_depth_of_two_test() {
656 let test_fs = Box::new(TestFilesystem::new());
657 let dts = DirectoryTreeStructure {
658 files_per_directory: 4,
659 directories_per_directory: 2,
660 max_depth: 2,
661 };
662 let root = test_fs.benchmark_dir().to_owned();
663 dts.create_directory_tree(root.clone());
664
665 let directory_tree = read_in_directory_tree(root);
666 assert_eq!(
667 directory_tree,
668 DirectoryTree {
669 files: 4,
670 directories: vec![
671 DirectoryTree {
672 files: 4,
673 directories: vec![
674 DirectoryTree { files: 4, directories: vec![] },
675 DirectoryTree { files: 4, directories: vec![] }
676 ]
677 },
678 DirectoryTree {
679 files: 4,
680 directories: vec![
681 DirectoryTree { files: 4, directories: vec![] },
682 DirectoryTree { files: 4, directories: vec![] }
683 ]
684 }
685 ]
686 }
687 );
688 test_fs.shutdown().await;
689 }
690
691 #[fuchsia::test]
692 fn enumerate_paths_test() {
693 let dts = DirectoryTreeStructure {
694 files_per_directory: 2,
695 directories_per_directory: 2,
696 max_depth: 0,
697 };
698 let paths = dts.enumerate_paths();
699 assert_eq!(paths, vec![PathBuf::from("file-000.txt"), PathBuf::from("file-001.txt")],);
700
701 let dts = DirectoryTreeStructure {
702 files_per_directory: 2,
703 directories_per_directory: 2,
704 max_depth: 1,
705 };
706 let paths = dts.enumerate_paths();
707 assert_eq!(
708 paths,
709 vec![
710 PathBuf::from("file-000.txt"),
711 PathBuf::from("file-001.txt"),
712 PathBuf::from("dir-000"),
713 PathBuf::from("dir-000/file-000.txt"),
714 PathBuf::from("dir-000/file-001.txt"),
715 PathBuf::from("dir-001"),
716 PathBuf::from("dir-001/file-000.txt"),
717 PathBuf::from("dir-001/file-001.txt"),
718 ],
719 );
720 }
721
722 #[fuchsia::test]
723 fn batch_range_test() {
724 assert_eq!(batch_range(10, 1, 0), 0..10);
725
726 assert_eq!(batch_range(10, 3, 0), 0..4);
727 assert_eq!(batch_range(10, 3, 1), 4..8);
728 assert_eq!(batch_range(10, 3, 2), 8..10);
729
730 assert_eq!(batch_range(10, 4, 3), 9..10);
731 assert_eq!(batch_range(12, 4, 3), 9..12);
732 }
733
734 #[fuchsia::test]
735 async fn walk_directory_tree_cold_test() {
736 let mut test_fs = Box::new(TestFilesystem::new());
737 let dts = DirectoryTreeStructure {
738 files_per_directory: 2,
739 directories_per_directory: 2,
740 max_depth: 2,
741 };
742 let results = WalkDirectoryTreeCold::new(dts, ITERATION_COUNT).run(test_fs.as_mut()).await;
743
744 assert_eq!(results.len(), ITERATION_COUNT as usize);
745 assert_eq!(test_fs.clear_cache_count().await, ITERATION_COUNT);
746 test_fs.shutdown().await;
747 }
748
749 #[fuchsia::test]
750 async fn walk_directory_tree_warm_test() {
751 let mut test_fs = Box::new(TestFilesystem::new());
752 let dts = DirectoryTreeStructure {
753 files_per_directory: 2,
754 directories_per_directory: 2,
755 max_depth: 2,
756 };
757 let results = WalkDirectoryTreeWarm::new(dts, ITERATION_COUNT).run(test_fs.as_mut()).await;
758
759 assert_eq!(results.len(), ITERATION_COUNT as usize);
760 assert_eq!(test_fs.clear_cache_count().await, 0);
761 test_fs.shutdown().await;
762 }
763
764 #[fuchsia::test]
765 async fn stat_path_test() {
766 let mut test_fs = Box::new(TestFilesystem::new());
767 let benchmark = StatPath { file_count: 5, read_only: false };
768 let results = benchmark.run(test_fs.as_mut()).await;
769 assert_eq!(results.len(), 5);
770 assert_eq!(test_fs.clear_cache_count().await, 0);
771 test_fs.shutdown().await;
772 }
773
774 #[fuchsia::test]
775 async fn open_file_test() {
776 let mut test_fs = Box::new(TestFilesystem::new());
777 let benchmark = OpenFile { file_count: 5, read_only: false };
778 let results = benchmark.run(test_fs.as_mut()).await;
779 assert_eq!(results.len(), 5);
780 assert_eq!(test_fs.clear_cache_count().await, 0);
781 test_fs.shutdown().await;
782 }
783
784 #[fuchsia::test]
785 async fn create_file_test() {
786 let mut test_fs = Box::new(TestFilesystem::new());
787 let benchmark = CreateFile { file_count: 5 };
788 let results = benchmark.run(test_fs.as_mut()).await;
789 assert_eq!(results.len(), 5);
790 assert_eq!(test_fs.clear_cache_count().await, 0);
791 test_fs.shutdown().await;
792 }
793
794 #[fuchsia::test]
795 async fn unlink_file_test() {
796 let mut test_fs = Box::new(TestFilesystem::new());
797 let benchmark = UnlinkFile { file_count: 5 };
798 let results = benchmark.run(test_fs.as_mut()).await;
799 assert_eq!(results.len(), 5);
800 assert_eq!(test_fs.clear_cache_count().await, 0);
801 test_fs.shutdown().await;
802 }
803
804 #[fuchsia::test]
805 async fn open_deeply_nested_file_test() {
806 let mut test_fs = Box::new(TestFilesystem::new());
807 let benchmark = OpenDeeplyNestedFile { file_count: 5, depth: 3, read_only: false };
808 let results = benchmark.run(test_fs.as_mut()).await;
809 assert_eq!(results.len(), 5);
810 assert_eq!(test_fs.clear_cache_count().await, 0);
811 test_fs.shutdown().await;
812 }
813
814 #[fuchsia::test]
815 async fn git_status_test() {
816 let mut test_fs = Box::new(TestFilesystem::new());
817 let dts = DirectoryTreeStructure {
818 files_per_directory: 2,
819 directories_per_directory: 2,
820 max_depth: 2,
821 };
822 let benchmark =
823 GitStatus { dts, iterations: ITERATION_COUNT, stat_threads: 1, read_only: false };
824 let results = benchmark.run(test_fs.as_mut()).await;
825
826 assert_eq!(results.len(), ITERATION_COUNT as usize);
827 assert_eq!(test_fs.clear_cache_count().await, 0);
828 test_fs.shutdown().await;
829 }
830}