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