]>
Commit | Line | Data |
---|---|---|
1b1a35ee XL |
1 | use crate::io::prelude::*; |
2 | ||
3 | use crate::fs::{self, File, OpenOptions}; | |
4 | use crate::io::{ErrorKind, SeekFrom}; | |
5 | use crate::path::Path; | |
6 | use crate::str; | |
7 | use crate::sys_common::io::test::{tmpdir, TempDir}; | |
8 | use crate::thread; | |
9 | ||
10 | use rand::{rngs::StdRng, RngCore, SeedableRng}; | |
11 | ||
12 | #[cfg(unix)] | |
13 | use crate::os::unix::fs::symlink as symlink_dir; | |
14 | #[cfg(unix)] | |
15 | use crate::os::unix::fs::symlink as symlink_file; | |
16 | #[cfg(unix)] | |
17 | use crate::os::unix::fs::symlink as symlink_junction; | |
18 | #[cfg(windows)] | |
19 | use crate::os::windows::fs::{symlink_dir, symlink_file}; | |
20 | #[cfg(windows)] | |
21 | use crate::sys::fs::symlink_junction; | |
22 | ||
23 | macro_rules! check { | |
24 | ($e:expr) => { | |
25 | match $e { | |
26 | Ok(t) => t, | |
27 | Err(e) => panic!("{} failed with: {}", stringify!($e), e), | |
28 | } | |
29 | }; | |
30 | } | |
31 | ||
32 | #[cfg(windows)] | |
33 | macro_rules! error { | |
34 | ($e:expr, $s:expr) => { | |
35 | match $e { | |
36 | Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), | |
37 | Err(ref err) => assert!( | |
38 | err.raw_os_error() == Some($s), | |
39 | format!("`{}` did not have a code of `{}`", err, $s) | |
40 | ), | |
41 | } | |
42 | }; | |
43 | } | |
44 | ||
45 | #[cfg(unix)] | |
46 | macro_rules! error { | |
47 | ($e:expr, $s:expr) => { | |
48 | error_contains!($e, $s) | |
49 | }; | |
50 | } | |
51 | ||
52 | macro_rules! error_contains { | |
53 | ($e:expr, $s:expr) => { | |
54 | match $e { | |
55 | Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), | |
56 | Err(ref err) => { | |
57 | assert!(err.to_string().contains($s), format!("`{}` did not contain `{}`", err, $s)) | |
58 | } | |
59 | } | |
60 | }; | |
61 | } | |
62 | ||
63 | // Several test fail on windows if the user does not have permission to | |
64 | // create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of | |
65 | // disabling these test on Windows, use this function to test whether we | |
66 | // have permission, and return otherwise. This way, we still don't run these | |
67 | // tests most of the time, but at least we do if the user has the right | |
68 | // permissions. | |
69 | pub fn got_symlink_permission(tmpdir: &TempDir) -> bool { | |
70 | if cfg!(unix) { | |
71 | return true; | |
72 | } | |
73 | let link = tmpdir.join("some_hopefully_unique_link_name"); | |
74 | ||
75 | match symlink_file(r"nonexisting_target", link) { | |
1b1a35ee XL |
76 | // ERROR_PRIVILEGE_NOT_HELD = 1314 |
77 | Err(ref err) if err.raw_os_error() == Some(1314) => false, | |
29967ef6 | 78 | Ok(_) | Err(_) => true, |
1b1a35ee XL |
79 | } |
80 | } | |
81 | ||
82 | #[test] | |
83 | fn file_test_io_smoke_test() { | |
84 | let message = "it's alright. have a good time"; | |
85 | let tmpdir = tmpdir(); | |
86 | let filename = &tmpdir.join("file_rt_io_file_test.txt"); | |
87 | { | |
88 | let mut write_stream = check!(File::create(filename)); | |
89 | check!(write_stream.write(message.as_bytes())); | |
90 | } | |
91 | { | |
92 | let mut read_stream = check!(File::open(filename)); | |
93 | let mut read_buf = [0; 1028]; | |
94 | let read_str = match check!(read_stream.read(&mut read_buf)) { | |
95 | 0 => panic!("shouldn't happen"), | |
96 | n => str::from_utf8(&read_buf[..n]).unwrap().to_string(), | |
97 | }; | |
98 | assert_eq!(read_str, message); | |
99 | } | |
100 | check!(fs::remove_file(filename)); | |
101 | } | |
102 | ||
103 | #[test] | |
104 | fn invalid_path_raises() { | |
105 | let tmpdir = tmpdir(); | |
106 | let filename = &tmpdir.join("file_that_does_not_exist.txt"); | |
107 | let result = File::open(filename); | |
108 | ||
109 | #[cfg(all(unix, not(target_os = "vxworks")))] | |
110 | error!(result, "No such file or directory"); | |
111 | #[cfg(target_os = "vxworks")] | |
112 | error!(result, "no such file or directory"); | |
113 | #[cfg(windows)] | |
114 | error!(result, 2); // ERROR_FILE_NOT_FOUND | |
115 | } | |
116 | ||
117 | #[test] | |
118 | fn file_test_iounlinking_invalid_path_should_raise_condition() { | |
119 | let tmpdir = tmpdir(); | |
120 | let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt"); | |
121 | ||
122 | let result = fs::remove_file(filename); | |
123 | ||
124 | #[cfg(all(unix, not(target_os = "vxworks")))] | |
125 | error!(result, "No such file or directory"); | |
126 | #[cfg(target_os = "vxworks")] | |
127 | error!(result, "no such file or directory"); | |
128 | #[cfg(windows)] | |
129 | error!(result, 2); // ERROR_FILE_NOT_FOUND | |
130 | } | |
131 | ||
132 | #[test] | |
133 | fn file_test_io_non_positional_read() { | |
134 | let message: &str = "ten-four"; | |
135 | let mut read_mem = [0; 8]; | |
136 | let tmpdir = tmpdir(); | |
137 | let filename = &tmpdir.join("file_rt_io_file_test_positional.txt"); | |
138 | { | |
139 | let mut rw_stream = check!(File::create(filename)); | |
140 | check!(rw_stream.write(message.as_bytes())); | |
141 | } | |
142 | { | |
143 | let mut read_stream = check!(File::open(filename)); | |
144 | { | |
145 | let read_buf = &mut read_mem[0..4]; | |
146 | check!(read_stream.read(read_buf)); | |
147 | } | |
148 | { | |
149 | let read_buf = &mut read_mem[4..8]; | |
150 | check!(read_stream.read(read_buf)); | |
151 | } | |
152 | } | |
153 | check!(fs::remove_file(filename)); | |
154 | let read_str = str::from_utf8(&read_mem).unwrap(); | |
155 | assert_eq!(read_str, message); | |
156 | } | |
157 | ||
158 | #[test] | |
159 | fn file_test_io_seek_and_tell_smoke_test() { | |
160 | let message = "ten-four"; | |
161 | let mut read_mem = [0; 4]; | |
162 | let set_cursor = 4 as u64; | |
163 | let tell_pos_pre_read; | |
164 | let tell_pos_post_read; | |
165 | let tmpdir = tmpdir(); | |
166 | let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt"); | |
167 | { | |
168 | let mut rw_stream = check!(File::create(filename)); | |
169 | check!(rw_stream.write(message.as_bytes())); | |
170 | } | |
171 | { | |
172 | let mut read_stream = check!(File::open(filename)); | |
173 | check!(read_stream.seek(SeekFrom::Start(set_cursor))); | |
174 | tell_pos_pre_read = check!(read_stream.seek(SeekFrom::Current(0))); | |
175 | check!(read_stream.read(&mut read_mem)); | |
176 | tell_pos_post_read = check!(read_stream.seek(SeekFrom::Current(0))); | |
177 | } | |
178 | check!(fs::remove_file(filename)); | |
179 | let read_str = str::from_utf8(&read_mem).unwrap(); | |
180 | assert_eq!(read_str, &message[4..8]); | |
181 | assert_eq!(tell_pos_pre_read, set_cursor); | |
182 | assert_eq!(tell_pos_post_read, message.len() as u64); | |
183 | } | |
184 | ||
185 | #[test] | |
186 | fn file_test_io_seek_and_write() { | |
187 | let initial_msg = "food-is-yummy"; | |
188 | let overwrite_msg = "-the-bar!!"; | |
189 | let final_msg = "foo-the-bar!!"; | |
190 | let seek_idx = 3; | |
191 | let mut read_mem = [0; 13]; | |
192 | let tmpdir = tmpdir(); | |
193 | let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt"); | |
194 | { | |
195 | let mut rw_stream = check!(File::create(filename)); | |
196 | check!(rw_stream.write(initial_msg.as_bytes())); | |
197 | check!(rw_stream.seek(SeekFrom::Start(seek_idx))); | |
198 | check!(rw_stream.write(overwrite_msg.as_bytes())); | |
199 | } | |
200 | { | |
201 | let mut read_stream = check!(File::open(filename)); | |
202 | check!(read_stream.read(&mut read_mem)); | |
203 | } | |
204 | check!(fs::remove_file(filename)); | |
205 | let read_str = str::from_utf8(&read_mem).unwrap(); | |
206 | assert!(read_str == final_msg); | |
207 | } | |
208 | ||
209 | #[test] | |
210 | fn file_test_io_seek_shakedown() { | |
211 | // 01234567890123 | |
212 | let initial_msg = "qwer-asdf-zxcv"; | |
213 | let chunk_one: &str = "qwer"; | |
214 | let chunk_two: &str = "asdf"; | |
215 | let chunk_three: &str = "zxcv"; | |
216 | let mut read_mem = [0; 4]; | |
217 | let tmpdir = tmpdir(); | |
218 | let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt"); | |
219 | { | |
220 | let mut rw_stream = check!(File::create(filename)); | |
221 | check!(rw_stream.write(initial_msg.as_bytes())); | |
222 | } | |
223 | { | |
224 | let mut read_stream = check!(File::open(filename)); | |
225 | ||
226 | check!(read_stream.seek(SeekFrom::End(-4))); | |
227 | check!(read_stream.read(&mut read_mem)); | |
228 | assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three); | |
229 | ||
230 | check!(read_stream.seek(SeekFrom::Current(-9))); | |
231 | check!(read_stream.read(&mut read_mem)); | |
232 | assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two); | |
233 | ||
234 | check!(read_stream.seek(SeekFrom::Start(0))); | |
235 | check!(read_stream.read(&mut read_mem)); | |
236 | assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one); | |
237 | } | |
238 | check!(fs::remove_file(filename)); | |
239 | } | |
240 | ||
241 | #[test] | |
242 | fn file_test_io_eof() { | |
243 | let tmpdir = tmpdir(); | |
244 | let filename = tmpdir.join("file_rt_io_file_test_eof.txt"); | |
245 | let mut buf = [0; 256]; | |
246 | { | |
247 | let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); | |
248 | let mut rw = check!(oo.open(&filename)); | |
249 | assert_eq!(check!(rw.read(&mut buf)), 0); | |
250 | assert_eq!(check!(rw.read(&mut buf)), 0); | |
251 | } | |
252 | check!(fs::remove_file(&filename)); | |
253 | } | |
254 | ||
255 | #[test] | |
256 | #[cfg(unix)] | |
257 | fn file_test_io_read_write_at() { | |
258 | use crate::os::unix::fs::FileExt; | |
259 | ||
260 | let tmpdir = tmpdir(); | |
261 | let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt"); | |
262 | let mut buf = [0; 256]; | |
263 | let write1 = "asdf"; | |
264 | let write2 = "qwer-"; | |
265 | let write3 = "-zxcv"; | |
266 | let content = "qwer-asdf-zxcv"; | |
267 | { | |
268 | let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); | |
269 | let mut rw = check!(oo.open(&filename)); | |
270 | assert_eq!(check!(rw.write_at(write1.as_bytes(), 5)), write1.len()); | |
271 | assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); | |
272 | assert_eq!(check!(rw.read_at(&mut buf, 5)), write1.len()); | |
273 | assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); | |
274 | assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); | |
275 | assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); | |
276 | assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok("\0\0\0\0\0")); | |
277 | assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); | |
278 | assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); | |
279 | assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); | |
280 | assert_eq!(check!(rw.read(&mut buf)), write1.len()); | |
281 | assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); | |
282 | assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); | |
283 | assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); | |
284 | assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); | |
285 | assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); | |
286 | assert_eq!(check!(rw.write_at(write3.as_bytes(), 9)), write3.len()); | |
287 | assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); | |
288 | } | |
289 | { | |
290 | let mut read = check!(File::open(&filename)); | |
291 | assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); | |
292 | assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); | |
293 | assert_eq!(check!(read.seek(SeekFrom::Current(0))), 0); | |
294 | assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); | |
295 | assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); | |
296 | assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); | |
297 | assert_eq!(check!(read.seek(SeekFrom::Current(0))), 9); | |
298 | assert_eq!(check!(read.read(&mut buf)), write3.len()); | |
299 | assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); | |
300 | assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); | |
301 | assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); | |
302 | assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); | |
303 | assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); | |
304 | assert_eq!(check!(read.read_at(&mut buf, 14)), 0); | |
305 | assert_eq!(check!(read.read_at(&mut buf, 15)), 0); | |
306 | assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); | |
307 | } | |
308 | check!(fs::remove_file(&filename)); | |
309 | } | |
310 | ||
311 | #[test] | |
312 | #[cfg(unix)] | |
313 | fn set_get_unix_permissions() { | |
314 | use crate::os::unix::fs::PermissionsExt; | |
315 | ||
316 | let tmpdir = tmpdir(); | |
317 | let filename = &tmpdir.join("set_get_unix_permissions"); | |
318 | check!(fs::create_dir(filename)); | |
319 | let mask = 0o7777; | |
320 | ||
321 | check!(fs::set_permissions(filename, fs::Permissions::from_mode(0))); | |
322 | let metadata0 = check!(fs::metadata(filename)); | |
323 | assert_eq!(mask & metadata0.permissions().mode(), 0); | |
324 | ||
325 | check!(fs::set_permissions(filename, fs::Permissions::from_mode(0o1777))); | |
326 | let metadata1 = check!(fs::metadata(filename)); | |
327 | #[cfg(all(unix, not(target_os = "vxworks")))] | |
328 | assert_eq!(mask & metadata1.permissions().mode(), 0o1777); | |
329 | #[cfg(target_os = "vxworks")] | |
330 | assert_eq!(mask & metadata1.permissions().mode(), 0o0777); | |
331 | } | |
332 | ||
333 | #[test] | |
334 | #[cfg(windows)] | |
335 | fn file_test_io_seek_read_write() { | |
336 | use crate::os::windows::fs::FileExt; | |
337 | ||
338 | let tmpdir = tmpdir(); | |
339 | let filename = tmpdir.join("file_rt_io_file_test_seek_read_write.txt"); | |
340 | let mut buf = [0; 256]; | |
341 | let write1 = "asdf"; | |
342 | let write2 = "qwer-"; | |
343 | let write3 = "-zxcv"; | |
344 | let content = "qwer-asdf-zxcv"; | |
345 | { | |
346 | let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); | |
347 | let mut rw = check!(oo.open(&filename)); | |
348 | assert_eq!(check!(rw.seek_write(write1.as_bytes(), 5)), write1.len()); | |
349 | assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); | |
350 | assert_eq!(check!(rw.seek_read(&mut buf, 5)), write1.len()); | |
351 | assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); | |
352 | assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); | |
353 | assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0); | |
354 | assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); | |
355 | assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); | |
356 | assert_eq!(check!(rw.read(&mut buf)), write1.len()); | |
357 | assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); | |
358 | assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); | |
359 | assert_eq!(check!(rw.seek_read(&mut buf[..write2.len()], 0)), write2.len()); | |
360 | assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); | |
361 | assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); | |
362 | assert_eq!(check!(rw.seek_write(write3.as_bytes(), 9)), write3.len()); | |
363 | assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 14); | |
364 | } | |
365 | { | |
366 | let mut read = check!(File::open(&filename)); | |
367 | assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); | |
368 | assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); | |
369 | assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); | |
370 | assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); | |
371 | assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); | |
372 | assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); | |
373 | assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); | |
374 | assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); | |
375 | assert_eq!(check!(read.read(&mut buf)), write3.len()); | |
376 | assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); | |
377 | assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); | |
378 | assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); | |
379 | assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); | |
380 | assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); | |
381 | assert_eq!(check!(read.seek_read(&mut buf, 14)), 0); | |
382 | assert_eq!(check!(read.seek_read(&mut buf, 15)), 0); | |
383 | } | |
384 | check!(fs::remove_file(&filename)); | |
385 | } | |
386 | ||
387 | #[test] | |
388 | fn file_test_stat_is_correct_on_is_file() { | |
389 | let tmpdir = tmpdir(); | |
390 | let filename = &tmpdir.join("file_stat_correct_on_is_file.txt"); | |
391 | { | |
392 | let mut opts = OpenOptions::new(); | |
393 | let mut fs = check!(opts.read(true).write(true).create(true).open(filename)); | |
394 | let msg = "hw"; | |
395 | fs.write(msg.as_bytes()).unwrap(); | |
396 | ||
397 | let fstat_res = check!(fs.metadata()); | |
398 | assert!(fstat_res.is_file()); | |
399 | } | |
400 | let stat_res_fn = check!(fs::metadata(filename)); | |
401 | assert!(stat_res_fn.is_file()); | |
402 | let stat_res_meth = check!(filename.metadata()); | |
403 | assert!(stat_res_meth.is_file()); | |
404 | check!(fs::remove_file(filename)); | |
405 | } | |
406 | ||
407 | #[test] | |
408 | fn file_test_stat_is_correct_on_is_dir() { | |
409 | let tmpdir = tmpdir(); | |
410 | let filename = &tmpdir.join("file_stat_correct_on_is_dir"); | |
411 | check!(fs::create_dir(filename)); | |
412 | let stat_res_fn = check!(fs::metadata(filename)); | |
413 | assert!(stat_res_fn.is_dir()); | |
414 | let stat_res_meth = check!(filename.metadata()); | |
415 | assert!(stat_res_meth.is_dir()); | |
416 | check!(fs::remove_dir(filename)); | |
417 | } | |
418 | ||
419 | #[test] | |
420 | fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { | |
421 | let tmpdir = tmpdir(); | |
422 | let dir = &tmpdir.join("fileinfo_false_on_dir"); | |
423 | check!(fs::create_dir(dir)); | |
424 | assert!(!dir.is_file()); | |
425 | check!(fs::remove_dir(dir)); | |
426 | } | |
427 | ||
428 | #[test] | |
429 | fn file_test_fileinfo_check_exists_before_and_after_file_creation() { | |
430 | let tmpdir = tmpdir(); | |
431 | let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt"); | |
432 | check!(check!(File::create(file)).write(b"foo")); | |
433 | assert!(file.exists()); | |
434 | check!(fs::remove_file(file)); | |
435 | assert!(!file.exists()); | |
436 | } | |
437 | ||
438 | #[test] | |
439 | fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { | |
440 | let tmpdir = tmpdir(); | |
441 | let dir = &tmpdir.join("before_and_after_dir"); | |
442 | assert!(!dir.exists()); | |
443 | check!(fs::create_dir(dir)); | |
444 | assert!(dir.exists()); | |
445 | assert!(dir.is_dir()); | |
446 | check!(fs::remove_dir(dir)); | |
447 | assert!(!dir.exists()); | |
448 | } | |
449 | ||
450 | #[test] | |
451 | fn file_test_directoryinfo_readdir() { | |
452 | let tmpdir = tmpdir(); | |
453 | let dir = &tmpdir.join("di_readdir"); | |
454 | check!(fs::create_dir(dir)); | |
455 | let prefix = "foo"; | |
456 | for n in 0..3 { | |
457 | let f = dir.join(&format!("{}.txt", n)); | |
458 | let mut w = check!(File::create(&f)); | |
459 | let msg_str = format!("{}{}", prefix, n.to_string()); | |
460 | let msg = msg_str.as_bytes(); | |
461 | check!(w.write(msg)); | |
462 | } | |
463 | let files = check!(fs::read_dir(dir)); | |
464 | let mut mem = [0; 4]; | |
465 | for f in files { | |
466 | let f = f.unwrap().path(); | |
467 | { | |
468 | let n = f.file_stem().unwrap(); | |
469 | check!(check!(File::open(&f)).read(&mut mem)); | |
470 | let read_str = str::from_utf8(&mem).unwrap(); | |
471 | let expected = format!("{}{}", prefix, n.to_str().unwrap()); | |
472 | assert_eq!(expected, read_str); | |
473 | } | |
474 | check!(fs::remove_file(&f)); | |
475 | } | |
476 | check!(fs::remove_dir(dir)); | |
477 | } | |
478 | ||
479 | #[test] | |
480 | fn file_create_new_already_exists_error() { | |
481 | let tmpdir = tmpdir(); | |
482 | let file = &tmpdir.join("file_create_new_error_exists"); | |
483 | check!(fs::File::create(file)); | |
484 | let e = fs::OpenOptions::new().write(true).create_new(true).open(file).unwrap_err(); | |
485 | assert_eq!(e.kind(), ErrorKind::AlreadyExists); | |
486 | } | |
487 | ||
488 | #[test] | |
489 | fn mkdir_path_already_exists_error() { | |
490 | let tmpdir = tmpdir(); | |
491 | let dir = &tmpdir.join("mkdir_error_twice"); | |
492 | check!(fs::create_dir(dir)); | |
493 | let e = fs::create_dir(dir).unwrap_err(); | |
494 | assert_eq!(e.kind(), ErrorKind::AlreadyExists); | |
495 | } | |
496 | ||
497 | #[test] | |
498 | fn recursive_mkdir() { | |
499 | let tmpdir = tmpdir(); | |
500 | let dir = tmpdir.join("d1/d2"); | |
501 | check!(fs::create_dir_all(&dir)); | |
502 | assert!(dir.is_dir()) | |
503 | } | |
504 | ||
505 | #[test] | |
506 | fn recursive_mkdir_failure() { | |
507 | let tmpdir = tmpdir(); | |
508 | let dir = tmpdir.join("d1"); | |
509 | let file = dir.join("f1"); | |
510 | ||
511 | check!(fs::create_dir_all(&dir)); | |
512 | check!(File::create(&file)); | |
513 | ||
514 | let result = fs::create_dir_all(&file); | |
515 | ||
516 | assert!(result.is_err()); | |
517 | } | |
518 | ||
519 | #[test] | |
520 | fn concurrent_recursive_mkdir() { | |
521 | for _ in 0..100 { | |
522 | let dir = tmpdir(); | |
523 | let mut dir = dir.join("a"); | |
524 | for _ in 0..40 { | |
525 | dir = dir.join("a"); | |
526 | } | |
527 | let mut join = vec![]; | |
528 | for _ in 0..8 { | |
529 | let dir = dir.clone(); | |
530 | join.push(thread::spawn(move || { | |
531 | check!(fs::create_dir_all(&dir)); | |
532 | })) | |
533 | } | |
534 | ||
535 | // No `Display` on result of `join()` | |
536 | join.drain(..).map(|join| join.join().unwrap()).count(); | |
537 | } | |
538 | } | |
539 | ||
540 | #[test] | |
541 | fn recursive_mkdir_slash() { | |
542 | check!(fs::create_dir_all(Path::new("/"))); | |
543 | } | |
544 | ||
545 | #[test] | |
546 | fn recursive_mkdir_dot() { | |
547 | check!(fs::create_dir_all(Path::new("."))); | |
548 | } | |
549 | ||
550 | #[test] | |
551 | fn recursive_mkdir_empty() { | |
552 | check!(fs::create_dir_all(Path::new(""))); | |
553 | } | |
554 | ||
555 | #[test] | |
556 | fn recursive_rmdir() { | |
557 | let tmpdir = tmpdir(); | |
558 | let d1 = tmpdir.join("d1"); | |
559 | let dt = d1.join("t"); | |
560 | let dtt = dt.join("t"); | |
561 | let d2 = tmpdir.join("d2"); | |
562 | let canary = d2.join("do_not_delete"); | |
563 | check!(fs::create_dir_all(&dtt)); | |
564 | check!(fs::create_dir_all(&d2)); | |
565 | check!(check!(File::create(&canary)).write(b"foo")); | |
566 | check!(symlink_junction(&d2, &dt.join("d2"))); | |
567 | let _ = symlink_file(&canary, &d1.join("canary")); | |
568 | check!(fs::remove_dir_all(&d1)); | |
569 | ||
570 | assert!(!d1.is_dir()); | |
571 | assert!(canary.exists()); | |
572 | } | |
573 | ||
574 | #[test] | |
575 | fn recursive_rmdir_of_symlink() { | |
576 | // test we do not recursively delete a symlink but only dirs. | |
577 | let tmpdir = tmpdir(); | |
578 | let link = tmpdir.join("d1"); | |
579 | let dir = tmpdir.join("d2"); | |
580 | let canary = dir.join("do_not_delete"); | |
581 | check!(fs::create_dir_all(&dir)); | |
582 | check!(check!(File::create(&canary)).write(b"foo")); | |
583 | check!(symlink_junction(&dir, &link)); | |
584 | check!(fs::remove_dir_all(&link)); | |
585 | ||
586 | assert!(!link.is_dir()); | |
587 | assert!(canary.exists()); | |
588 | } | |
589 | ||
590 | #[test] | |
591 | // only Windows makes a distinction between file and directory symlinks. | |
592 | #[cfg(windows)] | |
593 | fn recursive_rmdir_of_file_symlink() { | |
594 | let tmpdir = tmpdir(); | |
595 | if !got_symlink_permission(&tmpdir) { | |
596 | return; | |
597 | }; | |
598 | ||
599 | let f1 = tmpdir.join("f1"); | |
600 | let f2 = tmpdir.join("f2"); | |
601 | check!(check!(File::create(&f1)).write(b"foo")); | |
602 | check!(symlink_file(&f1, &f2)); | |
603 | match fs::remove_dir_all(&f2) { | |
604 | Ok(..) => panic!("wanted a failure"), | |
605 | Err(..) => {} | |
606 | } | |
607 | } | |
608 | ||
609 | #[test] | |
610 | fn unicode_path_is_dir() { | |
611 | assert!(Path::new(".").is_dir()); | |
612 | assert!(!Path::new("test/stdtest/fs.rs").is_dir()); | |
613 | ||
614 | let tmpdir = tmpdir(); | |
615 | ||
616 | let mut dirpath = tmpdir.path().to_path_buf(); | |
617 | dirpath.push("test-가一ー你好"); | |
618 | check!(fs::create_dir(&dirpath)); | |
619 | assert!(dirpath.is_dir()); | |
620 | ||
621 | let mut filepath = dirpath; | |
622 | filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs"); | |
623 | check!(File::create(&filepath)); // ignore return; touch only | |
624 | assert!(!filepath.is_dir()); | |
625 | assert!(filepath.exists()); | |
626 | } | |
627 | ||
628 | #[test] | |
629 | fn unicode_path_exists() { | |
630 | assert!(Path::new(".").exists()); | |
631 | assert!(!Path::new("test/nonexistent-bogus-path").exists()); | |
632 | ||
633 | let tmpdir = tmpdir(); | |
634 | let unicode = tmpdir.path(); | |
635 | let unicode = unicode.join("test-각丁ー再见"); | |
636 | check!(fs::create_dir(&unicode)); | |
637 | assert!(unicode.exists()); | |
638 | assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); | |
639 | } | |
640 | ||
641 | #[test] | |
642 | fn copy_file_does_not_exist() { | |
643 | let from = Path::new("test/nonexistent-bogus-path"); | |
644 | let to = Path::new("test/other-bogus-path"); | |
645 | ||
646 | match fs::copy(&from, &to) { | |
647 | Ok(..) => panic!(), | |
648 | Err(..) => { | |
649 | assert!(!from.exists()); | |
650 | assert!(!to.exists()); | |
651 | } | |
652 | } | |
653 | } | |
654 | ||
655 | #[test] | |
656 | fn copy_src_does_not_exist() { | |
657 | let tmpdir = tmpdir(); | |
658 | let from = Path::new("test/nonexistent-bogus-path"); | |
659 | let to = tmpdir.join("out.txt"); | |
660 | check!(check!(File::create(&to)).write(b"hello")); | |
661 | assert!(fs::copy(&from, &to).is_err()); | |
662 | assert!(!from.exists()); | |
663 | let mut v = Vec::new(); | |
664 | check!(check!(File::open(&to)).read_to_end(&mut v)); | |
665 | assert_eq!(v, b"hello"); | |
666 | } | |
667 | ||
668 | #[test] | |
669 | fn copy_file_ok() { | |
670 | let tmpdir = tmpdir(); | |
671 | let input = tmpdir.join("in.txt"); | |
672 | let out = tmpdir.join("out.txt"); | |
673 | ||
674 | check!(check!(File::create(&input)).write(b"hello")); | |
675 | check!(fs::copy(&input, &out)); | |
676 | let mut v = Vec::new(); | |
677 | check!(check!(File::open(&out)).read_to_end(&mut v)); | |
678 | assert_eq!(v, b"hello"); | |
679 | ||
680 | assert_eq!(check!(input.metadata()).permissions(), check!(out.metadata()).permissions()); | |
681 | } | |
682 | ||
683 | #[test] | |
684 | fn copy_file_dst_dir() { | |
685 | let tmpdir = tmpdir(); | |
686 | let out = tmpdir.join("out"); | |
687 | ||
688 | check!(File::create(&out)); | |
689 | match fs::copy(&*out, tmpdir.path()) { | |
690 | Ok(..) => panic!(), | |
691 | Err(..) => {} | |
692 | } | |
693 | } | |
694 | ||
695 | #[test] | |
696 | fn copy_file_dst_exists() { | |
697 | let tmpdir = tmpdir(); | |
698 | let input = tmpdir.join("in"); | |
699 | let output = tmpdir.join("out"); | |
700 | ||
701 | check!(check!(File::create(&input)).write("foo".as_bytes())); | |
702 | check!(check!(File::create(&output)).write("bar".as_bytes())); | |
703 | check!(fs::copy(&input, &output)); | |
704 | ||
705 | let mut v = Vec::new(); | |
706 | check!(check!(File::open(&output)).read_to_end(&mut v)); | |
707 | assert_eq!(v, b"foo".to_vec()); | |
708 | } | |
709 | ||
710 | #[test] | |
711 | fn copy_file_src_dir() { | |
712 | let tmpdir = tmpdir(); | |
713 | let out = tmpdir.join("out"); | |
714 | ||
715 | match fs::copy(tmpdir.path(), &out) { | |
716 | Ok(..) => panic!(), | |
717 | Err(..) => {} | |
718 | } | |
719 | assert!(!out.exists()); | |
720 | } | |
721 | ||
722 | #[test] | |
723 | fn copy_file_preserves_perm_bits() { | |
724 | let tmpdir = tmpdir(); | |
725 | let input = tmpdir.join("in.txt"); | |
726 | let out = tmpdir.join("out.txt"); | |
727 | ||
728 | let attr = check!(check!(File::create(&input)).metadata()); | |
729 | let mut p = attr.permissions(); | |
730 | p.set_readonly(true); | |
731 | check!(fs::set_permissions(&input, p)); | |
732 | check!(fs::copy(&input, &out)); | |
733 | assert!(check!(out.metadata()).permissions().readonly()); | |
734 | check!(fs::set_permissions(&input, attr.permissions())); | |
735 | check!(fs::set_permissions(&out, attr.permissions())); | |
736 | } | |
737 | ||
738 | #[test] | |
739 | #[cfg(windows)] | |
740 | fn copy_file_preserves_streams() { | |
741 | let tmp = tmpdir(); | |
742 | check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes())); | |
743 | assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 0); | |
744 | assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0); | |
745 | let mut v = Vec::new(); | |
746 | check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v)); | |
747 | assert_eq!(v, b"carrot".to_vec()); | |
748 | } | |
749 | ||
750 | #[test] | |
751 | fn copy_file_returns_metadata_len() { | |
752 | let tmp = tmpdir(); | |
753 | let in_path = tmp.join("in.txt"); | |
754 | let out_path = tmp.join("out.txt"); | |
755 | check!(check!(File::create(&in_path)).write(b"lettuce")); | |
756 | #[cfg(windows)] | |
757 | check!(check!(File::create(tmp.join("in.txt:bunny"))).write(b"carrot")); | |
758 | let copied_len = check!(fs::copy(&in_path, &out_path)); | |
759 | assert_eq!(check!(out_path.metadata()).len(), copied_len); | |
760 | } | |
761 | ||
762 | #[test] | |
763 | fn copy_file_follows_dst_symlink() { | |
764 | let tmp = tmpdir(); | |
765 | if !got_symlink_permission(&tmp) { | |
766 | return; | |
767 | }; | |
768 | ||
769 | let in_path = tmp.join("in.txt"); | |
770 | let out_path = tmp.join("out.txt"); | |
771 | let out_path_symlink = tmp.join("out_symlink.txt"); | |
772 | ||
773 | check!(fs::write(&in_path, "foo")); | |
774 | check!(fs::write(&out_path, "bar")); | |
775 | check!(symlink_file(&out_path, &out_path_symlink)); | |
776 | ||
777 | check!(fs::copy(&in_path, &out_path_symlink)); | |
778 | ||
779 | assert!(check!(out_path_symlink.symlink_metadata()).file_type().is_symlink()); | |
780 | assert_eq!(check!(fs::read(&out_path_symlink)), b"foo".to_vec()); | |
781 | assert_eq!(check!(fs::read(&out_path)), b"foo".to_vec()); | |
782 | } | |
783 | ||
784 | #[test] | |
785 | fn symlinks_work() { | |
786 | let tmpdir = tmpdir(); | |
787 | if !got_symlink_permission(&tmpdir) { | |
788 | return; | |
789 | }; | |
790 | ||
791 | let input = tmpdir.join("in.txt"); | |
792 | let out = tmpdir.join("out.txt"); | |
793 | ||
794 | check!(check!(File::create(&input)).write("foobar".as_bytes())); | |
795 | check!(symlink_file(&input, &out)); | |
796 | assert!(check!(out.symlink_metadata()).file_type().is_symlink()); | |
797 | assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len()); | |
798 | let mut v = Vec::new(); | |
799 | check!(check!(File::open(&out)).read_to_end(&mut v)); | |
800 | assert_eq!(v, b"foobar".to_vec()); | |
801 | } | |
802 | ||
803 | #[test] | |
804 | fn symlink_noexist() { | |
805 | // Symlinks can point to things that don't exist | |
806 | let tmpdir = tmpdir(); | |
807 | if !got_symlink_permission(&tmpdir) { | |
808 | return; | |
809 | }; | |
810 | ||
811 | // Use a relative path for testing. Symlinks get normalized by Windows, | |
812 | // so we may not get the same path back for absolute paths | |
813 | check!(symlink_file(&"foo", &tmpdir.join("bar"))); | |
814 | assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))).to_str().unwrap(), "foo"); | |
815 | } | |
816 | ||
817 | #[test] | |
818 | fn read_link() { | |
819 | if cfg!(windows) { | |
820 | // directory symlink | |
821 | assert_eq!( | |
822 | check!(fs::read_link(r"C:\Users\All Users")).to_str().unwrap(), | |
823 | r"C:\ProgramData" | |
824 | ); | |
825 | // junction | |
826 | assert_eq!( | |
827 | check!(fs::read_link(r"C:\Users\Default User")).to_str().unwrap(), | |
828 | r"C:\Users\Default" | |
829 | ); | |
830 | // junction with special permissions | |
831 | assert_eq!( | |
832 | check!(fs::read_link(r"C:\Documents and Settings\")).to_str().unwrap(), | |
833 | r"C:\Users" | |
834 | ); | |
835 | } | |
836 | let tmpdir = tmpdir(); | |
837 | let link = tmpdir.join("link"); | |
838 | if !got_symlink_permission(&tmpdir) { | |
839 | return; | |
840 | }; | |
841 | check!(symlink_file(&"foo", &link)); | |
842 | assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo"); | |
843 | } | |
844 | ||
845 | #[test] | |
846 | fn readlink_not_symlink() { | |
847 | let tmpdir = tmpdir(); | |
848 | match fs::read_link(tmpdir.path()) { | |
849 | Ok(..) => panic!("wanted a failure"), | |
850 | Err(..) => {} | |
851 | } | |
852 | } | |
853 | ||
854 | #[test] | |
855 | fn links_work() { | |
856 | let tmpdir = tmpdir(); | |
857 | let input = tmpdir.join("in.txt"); | |
858 | let out = tmpdir.join("out.txt"); | |
859 | ||
860 | check!(check!(File::create(&input)).write("foobar".as_bytes())); | |
861 | check!(fs::hard_link(&input, &out)); | |
862 | assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len()); | |
863 | assert_eq!(check!(fs::metadata(&out)).len(), check!(input.metadata()).len()); | |
864 | let mut v = Vec::new(); | |
865 | check!(check!(File::open(&out)).read_to_end(&mut v)); | |
866 | assert_eq!(v, b"foobar".to_vec()); | |
867 | ||
868 | // can't link to yourself | |
869 | match fs::hard_link(&input, &input) { | |
870 | Ok(..) => panic!("wanted a failure"), | |
871 | Err(..) => {} | |
872 | } | |
873 | // can't link to something that doesn't exist | |
874 | match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) { | |
875 | Ok(..) => panic!("wanted a failure"), | |
876 | Err(..) => {} | |
877 | } | |
878 | } | |
879 | ||
880 | #[test] | |
881 | fn chmod_works() { | |
882 | let tmpdir = tmpdir(); | |
883 | let file = tmpdir.join("in.txt"); | |
884 | ||
885 | check!(File::create(&file)); | |
886 | let attr = check!(fs::metadata(&file)); | |
887 | assert!(!attr.permissions().readonly()); | |
888 | let mut p = attr.permissions(); | |
889 | p.set_readonly(true); | |
890 | check!(fs::set_permissions(&file, p.clone())); | |
891 | let attr = check!(fs::metadata(&file)); | |
892 | assert!(attr.permissions().readonly()); | |
893 | ||
894 | match fs::set_permissions(&tmpdir.join("foo"), p.clone()) { | |
895 | Ok(..) => panic!("wanted an error"), | |
896 | Err(..) => {} | |
897 | } | |
898 | ||
899 | p.set_readonly(false); | |
900 | check!(fs::set_permissions(&file, p)); | |
901 | } | |
902 | ||
903 | #[test] | |
904 | fn fchmod_works() { | |
905 | let tmpdir = tmpdir(); | |
906 | let path = tmpdir.join("in.txt"); | |
907 | ||
908 | let file = check!(File::create(&path)); | |
909 | let attr = check!(fs::metadata(&path)); | |
910 | assert!(!attr.permissions().readonly()); | |
911 | let mut p = attr.permissions(); | |
912 | p.set_readonly(true); | |
913 | check!(file.set_permissions(p.clone())); | |
914 | let attr = check!(fs::metadata(&path)); | |
915 | assert!(attr.permissions().readonly()); | |
916 | ||
917 | p.set_readonly(false); | |
918 | check!(file.set_permissions(p)); | |
919 | } | |
920 | ||
921 | #[test] | |
922 | fn sync_doesnt_kill_anything() { | |
923 | let tmpdir = tmpdir(); | |
924 | let path = tmpdir.join("in.txt"); | |
925 | ||
926 | let mut file = check!(File::create(&path)); | |
927 | check!(file.sync_all()); | |
928 | check!(file.sync_data()); | |
929 | check!(file.write(b"foo")); | |
930 | check!(file.sync_all()); | |
931 | check!(file.sync_data()); | |
932 | } | |
933 | ||
934 | #[test] | |
935 | fn truncate_works() { | |
936 | let tmpdir = tmpdir(); | |
937 | let path = tmpdir.join("in.txt"); | |
938 | ||
939 | let mut file = check!(File::create(&path)); | |
940 | check!(file.write(b"foo")); | |
941 | check!(file.sync_all()); | |
942 | ||
943 | // Do some simple things with truncation | |
944 | assert_eq!(check!(file.metadata()).len(), 3); | |
945 | check!(file.set_len(10)); | |
946 | assert_eq!(check!(file.metadata()).len(), 10); | |
947 | check!(file.write(b"bar")); | |
948 | check!(file.sync_all()); | |
949 | assert_eq!(check!(file.metadata()).len(), 10); | |
950 | ||
951 | let mut v = Vec::new(); | |
952 | check!(check!(File::open(&path)).read_to_end(&mut v)); | |
953 | assert_eq!(v, b"foobar\0\0\0\0".to_vec()); | |
954 | ||
955 | // Truncate to a smaller length, don't seek, and then write something. | |
956 | // Ensure that the intermediate zeroes are all filled in (we have `seek`ed | |
957 | // past the end of the file). | |
958 | check!(file.set_len(2)); | |
959 | assert_eq!(check!(file.metadata()).len(), 2); | |
960 | check!(file.write(b"wut")); | |
961 | check!(file.sync_all()); | |
962 | assert_eq!(check!(file.metadata()).len(), 9); | |
963 | let mut v = Vec::new(); | |
964 | check!(check!(File::open(&path)).read_to_end(&mut v)); | |
965 | assert_eq!(v, b"fo\0\0\0\0wut".to_vec()); | |
966 | } | |
967 | ||
968 | #[test] | |
969 | fn open_flavors() { | |
970 | use crate::fs::OpenOptions as OO; | |
971 | fn c<T: Clone>(t: &T) -> T { | |
972 | t.clone() | |
973 | } | |
974 | ||
975 | let tmpdir = tmpdir(); | |
976 | ||
977 | let mut r = OO::new(); | |
978 | r.read(true); | |
979 | let mut w = OO::new(); | |
980 | w.write(true); | |
981 | let mut rw = OO::new(); | |
982 | rw.read(true).write(true); | |
983 | let mut a = OO::new(); | |
984 | a.append(true); | |
985 | let mut ra = OO::new(); | |
986 | ra.read(true).append(true); | |
987 | ||
988 | #[cfg(windows)] | |
989 | let invalid_options = 87; // ERROR_INVALID_PARAMETER | |
990 | #[cfg(all(unix, not(target_os = "vxworks")))] | |
991 | let invalid_options = "Invalid argument"; | |
992 | #[cfg(target_os = "vxworks")] | |
993 | let invalid_options = "invalid argument"; | |
994 | ||
995 | // Test various combinations of creation modes and access modes. | |
996 | // | |
997 | // Allowed: | |
998 | // creation mode | read | write | read-write | append | read-append | | |
999 | // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:| | |
1000 | // not set (open existing) | X | X | X | X | X | | |
1001 | // create | | X | X | X | X | | |
1002 | // truncate | | X | X | | | | |
1003 | // create and truncate | | X | X | | | | |
1004 | // create_new | | X | X | X | X | | |
1005 | // | |
1006 | // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it. | |
1007 | ||
1008 | // write-only | |
1009 | check!(c(&w).create_new(true).open(&tmpdir.join("a"))); | |
1010 | check!(c(&w).create(true).truncate(true).open(&tmpdir.join("a"))); | |
1011 | check!(c(&w).truncate(true).open(&tmpdir.join("a"))); | |
1012 | check!(c(&w).create(true).open(&tmpdir.join("a"))); | |
1013 | check!(c(&w).open(&tmpdir.join("a"))); | |
1014 | ||
1015 | // read-only | |
1016 | error!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options); | |
1017 | error!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options); | |
1018 | error!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options); | |
1019 | error!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options); | |
1020 | check!(c(&r).open(&tmpdir.join("a"))); // try opening the file created with write_only | |
1021 | ||
1022 | // read-write | |
1023 | check!(c(&rw).create_new(true).open(&tmpdir.join("c"))); | |
1024 | check!(c(&rw).create(true).truncate(true).open(&tmpdir.join("c"))); | |
1025 | check!(c(&rw).truncate(true).open(&tmpdir.join("c"))); | |
1026 | check!(c(&rw).create(true).open(&tmpdir.join("c"))); | |
1027 | check!(c(&rw).open(&tmpdir.join("c"))); | |
1028 | ||
1029 | // append | |
1030 | check!(c(&a).create_new(true).open(&tmpdir.join("d"))); | |
1031 | error!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options); | |
1032 | error!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options); | |
1033 | check!(c(&a).create(true).open(&tmpdir.join("d"))); | |
1034 | check!(c(&a).open(&tmpdir.join("d"))); | |
1035 | ||
1036 | // read-append | |
1037 | check!(c(&ra).create_new(true).open(&tmpdir.join("e"))); | |
1038 | error!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options); | |
1039 | error!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options); | |
1040 | check!(c(&ra).create(true).open(&tmpdir.join("e"))); | |
1041 | check!(c(&ra).open(&tmpdir.join("e"))); | |
1042 | ||
1043 | // Test opening a file without setting an access mode | |
1044 | let mut blank = OO::new(); | |
1045 | error!(blank.create(true).open(&tmpdir.join("f")), invalid_options); | |
1046 | ||
1047 | // Test write works | |
1048 | check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes())); | |
1049 | ||
1050 | // Test write fails for read-only | |
1051 | check!(r.open(&tmpdir.join("h"))); | |
1052 | { | |
1053 | let mut f = check!(r.open(&tmpdir.join("h"))); | |
1054 | assert!(f.write("wut".as_bytes()).is_err()); | |
1055 | } | |
1056 | ||
1057 | // Test write overwrites | |
1058 | { | |
1059 | let mut f = check!(c(&w).open(&tmpdir.join("h"))); | |
1060 | check!(f.write("baz".as_bytes())); | |
1061 | } | |
1062 | { | |
1063 | let mut f = check!(c(&r).open(&tmpdir.join("h"))); | |
1064 | let mut b = vec![0; 6]; | |
1065 | check!(f.read(&mut b)); | |
1066 | assert_eq!(b, "bazbar".as_bytes()); | |
1067 | } | |
1068 | ||
1069 | // Test truncate works | |
1070 | { | |
1071 | let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h"))); | |
1072 | check!(f.write("foo".as_bytes())); | |
1073 | } | |
1074 | assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3); | |
1075 | ||
1076 | // Test append works | |
1077 | assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3); | |
1078 | { | |
1079 | let mut f = check!(c(&a).open(&tmpdir.join("h"))); | |
1080 | check!(f.write("bar".as_bytes())); | |
1081 | } | |
1082 | assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6); | |
1083 | ||
1084 | // Test .append(true) equals .write(true).append(true) | |
1085 | { | |
1086 | let mut f = check!(c(&w).append(true).open(&tmpdir.join("h"))); | |
1087 | check!(f.write("baz".as_bytes())); | |
1088 | } | |
1089 | assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9); | |
1090 | } | |
1091 | ||
1092 | #[test] | |
1093 | fn _assert_send_sync() { | |
1094 | fn _assert_send_sync<T: Send + Sync>() {} | |
1095 | _assert_send_sync::<OpenOptions>(); | |
1096 | } | |
1097 | ||
1098 | #[test] | |
1099 | fn binary_file() { | |
1100 | let mut bytes = [0; 1024]; | |
1101 | StdRng::from_entropy().fill_bytes(&mut bytes); | |
1102 | ||
1103 | let tmpdir = tmpdir(); | |
1104 | ||
1105 | check!(check!(File::create(&tmpdir.join("test"))).write(&bytes)); | |
1106 | let mut v = Vec::new(); | |
1107 | check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v)); | |
1108 | assert!(v == &bytes[..]); | |
1109 | } | |
1110 | ||
1111 | #[test] | |
1112 | fn write_then_read() { | |
1113 | let mut bytes = [0; 1024]; | |
1114 | StdRng::from_entropy().fill_bytes(&mut bytes); | |
1115 | ||
1116 | let tmpdir = tmpdir(); | |
1117 | ||
1118 | check!(fs::write(&tmpdir.join("test"), &bytes[..])); | |
1119 | let v = check!(fs::read(&tmpdir.join("test"))); | |
1120 | assert!(v == &bytes[..]); | |
1121 | ||
1122 | check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF])); | |
1123 | error_contains!( | |
1124 | fs::read_to_string(&tmpdir.join("not-utf8")), | |
1125 | "stream did not contain valid UTF-8" | |
1126 | ); | |
1127 | ||
1128 | let s = "𐁁𐀓𐀠𐀴𐀍"; | |
1129 | check!(fs::write(&tmpdir.join("utf8"), s.as_bytes())); | |
1130 | let string = check!(fs::read_to_string(&tmpdir.join("utf8"))); | |
1131 | assert_eq!(string, s); | |
1132 | } | |
1133 | ||
1134 | #[test] | |
1135 | fn file_try_clone() { | |
1136 | let tmpdir = tmpdir(); | |
1137 | ||
1138 | let mut f1 = | |
1139 | check!(OpenOptions::new().read(true).write(true).create(true).open(&tmpdir.join("test"))); | |
1140 | let mut f2 = check!(f1.try_clone()); | |
1141 | ||
1142 | check!(f1.write_all(b"hello world")); | |
1143 | check!(f1.seek(SeekFrom::Start(2))); | |
1144 | ||
1145 | let mut buf = vec![]; | |
1146 | check!(f2.read_to_end(&mut buf)); | |
1147 | assert_eq!(buf, b"llo world"); | |
1148 | drop(f2); | |
1149 | ||
1150 | check!(f1.write_all(b"!")); | |
1151 | } | |
1152 | ||
1153 | #[test] | |
1154 | #[cfg(not(windows))] | |
1155 | fn unlink_readonly() { | |
1156 | let tmpdir = tmpdir(); | |
1157 | let path = tmpdir.join("file"); | |
1158 | check!(File::create(&path)); | |
1159 | let mut perm = check!(fs::metadata(&path)).permissions(); | |
1160 | perm.set_readonly(true); | |
1161 | check!(fs::set_permissions(&path, perm)); | |
1162 | check!(fs::remove_file(&path)); | |
1163 | } | |
1164 | ||
1165 | #[test] | |
1166 | fn mkdir_trailing_slash() { | |
1167 | let tmpdir = tmpdir(); | |
1168 | let path = tmpdir.join("file"); | |
1169 | check!(fs::create_dir_all(&path.join("a/"))); | |
1170 | } | |
1171 | ||
1172 | #[test] | |
1173 | fn canonicalize_works_simple() { | |
1174 | let tmpdir = tmpdir(); | |
1175 | let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); | |
1176 | let file = tmpdir.join("test"); | |
1177 | File::create(&file).unwrap(); | |
1178 | assert_eq!(fs::canonicalize(&file).unwrap(), file); | |
1179 | } | |
1180 | ||
1181 | #[test] | |
1182 | fn realpath_works() { | |
1183 | let tmpdir = tmpdir(); | |
1184 | if !got_symlink_permission(&tmpdir) { | |
1185 | return; | |
1186 | }; | |
1187 | ||
1188 | let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); | |
1189 | let file = tmpdir.join("test"); | |
1190 | let dir = tmpdir.join("test2"); | |
1191 | let link = dir.join("link"); | |
1192 | let linkdir = tmpdir.join("test3"); | |
1193 | ||
1194 | File::create(&file).unwrap(); | |
1195 | fs::create_dir(&dir).unwrap(); | |
1196 | symlink_file(&file, &link).unwrap(); | |
1197 | symlink_dir(&dir, &linkdir).unwrap(); | |
1198 | ||
1199 | assert!(link.symlink_metadata().unwrap().file_type().is_symlink()); | |
1200 | ||
1201 | assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir); | |
1202 | assert_eq!(fs::canonicalize(&file).unwrap(), file); | |
1203 | assert_eq!(fs::canonicalize(&link).unwrap(), file); | |
1204 | assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir); | |
1205 | assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file); | |
1206 | } | |
1207 | ||
1208 | #[test] | |
1209 | fn realpath_works_tricky() { | |
1210 | let tmpdir = tmpdir(); | |
1211 | if !got_symlink_permission(&tmpdir) { | |
1212 | return; | |
1213 | }; | |
1214 | ||
1215 | let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); | |
1216 | let a = tmpdir.join("a"); | |
1217 | let b = a.join("b"); | |
1218 | let c = b.join("c"); | |
1219 | let d = a.join("d"); | |
1220 | let e = d.join("e"); | |
1221 | let f = a.join("f"); | |
1222 | ||
1223 | fs::create_dir_all(&b).unwrap(); | |
1224 | fs::create_dir_all(&d).unwrap(); | |
1225 | File::create(&f).unwrap(); | |
1226 | if cfg!(not(windows)) { | |
1227 | symlink_file("../d/e", &c).unwrap(); | |
1228 | symlink_file("../f", &e).unwrap(); | |
1229 | } | |
1230 | if cfg!(windows) { | |
1231 | symlink_file(r"..\d\e", &c).unwrap(); | |
1232 | symlink_file(r"..\f", &e).unwrap(); | |
1233 | } | |
1234 | ||
1235 | assert_eq!(fs::canonicalize(&c).unwrap(), f); | |
1236 | assert_eq!(fs::canonicalize(&e).unwrap(), f); | |
1237 | } | |
1238 | ||
1239 | #[test] | |
1240 | fn dir_entry_methods() { | |
1241 | let tmpdir = tmpdir(); | |
1242 | ||
1243 | fs::create_dir_all(&tmpdir.join("a")).unwrap(); | |
1244 | File::create(&tmpdir.join("b")).unwrap(); | |
1245 | ||
1246 | for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) { | |
1247 | let fname = file.file_name(); | |
1248 | match fname.to_str() { | |
1249 | Some("a") => { | |
1250 | assert!(file.file_type().unwrap().is_dir()); | |
1251 | assert!(file.metadata().unwrap().is_dir()); | |
1252 | } | |
1253 | Some("b") => { | |
1254 | assert!(file.file_type().unwrap().is_file()); | |
1255 | assert!(file.metadata().unwrap().is_file()); | |
1256 | } | |
1257 | f => panic!("unknown file name: {:?}", f), | |
1258 | } | |
1259 | } | |
1260 | } | |
1261 | ||
1262 | #[test] | |
1263 | fn dir_entry_debug() { | |
1264 | let tmpdir = tmpdir(); | |
1265 | File::create(&tmpdir.join("b")).unwrap(); | |
1266 | let mut read_dir = tmpdir.path().read_dir().unwrap(); | |
1267 | let dir_entry = read_dir.next().unwrap().unwrap(); | |
1268 | let actual = format!("{:?}", dir_entry); | |
1269 | let expected = format!("DirEntry({:?})", dir_entry.0.path()); | |
1270 | assert_eq!(actual, expected); | |
1271 | } | |
1272 | ||
1273 | #[test] | |
1274 | fn read_dir_not_found() { | |
1275 | let res = fs::read_dir("/path/that/does/not/exist"); | |
1276 | assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound); | |
1277 | } | |
1278 | ||
1279 | #[test] | |
1280 | fn create_dir_all_with_junctions() { | |
1281 | let tmpdir = tmpdir(); | |
1282 | let target = tmpdir.join("target"); | |
1283 | ||
1284 | let junction = tmpdir.join("junction"); | |
1285 | let b = junction.join("a/b"); | |
1286 | ||
1287 | let link = tmpdir.join("link"); | |
1288 | let d = link.join("c/d"); | |
1289 | ||
1290 | fs::create_dir(&target).unwrap(); | |
1291 | ||
1292 | check!(symlink_junction(&target, &junction)); | |
1293 | check!(fs::create_dir_all(&b)); | |
1294 | // the junction itself is not a directory, but `is_dir()` on a Path | |
1295 | // follows links | |
1296 | assert!(junction.is_dir()); | |
1297 | assert!(b.exists()); | |
1298 | ||
1299 | if !got_symlink_permission(&tmpdir) { | |
1300 | return; | |
1301 | }; | |
1302 | check!(symlink_dir(&target, &link)); | |
1303 | check!(fs::create_dir_all(&d)); | |
1304 | assert!(link.is_dir()); | |
1305 | assert!(d.exists()); | |
1306 | } | |
1307 | ||
1308 | #[test] | |
1309 | fn metadata_access_times() { | |
1310 | let tmpdir = tmpdir(); | |
1311 | ||
1312 | let b = tmpdir.join("b"); | |
1313 | File::create(&b).unwrap(); | |
1314 | ||
1315 | let a = check!(fs::metadata(&tmpdir.path())); | |
1316 | let b = check!(fs::metadata(&b)); | |
1317 | ||
1318 | assert_eq!(check!(a.accessed()), check!(a.accessed())); | |
1319 | assert_eq!(check!(a.modified()), check!(a.modified())); | |
1320 | assert_eq!(check!(b.accessed()), check!(b.modified())); | |
1321 | ||
1322 | if cfg!(target_os = "macos") || cfg!(target_os = "windows") { | |
1323 | check!(a.created()); | |
1324 | check!(b.created()); | |
1325 | } | |
1326 | ||
1327 | if cfg!(target_os = "linux") { | |
1328 | // Not always available | |
1329 | match (a.created(), b.created()) { | |
1330 | (Ok(t1), Ok(t2)) => assert!(t1 <= t2), | |
1331 | (Err(e1), Err(e2)) | |
cdc7bbd5 XL |
1332 | if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other |
1333 | || e1.kind() == ErrorKind::Unsupported | |
1334 | && e2.kind() == ErrorKind::Unsupported => {} | |
1b1a35ee XL |
1335 | (a, b) => { |
1336 | panic!("creation time must be always supported or not supported: {:?} {:?}", a, b,) | |
1337 | } | |
1338 | } | |
1339 | } | |
1340 | } | |
29967ef6 XL |
1341 | |
1342 | /// Test creating hard links to symlinks. | |
1343 | #[test] | |
1344 | fn symlink_hard_link() { | |
1345 | let tmpdir = tmpdir(); | |
fc512014 XL |
1346 | if !got_symlink_permission(&tmpdir) { |
1347 | return; | |
1348 | }; | |
29967ef6 XL |
1349 | |
1350 | // Create "file", a file. | |
1351 | check!(fs::File::create(tmpdir.join("file"))); | |
1352 | ||
1353 | // Create "symlink", a symlink to "file". | |
1354 | check!(symlink_file("file", tmpdir.join("symlink"))); | |
1355 | ||
1356 | // Create "hard_link", a hard link to "symlink". | |
1357 | check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link"))); | |
1358 | ||
1359 | // "hard_link" should appear as a symlink. | |
1360 | assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink()); | |
1361 | ||
1362 | // We sould be able to open "file" via any of the above names. | |
1363 | let _ = check!(fs::File::open(tmpdir.join("file"))); | |
1364 | assert!(fs::File::open(tmpdir.join("file.renamed")).is_err()); | |
1365 | let _ = check!(fs::File::open(tmpdir.join("symlink"))); | |
1366 | let _ = check!(fs::File::open(tmpdir.join("hard_link"))); | |
1367 | ||
1368 | // Rename "file" to "file.renamed". | |
1369 | check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed"))); | |
1370 | ||
1371 | // Now, the symlink and the hard link should be dangling. | |
1372 | assert!(fs::File::open(tmpdir.join("file")).is_err()); | |
1373 | let _ = check!(fs::File::open(tmpdir.join("file.renamed"))); | |
1374 | assert!(fs::File::open(tmpdir.join("symlink")).is_err()); | |
1375 | assert!(fs::File::open(tmpdir.join("hard_link")).is_err()); | |
1376 | ||
1377 | // The symlink and the hard link should both still point to "file". | |
1378 | assert!(fs::read_link(tmpdir.join("file")).is_err()); | |
1379 | assert!(fs::read_link(tmpdir.join("file.renamed")).is_err()); | |
1380 | assert_eq!(check!(fs::read_link(tmpdir.join("symlink"))), Path::new("file")); | |
1381 | assert_eq!(check!(fs::read_link(tmpdir.join("hard_link"))), Path::new("file")); | |
1382 | ||
1383 | // Remove "file.renamed". | |
1384 | check!(fs::remove_file(tmpdir.join("file.renamed"))); | |
1385 | ||
1386 | // Now, we can't open the file by any name. | |
1387 | assert!(fs::File::open(tmpdir.join("file")).is_err()); | |
1388 | assert!(fs::File::open(tmpdir.join("file.renamed")).is_err()); | |
1389 | assert!(fs::File::open(tmpdir.join("symlink")).is_err()); | |
1390 | assert!(fs::File::open(tmpdir.join("hard_link")).is_err()); | |
1391 | ||
1392 | // "hard_link" should still appear as a symlink. | |
1393 | assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink()); | |
1394 | } |