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