1 #![deny(warnings)]
2
3
4 // This test suite is incomplete and doesn't cover all available functionality.
5 // Contributions to improve test coverage would be highly appreciated!
6
7 use inotify::{
8 Inotify,
9 WatchMask,
10 };
11 use std::fs::File;
12 use std::io::{
13 Write,
14 ErrorKind,
15 };
16 use std::os::unix::io::{
17 AsRawFd,
18 FromRawFd,
19 IntoRawFd,
20 };
21 use std::path::PathBuf;
22 use tempdir::TempDir;
23
24
25 #[test]
it_should_watch_a_file()26 fn it_should_watch_a_file() {
27 let mut testdir = TestDir::new();
28 let (path, mut file) = testdir.new_file();
29
30 let mut inotify = Inotify::init().unwrap();
31 let watch = inotify.add_watch(&path, WatchMask::MODIFY).unwrap();
32
33 write_to(&mut file);
34
35 let mut buffer = [0; 1024];
36 let events = inotify.read_events_blocking(&mut buffer).unwrap();
37
38 let mut num_events = 0;
39 for event in events {
40 assert_eq!(watch, event.wd);
41 num_events += 1;
42 }
43 assert!(num_events > 0);
44 }
45
46 #[cfg(feature = "stream")]
47 #[tokio::test]
it_should_watch_a_file_async()48 async fn it_should_watch_a_file_async() {
49 let mut testdir = TestDir::new();
50 let (path, mut file) = testdir.new_file();
51
52 let mut inotify = Inotify::init().unwrap();
53 let watch = inotify.add_watch(&path, WatchMask::MODIFY).unwrap();
54
55 write_to(&mut file);
56
57 let mut buffer = [0; 1024];
58
59 use futures_util::StreamExt;
60 let events = inotify
61 .event_stream(&mut buffer[..])
62 .unwrap()
63 .take(1)
64 .collect::<Vec<_>>()
65 .await;
66
67 let mut num_events = 0;
68 for event in events {
69 if let Ok(event) = event {
70 assert_eq!(watch, event.wd);
71 num_events += 1;
72 }
73 }
74 assert!(num_events > 0);
75 }
76
77 #[test]
it_should_return_immediately_if_no_events_are_available()78 fn it_should_return_immediately_if_no_events_are_available() {
79 let mut inotify = Inotify::init().unwrap();
80
81 let mut buffer = [0; 1024];
82 assert_eq!(0, inotify.read_events(&mut buffer).unwrap().count());
83 }
84
85 #[test]
it_should_convert_the_name_into_an_os_str()86 fn it_should_convert_the_name_into_an_os_str() {
87 let mut testdir = TestDir::new();
88 let (path, mut file) = testdir.new_file();
89
90 let mut inotify = Inotify::init().unwrap();
91 inotify.add_watch(&path.parent().unwrap(), WatchMask::MODIFY).unwrap();
92
93 write_to(&mut file);
94
95 let mut buffer = [0; 1024];
96 let mut events = inotify.read_events_blocking(&mut buffer).unwrap();
97
98 if let Some(event) = events.next() {
99 assert_eq!(path.file_name(), event.name);
100 }
101 else {
102 panic!("Expected inotify event");
103 }
104 }
105
106 #[test]
it_should_set_name_to_none_if_it_is_empty()107 fn it_should_set_name_to_none_if_it_is_empty() {
108 let mut testdir = TestDir::new();
109 let (path, mut file) = testdir.new_file();
110
111 let mut inotify = Inotify::init().unwrap();
112 inotify.add_watch(&path, WatchMask::MODIFY).unwrap();
113
114 write_to(&mut file);
115
116 let mut buffer = [0; 1024];
117 let mut events = inotify.read_events_blocking(&mut buffer).unwrap();
118
119 if let Some(event) = events.next() {
120 assert_eq!(event.name, None);
121 }
122 else {
123 panic!("Expected inotify event");
124 }
125 }
126
127 #[test]
it_should_not_accept_watchdescriptors_from_other_instances()128 fn it_should_not_accept_watchdescriptors_from_other_instances() {
129 let mut testdir = TestDir::new();
130 let (path, _) = testdir.new_file();
131
132 let mut inotify = Inotify::init().unwrap();
133 let _ = inotify.add_watch(&path, WatchMask::ACCESS).unwrap();
134
135 let mut second_inotify = Inotify::init().unwrap();
136 let wd2 = second_inotify.add_watch(&path, WatchMask::ACCESS).unwrap();
137
138 assert_eq!(inotify.rm_watch(wd2).unwrap_err().kind(), ErrorKind::InvalidInput);
139 }
140
141 #[test]
watch_descriptors_from_different_inotify_instances_should_not_be_equal()142 fn watch_descriptors_from_different_inotify_instances_should_not_be_equal() {
143 let mut testdir = TestDir::new();
144 let (path, _) = testdir.new_file();
145
146 let mut inotify_1 = Inotify::init()
147 .unwrap();
148 let mut inotify_2 = Inotify::init()
149 .unwrap();
150
151 let wd_1 = inotify_1
152 .add_watch(&path, WatchMask::ACCESS)
153 .unwrap();
154 let wd_2 = inotify_2
155 .add_watch(&path, WatchMask::ACCESS)
156 .unwrap();
157
158 // As far as inotify is concerned, watch descriptors are just integers that
159 // are scoped per inotify instance. This means that multiple instances will
160 // produce the same watch descriptor number, a case we want inotify-rs to
161 // detect.
162 assert!(wd_1 != wd_2);
163 }
164
165 #[test]
watch_descriptor_equality_should_not_be_confused_by_reused_fds()166 fn watch_descriptor_equality_should_not_be_confused_by_reused_fds() {
167 let mut testdir = TestDir::new();
168 let (path, _) = testdir.new_file();
169
170 // When a new inotify instance is created directly after closing another
171 // one, it is possible that the file descriptor is reused immediately, and
172 // we end up with a new instance that has the same file descriptor as the
173 // old one.
174 // This is quite likely, but it doesn't happen every time. Therefore we may
175 // need a few tries until we find two instances where that is the case.
176 let (wd_1, mut inotify_2) = loop {
177 let mut inotify_1 = Inotify::init()
178 .unwrap();
179
180 let wd_1 = inotify_1
181 .add_watch(&path, WatchMask::ACCESS)
182 .unwrap();
183 let fd_1 = inotify_1.as_raw_fd();
184
185 inotify_1
186 .close()
187 .unwrap();
188 let inotify_2 = Inotify::init()
189 .unwrap();
190
191 if fd_1 == inotify_2.as_raw_fd() {
192 break (wd_1, inotify_2);
193 }
194 };
195
196 let wd_2 = inotify_2
197 .add_watch(&path, WatchMask::ACCESS)
198 .unwrap();
199
200 // The way we engineered this situation, both `WatchDescriptor` instances
201 // have the same fields. They still come from different inotify instances
202 // though, so they shouldn't be equal.
203 assert!(wd_1 != wd_2);
204
205 inotify_2
206 .close()
207 .unwrap();
208
209 // A little extra gotcha: If both inotify instances are closed, and the `Eq`
210 // implementation naively compares the weak pointers, both will be `None`,
211 // making them equal. Let's make sure this isn't the case.
212 assert!(wd_1 != wd_2);
213 }
214
215 #[test]
it_should_implement_raw_fd_traits_correctly()216 fn it_should_implement_raw_fd_traits_correctly() {
217 let fd = Inotify::init()
218 .expect("Failed to initialize inotify instance")
219 .into_raw_fd();
220
221 // If `IntoRawFd` has been implemented naively, `Inotify`'s `Drop`
222 // implementation will have closed the inotify instance at this point. Let's
223 // make sure this didn't happen.
224 let mut inotify = unsafe { <Inotify as FromRawFd>::from_raw_fd(fd) };
225
226 let mut buffer = [0; 1024];
227 if let Err(error) = inotify.read_events(&mut buffer) {
228 panic!("Failed to add watch: {}", error);
229 }
230 }
231
232
233 struct TestDir {
234 dir: TempDir,
235 counter: u32,
236 }
237
238 impl TestDir {
new() -> TestDir239 fn new() -> TestDir {
240 TestDir {
241 dir: TempDir::new("inotify-rs-test").unwrap(),
242 counter: 0,
243 }
244 }
245
new_file(&mut self) -> (PathBuf, File)246 fn new_file(&mut self) -> (PathBuf, File) {
247 let id = self.counter;
248 self.counter += 1;
249
250 let path = self.dir.path().join("file-".to_string() + &id.to_string());
251 let file = File::create(&path)
252 .unwrap_or_else(|error| panic!("Failed to create temporary file: {}", error));
253
254 (path, file)
255 }
256 }
257
write_to(file: &mut File)258 fn write_to(file: &mut File) {
259 file
260 .write(b"This should trigger an inotify event.")
261 .unwrap_or_else(|error|
262 panic!("Failed to write to file: {}", error)
263 );
264 }
265