1#![cfg(not(target_os = "redox"))]
2
3use std::collections::HashMap;
4use std::fs::File;
5#[cfg(any(target_os = "android", target_os = "linux"))]
6use std::mem::MaybeUninit;
7
8use rustix::fs::{Dir, DirEntry};
9
10#[test]
11fn dir_entries() {
12    let tmpdir = tempfile::tempdir().expect("construct tempdir");
13    let dirfd = File::open(tmpdir.path()).expect("open tempdir as file");
14    let mut dir = Dir::read_from(dirfd).expect("construct Dir from dirfd");
15
16    let entries = read_entries(&mut dir);
17    assert_eq!(entries.len(), 0, "no files in directory");
18
19    let _f1 = File::create(tmpdir.path().join("file1")).expect("create file1");
20
21    let entries = read_entries(&mut dir);
22    assert!(
23        entries.get("file1").is_some(),
24        "directory contains `file1`: {:?}",
25        entries
26    );
27    assert_eq!(entries.len(), 1);
28
29    let _f2 = File::create(tmpdir.path().join("file2")).expect("create file1");
30    let entries = read_entries(&mut dir);
31    assert!(
32        entries.get("file1").is_some(),
33        "directory contains `file1`: {:?}",
34        entries
35    );
36    assert!(
37        entries.get("file2").is_some(),
38        "directory contains `file2`: {:?}",
39        entries
40    );
41    assert_eq!(entries.len(), 2);
42}
43
44fn read_entries(dir: &mut Dir) -> HashMap<String, DirEntry> {
45    dir.rewind();
46    let mut out = HashMap::new();
47    while let Some(err) = dir.read() {
48        let err = err.expect("non-error entry");
49        let name = err.file_name().to_str().expect("utf8 filename").to_owned();
50        if name != "." && name != ".." {
51            out.insert(name, err);
52        }
53    }
54    out
55}
56
57#[cfg(any(target_os = "android", target_os = "linux"))]
58fn test_raw_dir(buf: &mut [MaybeUninit<u8>]) {
59    use std::collections::HashSet;
60    use std::io::{Seek, SeekFrom};
61
62    use rustix::fd::AsFd;
63    use rustix::fs::RawDir;
64
65    fn read_raw_entries<Fd: AsFd>(dir: &mut RawDir<Fd>) -> HashSet<String> {
66        let mut out = HashSet::new();
67        while let Some(entry) = dir.next() {
68            let entry = entry.expect("non-error entry");
69            let name = entry
70                .file_name()
71                .to_str()
72                .expect("utf8 filename")
73                .to_owned();
74            if name != "." && name != ".." {
75                out.insert(name);
76            }
77        }
78        out
79    }
80
81    let tmpdir = tempfile::tempdir().expect("construct tempdir");
82    let mut dirfd = File::open(tmpdir.path()).expect("open tempdir as file");
83    let mut dir = RawDir::new(dirfd.try_clone().unwrap(), buf);
84
85    let entries = read_raw_entries(&mut dir);
86    assert_eq!(entries.len(), 0, "no files in directory");
87
88    let _f1 = File::create(tmpdir.path().join("file1")).expect("create file1");
89
90    dirfd.seek(SeekFrom::Start(0)).unwrap();
91    let entries = read_raw_entries(&mut dir);
92    assert!(
93        entries.get("file1").is_some(),
94        "directory contains `file1`: {:?}",
95        entries
96    );
97    assert_eq!(entries.len(), 1);
98
99    let _f2 = File::create(tmpdir.path().join("file2")).expect("create file1");
100    dirfd.seek(SeekFrom::Start(0)).unwrap();
101    let entries = read_raw_entries(&mut dir);
102    assert!(
103        entries.get("file1").is_some(),
104        "directory contains `file1`: {:?}",
105        entries
106    );
107    assert!(
108        entries.get("file2").is_some(),
109        "directory contains `file2`: {:?}",
110        entries
111    );
112    assert_eq!(entries.len(), 2);
113}
114
115#[test]
116#[cfg(any(target_os = "android", target_os = "linux"))]
117fn raw_dir_entries_heap() {
118    // When we can depend on Rust 1.60, we can use the spare_capacity_mut version instead.
119    /*
120    let mut buf = Vec::with_capacity(8192);
121    test_raw_dir(buf.spare_capacity_mut());
122    */
123    let mut buf = [MaybeUninit::new(0); 8192];
124    test_raw_dir(&mut buf);
125}
126
127#[test]
128#[cfg(any(target_os = "android", target_os = "linux"))]
129fn raw_dir_entries_stack() {
130    let mut buf = [MaybeUninit::uninit(); 2048];
131    test_raw_dir(&mut buf);
132}
133
134#[test]
135#[cfg(any(target_os = "android", target_os = "linux"))]
136fn raw_dir_entries_unaligned() {
137    let mut buf = [MaybeUninit::uninit(); 2048];
138    let buf = &mut buf[1..];
139
140    assert!(!(buf.as_ptr() as usize).is_power_of_two());
141
142    test_raw_dir(buf);
143}
144
145#[test]
146fn dir_from_openat() {
147    let dirfd = rustix::fs::openat(
148        rustix::fs::cwd(),
149        ".",
150        rustix::fs::OFlags::RDONLY,
151        rustix::fs::Mode::empty(),
152    )
153    .expect("open cwd as file");
154    let _dir = Dir::read_from(dirfd).expect("construct Dir from dirfd");
155}
156