1 extern crate assert_cmd;
2 extern crate predicates;
3 
4 use assert_cmd::prelude::*;
5 use assert_fs::prelude::*;
6 use predicates::prelude::*;
7 use std::process::Command;
8 
9 #[cfg(unix)]
10 use std::os::unix::fs;
11 
12 #[test]
test_runs_okay()13 fn test_runs_okay() {
14     cmd().assert().success();
15 }
16 
17 #[test]
test_list_empty_directory()18 fn test_list_empty_directory() {
19     cmd()
20         .arg("--ignore-config")
21         .arg(tempdir().path())
22         .assert()
23         .stdout(predicate::eq(""));
24 }
25 
26 #[test]
test_list_almost_all_empty_directory()27 fn test_list_almost_all_empty_directory() {
28     let matched = "";
29     cmd()
30         .arg("--almost-all")
31         .arg("--ignore-config")
32         .arg(tempdir().path())
33         .assert()
34         .stdout(predicate::eq(matched));
35 
36     cmd()
37         .arg("-A")
38         .arg("--ignore-config")
39         .arg(tempdir().path())
40         .assert()
41         .stdout(predicate::eq(matched));
42 }
43 
44 #[test]
test_list_all_empty_directory()45 fn test_list_all_empty_directory() {
46     let matched = "\\.\n\\.\\.\n$";
47     cmd()
48         .arg("--all")
49         .arg("--ignore-config")
50         .arg(tempdir().path())
51         .assert()
52         .stdout(predicate::str::is_match(matched).unwrap());
53 
54     cmd()
55         .arg("-a")
56         .arg("--ignore-config")
57         .arg(tempdir().path())
58         .assert()
59         .stdout(predicate::str::is_match(matched).unwrap());
60 }
61 
62 #[test]
test_list_populated_directory()63 fn test_list_populated_directory() {
64     let dir = tempdir();
65     dir.child("one").touch().unwrap();
66     dir.child("two").touch().unwrap();
67     cmd()
68         .arg("--ignore-config")
69         .arg(dir.path())
70         .assert()
71         .stdout(predicate::str::is_match("one\ntwo\n$").unwrap());
72 }
73 
74 #[test]
test_list_almost_all_populated_directory()75 fn test_list_almost_all_populated_directory() {
76     let dir = tempdir();
77     dir.child("one").touch().unwrap();
78     dir.child("two").touch().unwrap();
79     cmd()
80         .arg("--almost-all")
81         .arg("--ignore-config")
82         .arg(dir.path())
83         .assert()
84         .stdout(predicate::str::is_match("one\ntwo\n$").unwrap());
85 }
86 
87 #[test]
test_list_all_populated_directory()88 fn test_list_all_populated_directory() {
89     let dir = tempdir();
90     dir.child("one").touch().unwrap();
91     dir.child("two").touch().unwrap();
92     cmd()
93         .arg("--all")
94         .arg("--ignore-config")
95         .arg(dir.path())
96         .assert()
97         .stdout(predicate::str::is_match("\\.\n\\.\\.\none\ntwo\n$").unwrap());
98 }
99 
100 #[test]
test_almost_sort_with_folder()101 fn test_almost_sort_with_folder() {
102     let tmp = tempdir();
103     tmp.child("z").create_dir_all().unwrap();
104     tmp.child("z/a").touch().unwrap();
105 
106     cmd()
107         .current_dir(tmp.path())
108         .arg("-a")
109         .arg("--ignore-config")
110         .arg("z")
111         .assert()
112         .stdout(predicate::str::is_match("\\.\n\\.\\.\na\n$").unwrap());
113 }
114 
115 #[test]
test_list_inode_populated_directory()116 fn test_list_inode_populated_directory() {
117     let dir = tempdir();
118     dir.child("one").touch().unwrap();
119     dir.child("two").touch().unwrap();
120 
121     #[cfg(windows)]
122     let matched = "- one\n\\- two\n$";
123     #[cfg(unix)]
124     let matched = "\\d+ +one\n\\d+ +two\n$";
125 
126     cmd()
127         .arg("--inode")
128         .arg("--ignore-config")
129         .arg(dir.path())
130         .assert()
131         .stdout(predicate::str::is_match(matched).unwrap());
132     cmd()
133         .arg("-i")
134         .arg("--ignore-config")
135         .arg(dir.path())
136         .assert()
137         .stdout(predicate::str::is_match(matched).unwrap());
138 }
139 
140 #[test]
test_list_block_inode_populated_directory_without_long()141 fn test_list_block_inode_populated_directory_without_long() {
142     let dir = tempdir();
143     dir.child("one").touch().unwrap();
144     dir.child("two").touch().unwrap();
145 
146     #[cfg(windows)]
147     let matched = "- one\n\\- two\n$";
148     #[cfg(unix)]
149     let matched = "\\d+ +one\n\\d+ +two\n$";
150 
151     cmd()
152         .arg("--blocks")
153         .arg("inode,name")
154         .arg("--ignore-config")
155         .arg(dir.path())
156         .assert()
157         .stdout(predicate::str::is_match(matched).unwrap());
158 }
159 
160 #[test]
test_list_block_inode_populated_directory_with_long()161 fn test_list_block_inode_populated_directory_with_long() {
162     let dir = tempdir();
163     dir.child("one").touch().unwrap();
164     dir.child("two").touch().unwrap();
165 
166     #[cfg(windows)]
167     let matched = "- one\n\\- two\n$";
168     #[cfg(unix)]
169     let matched = "\\d+ +one\n\\d+ +two\n$";
170 
171     cmd()
172         .arg("--long")
173         .arg("--blocks")
174         .arg("inode,name")
175         .arg("--ignore-config")
176         .arg(dir.path())
177         .assert()
178         .stdout(predicate::str::is_match(matched).unwrap());
179 }
180 
181 #[test]
test_list_inode_with_long_ok()182 fn test_list_inode_with_long_ok() {
183     let dir = tempdir();
184     cmd()
185         .arg("-i")
186         .arg("-l")
187         .arg("--ignore-config")
188         .arg(dir.path())
189         .assert()
190         .success();
191 }
192 
193 #[cfg(unix)]
194 #[test]
test_list_broken_link_ok()195 fn test_list_broken_link_ok() {
196     let dir = tempdir();
197     let broken_link = dir.path().join("broken-softlink");
198     let matched = "No such file or directory";
199     fs::symlink("not-existed-file", &broken_link).unwrap();
200 
201     cmd()
202         .arg(&broken_link)
203         .arg("--ignore-config")
204         .assert()
205         .stderr(predicate::str::contains(matched).not());
206 
207     cmd()
208         .arg("-l")
209         .arg("--ignore-config")
210         .arg(broken_link)
211         .assert()
212         .stderr(predicate::str::contains(matched).not());
213 }
214 #[cfg(unix)]
215 #[test]
test_nosymlink_on_non_long()216 fn test_nosymlink_on_non_long() {
217     let dir = tempdir();
218     dir.child("target").touch().unwrap();
219     let link = dir.path().join("link");
220     let link_icon = "⇒";
221     fs::symlink("target", &link).unwrap();
222 
223     cmd()
224         .arg("-l")
225         .arg("--ignore-config")
226         .arg(&link)
227         .assert()
228         .stdout(predicate::str::contains(link_icon));
229 
230     cmd()
231         .arg("--ignore-config")
232         .arg(&link)
233         .assert()
234         .stdout(predicate::str::contains(link_icon).not());
235 }
236 
237 #[cfg(unix)]
238 #[test]
test_dereference_link_right_type_and_no_link()239 fn test_dereference_link_right_type_and_no_link() {
240     let dir = tempdir();
241     dir.child("target").touch().unwrap();
242     let link = dir.path().join("link");
243     let file_type = ".rw";
244     let link_icon = "⇒";
245     fs::symlink("target", &link).unwrap();
246 
247     cmd()
248         .arg("-l")
249         .arg("--dereference")
250         .arg("--ignore-config")
251         .arg(&link)
252         .assert()
253         .stdout(predicate::str::starts_with(file_type))
254         .stdout(predicate::str::contains(link_icon).not());
255 
256     cmd()
257         .arg("-l")
258         .arg("-L")
259         .arg("--ignore-config")
260         .arg(link)
261         .assert()
262         .stdout(predicate::str::starts_with(file_type))
263         .stdout(predicate::str::contains(link_icon).not());
264 }
265 
266 #[cfg(unix)]
267 #[test]
test_show_folder_content_of_symlink()268 fn test_show_folder_content_of_symlink() {
269     let dir = tempdir();
270     dir.child("target").child("inside").touch().unwrap();
271     let link = dir.path().join("link");
272     fs::symlink("target", &link).unwrap();
273 
274     cmd()
275         .arg("--ignore-config")
276         .arg(link)
277         .assert()
278         .stdout(predicate::str::starts_with("link").not())
279         .stdout(predicate::str::starts_with("inside"));
280 }
281 
282 #[cfg(unix)]
283 #[test]
test_no_show_folder_content_of_symlink_for_long()284 fn test_no_show_folder_content_of_symlink_for_long() {
285     let dir = tempdir();
286     dir.child("target").child("inside").touch().unwrap();
287     let link = dir.path().join("link");
288     fs::symlink("target", &link).unwrap();
289 
290     cmd()
291         .arg("-l")
292         .arg("--ignore-config")
293         .arg(link)
294         .assert()
295         .stdout(predicate::str::starts_with("lrw"))
296         .stdout(predicate::str::contains("⇒"));
297 
298     cmd()
299         .arg("-l")
300         .arg("--ignore-config")
301         .arg(dir.path().join("link/"))
302         .assert()
303         .stdout(predicate::str::starts_with(".rw"))
304         .stdout(predicate::str::contains("⇒").not());
305 }
306 
307 #[cfg(unix)]
308 #[test]
test_show_folder_of_symlink_for_long_multi()309 fn test_show_folder_of_symlink_for_long_multi() {
310     let dir = tempdir();
311     dir.child("target").child("inside").touch().unwrap();
312     let link = dir.path().join("link");
313     fs::symlink("target", &link).unwrap();
314 
315     cmd()
316         .arg("-l")
317         .arg("--ignore-config")
318         .arg(dir.path().join("link/"))
319         .arg(dir.path().join("link"))
320         .assert()
321         .stdout(predicate::str::starts_with("lrw"))
322         .stdout(predicate::str::contains("link:").not()) // do not show dir content when no /
323         .stdout(predicate::str::contains("link/:"));
324 }
325 
326 #[test]
test_version_sort()327 fn test_version_sort() {
328     let dir = tempdir();
329     dir.child("0.3.7").touch().unwrap();
330     dir.child("0.11.5").touch().unwrap();
331     dir.child("11a").touch().unwrap();
332     dir.child("0.2").touch().unwrap();
333     dir.child("0.11").touch().unwrap();
334     dir.child("1").touch().unwrap();
335     dir.child("11").touch().unwrap();
336     dir.child("2").touch().unwrap();
337     dir.child("22").touch().unwrap();
338     cmd()
339         .arg("-v")
340         .arg("--ignore-config")
341         .arg(dir.path())
342         .assert()
343         .stdout(
344             predicate::str::is_match("0.2\n0.3.7\n0.11\n0.11.5\n1\n2\n11\n11a\n22\n$").unwrap(),
345         );
346 }
347 
348 #[test]
test_version_sort_overwrite_by_timesort()349 fn test_version_sort_overwrite_by_timesort() {
350     let dir = tempdir();
351     dir.child("2").touch().unwrap();
352     dir.child("11").touch().unwrap();
353     cmd()
354         .arg("-v")
355         .arg("-t")
356         .arg("--ignore-config")
357         .arg(dir.path())
358         .assert()
359         .stdout(predicate::str::is_match("11\n2\n$").unwrap());
360 }
361 
362 #[test]
test_version_sort_overwrite_by_sizesort()363 fn test_version_sort_overwrite_by_sizesort() {
364     use std::fs::File;
365     use std::io::Write;
366     let dir = tempdir();
367     dir.child("2").touch().unwrap();
368     let larger = dir.path().join("11");
369     let mut larger_file = File::create(larger).unwrap();
370     writeln!(larger_file, "this is larger").unwrap();
371     cmd()
372         .arg("-v")
373         .arg("-S")
374         .arg("--ignore-config")
375         .arg(dir.path())
376         .assert()
377         .stdout(predicate::str::is_match("11\n2\n$").unwrap());
378 }
379 
380 #[cfg(target_os = "linux")]
bad_utf8(tmp: &std::path::Path, pre: &str, suf: &str) -> String381 fn bad_utf8(tmp: &std::path::Path, pre: &str, suf: &str) -> String {
382     let mut fname = format!("{}/{}", tmp.display(), pre).into_bytes();
383     fname.reserve(2 + suf.len());
384     fname.push(0xa7);
385     fname.push(0xfd);
386     fname.extend(suf.as_bytes());
387     unsafe { String::from_utf8_unchecked(fname) }
388 }
389 
390 #[test]
391 #[cfg(target_os = "linux")]
test_bad_utf_8_extension()392 fn test_bad_utf_8_extension() {
393     use std::fs::File;
394     let tmp = tempdir();
395     let fname = bad_utf8(tmp.path(), "bad.extension", "");
396     File::create(fname).expect("failed to create file");
397 
398     cmd()
399         .arg(tmp.path())
400         .assert()
401         .stdout(predicate::str::is_match("bad.extension\u{fffd}\u{fffd}\n$").unwrap());
402 }
403 
404 #[test]
405 #[cfg(target_os = "linux")]
test_bad_utf_8_name()406 fn test_bad_utf_8_name() {
407     use std::fs::File;
408     let tmp = tempdir();
409     let fname = bad_utf8(tmp.path(), "bad-name", ".ext");
410     File::create(fname).expect("failed to create file");
411 
412     cmd()
413         .arg(tmp.path())
414         .assert()
415         .stdout(predicate::str::is_match("bad-name\u{fffd}\u{fffd}.ext\n$").unwrap());
416 }
417 
418 #[test]
test_tree()419 fn test_tree() {
420     let tmp = tempdir();
421     tmp.child("one").touch().unwrap();
422     tmp.child("one.d").create_dir_all().unwrap();
423     tmp.child("one.d/two").touch().unwrap();
424 
425     cmd()
426         .arg(tmp.path())
427         .arg("--tree")
428         .assert()
429         .stdout(predicate::str::is_match("├── one\n└── one.d\n   └── two\n$").unwrap());
430 }
431 
432 #[test]
test_tree_all_not_show_self()433 fn test_tree_all_not_show_self() {
434     let tmp = tempdir();
435     tmp.child("one").touch().unwrap();
436     tmp.child("one.d").create_dir_all().unwrap();
437     tmp.child("one.d/two").touch().unwrap();
438     tmp.child("one.d/.hidden").touch().unwrap();
439 
440     cmd()
441         .arg(tmp.path())
442         .arg("--tree")
443         .arg("--all")
444         .assert()
445         .stdout(
446             predicate::str::is_match("├── one\n└── one.d\n   ├── .hidden\n   └── two\n$").unwrap(),
447         );
448 }
449 
450 #[test]
test_tree_d()451 fn test_tree_d() {
452     let tmp = tempdir();
453     tmp.child("one").touch().unwrap();
454     tmp.child("two").touch().unwrap();
455     tmp.child("one.d").create_dir_all().unwrap();
456     tmp.child("one.d/one").touch().unwrap();
457     tmp.child("one.d/one.d").create_dir_all().unwrap();
458     tmp.child("two.d").create_dir_all().unwrap();
459 
460     cmd()
461         .arg(tmp.path())
462         .arg("--tree")
463         .arg("-d")
464         .assert()
465         .stdout(predicate::str::is_match("├── one.d\n│  └── one.d\n└── two.d\n$").unwrap());
466 }
467 
cmd() -> Command468 fn cmd() -> Command {
469     Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap()
470 }
471 
tempdir() -> assert_fs::TempDir472 fn tempdir() -> assert_fs::TempDir {
473     assert_fs::TempDir::new().unwrap()
474 }
475 
476 #[cfg(unix)]
477 #[test]
test_lower_case_name_icon_match()478 fn test_lower_case_name_icon_match() {
479     let dir = tempdir();
480     dir.child(".trash").touch().unwrap();
481     let test_file = dir.path().join(".trash");
482 
483     cmd()
484         .arg("--icon")
485         .arg("always")
486         .arg("--ignore-config")
487         .arg(test_file)
488         .assert()
489         .stdout(predicate::str::contains("\u{f1f8}"));
490 }
491 
492 #[cfg(unix)]
493 #[test]
test_upper_case_name_icon_match()494 fn test_upper_case_name_icon_match() {
495     let dir = tempdir();
496     dir.child(".TRASH").touch().unwrap();
497     let test_file = dir.path().join(".TRASH");
498 
499     cmd()
500         .arg("--icon")
501         .arg("always")
502         .arg("--ignore-config")
503         .arg(test_file)
504         .assert()
505         .stdout(predicate::str::contains("\u{f1f8}"));
506 }
507 
508 #[cfg(unix)]
509 #[test]
test_lower_case_ext_icon_match()510 fn test_lower_case_ext_icon_match() {
511     let dir = tempdir();
512     dir.child("test.7z").touch().unwrap();
513     let test_file = dir.path().join("test.7z");
514 
515     cmd()
516         .arg("--icon")
517         .arg("always")
518         .arg("--ignore-config")
519         .arg(test_file)
520         .assert()
521         .stdout(predicate::str::contains("\u{f410}"));
522 }
523 
524 #[cfg(unix)]
525 #[test]
test_upper_case_ext_icon_match()526 fn test_upper_case_ext_icon_match() {
527     let dir = tempdir();
528     dir.child("test.7Z").touch().unwrap();
529     let test_file = dir.path().join("test.7Z");
530 
531     cmd()
532         .arg("--icon")
533         .arg("always")
534         .arg("--ignore-config")
535         .arg(test_file)
536         .assert()
537         .stdout(predicate::str::contains("\u{f410}"));
538 }
539