1 //! Tests for the `cargo init` command.
2 
3 use cargo_test_support::{command_is_available, paths, Execs};
4 use std::env;
5 use std::fs;
6 use std::process::Command;
7 
cargo_process(s: &str) -> Execs8 fn cargo_process(s: &str) -> Execs {
9     let mut execs = cargo_test_support::cargo_process(s);
10     execs.cwd(&paths::root()).env("HOME", &paths::home());
11     execs
12 }
13 
mercurial_available() -> bool14 fn mercurial_available() -> bool {
15     let result = Command::new("hg")
16         .arg("--version")
17         .output()
18         .map(|o| o.status.success())
19         .unwrap_or(false);
20     if !result {
21         println!("`hg` not available, skipping test");
22     }
23     result
24 }
25 
26 #[cargo_test]
simple_lib()27 fn simple_lib() {
28     cargo_process("init --lib --vcs none --edition 2015")
29         .with_stderr("[CREATED] library package")
30         .run();
31 
32     assert!(paths::root().join("Cargo.toml").is_file());
33     assert!(paths::root().join("src/lib.rs").is_file());
34     assert!(!paths::root().join(".gitignore").is_file());
35 
36     cargo_process("build").run();
37 }
38 
39 #[cargo_test]
simple_bin()40 fn simple_bin() {
41     let path = paths::root().join("foo");
42     fs::create_dir(&path).unwrap();
43     cargo_process("init --bin --vcs none --edition 2015")
44         .cwd(&path)
45         .with_stderr("[CREATED] binary (application) package")
46         .run();
47 
48     assert!(paths::root().join("foo/Cargo.toml").is_file());
49     assert!(paths::root().join("foo/src/main.rs").is_file());
50 
51     cargo_process("build").cwd(&path).run();
52     assert!(paths::root()
53         .join(&format!("foo/target/debug/foo{}", env::consts::EXE_SUFFIX))
54         .is_file());
55 }
56 
57 #[cargo_test]
simple_git_ignore_exists()58 fn simple_git_ignore_exists() {
59     // write a .gitignore file with two entries
60     fs::create_dir_all(paths::root().join("foo")).unwrap();
61     fs::write(
62         paths::root().join("foo/.gitignore"),
63         "/target\n**/some.file",
64     )
65     .unwrap();
66 
67     cargo_process("init --lib foo --edition 2015").run();
68 
69     assert!(paths::root().is_dir());
70     assert!(paths::root().join("foo/Cargo.toml").is_file());
71     assert!(paths::root().join("foo/src/lib.rs").is_file());
72     assert!(paths::root().join("foo/.git").is_dir());
73     assert!(paths::root().join("foo/.gitignore").is_file());
74 
75     let fp = paths::root().join("foo/.gitignore");
76     let contents = fs::read_to_string(fp).unwrap();
77     assert_eq!(
78         contents,
79         "/target\n\
80          **/some.file\n\n\
81          # Added by cargo\n\
82          #\n\
83          # already existing elements were commented out\n\
84          \n\
85          #/target\n\
86          Cargo.lock\n",
87     );
88 
89     cargo_process("build").cwd(&paths::root().join("foo")).run();
90 }
91 
92 #[cargo_test]
git_ignore_exists_no_conflicting_entries()93 fn git_ignore_exists_no_conflicting_entries() {
94     // write a .gitignore file with one entry
95     fs::create_dir_all(paths::root().join("foo")).unwrap();
96     fs::write(paths::root().join("foo/.gitignore"), "**/some.file").unwrap();
97 
98     cargo_process("init --lib foo --edition 2015").run();
99 
100     let fp = paths::root().join("foo/.gitignore");
101     let contents = fs::read_to_string(&fp).unwrap();
102     assert_eq!(
103         contents,
104         "**/some.file\n\n\
105          # Added by cargo\n\
106          \n\
107          /target\n\
108          Cargo.lock\n",
109     );
110 }
111 
112 #[cargo_test]
both_lib_and_bin()113 fn both_lib_and_bin() {
114     cargo_process("init --lib --bin")
115         .with_status(101)
116         .with_stderr("[ERROR] can't specify both lib and binary outputs")
117         .run();
118 }
119 
bin_already_exists(explicit: bool, rellocation: &str)120 fn bin_already_exists(explicit: bool, rellocation: &str) {
121     let path = paths::root().join("foo");
122     fs::create_dir_all(&path.join("src")).unwrap();
123 
124     let sourcefile_path = path.join(rellocation);
125 
126     let content = r#"
127         fn main() {
128             println!("Hello, world 2!");
129         }
130     "#;
131 
132     fs::write(&sourcefile_path, content).unwrap();
133 
134     if explicit {
135         cargo_process("init --bin --vcs none").cwd(&path).run();
136     } else {
137         cargo_process("init --vcs none").cwd(&path).run();
138     }
139 
140     assert!(paths::root().join("foo/Cargo.toml").is_file());
141     assert!(!paths::root().join("foo/src/lib.rs").is_file());
142 
143     // Check that our file is not overwritten
144     let new_content = fs::read_to_string(&sourcefile_path).unwrap();
145     assert_eq!(content, new_content);
146 }
147 
148 #[cargo_test]
bin_already_exists_explicit()149 fn bin_already_exists_explicit() {
150     bin_already_exists(true, "src/main.rs")
151 }
152 
153 #[cargo_test]
bin_already_exists_implicit()154 fn bin_already_exists_implicit() {
155     bin_already_exists(false, "src/main.rs")
156 }
157 
158 #[cargo_test]
bin_already_exists_explicit_nosrc()159 fn bin_already_exists_explicit_nosrc() {
160     bin_already_exists(true, "main.rs")
161 }
162 
163 #[cargo_test]
bin_already_exists_implicit_nosrc()164 fn bin_already_exists_implicit_nosrc() {
165     bin_already_exists(false, "main.rs")
166 }
167 
168 #[cargo_test]
bin_already_exists_implicit_namenosrc()169 fn bin_already_exists_implicit_namenosrc() {
170     bin_already_exists(false, "foo.rs")
171 }
172 
173 #[cargo_test]
bin_already_exists_implicit_namesrc()174 fn bin_already_exists_implicit_namesrc() {
175     bin_already_exists(false, "src/foo.rs")
176 }
177 
178 #[cargo_test]
confused_by_multiple_lib_files()179 fn confused_by_multiple_lib_files() {
180     let path = paths::root().join("foo");
181     fs::create_dir_all(&path.join("src")).unwrap();
182 
183     let path1 = path.join("src/lib.rs");
184     fs::write(path1, r#"fn qqq () { println!("Hello, world 2!"); }"#).unwrap();
185 
186     let path2 = path.join("lib.rs");
187     fs::write(path2, r#" fn qqq () { println!("Hello, world 3!"); }"#).unwrap();
188 
189     cargo_process("init --vcs none")
190         .cwd(&path)
191         .with_status(101)
192         .with_stderr(
193             "[ERROR] cannot have a package with multiple libraries, \
194             found both `src/lib.rs` and `lib.rs`",
195         )
196         .run();
197 
198     assert!(!paths::root().join("foo/Cargo.toml").is_file());
199 }
200 
201 #[cargo_test]
multibin_project_name_clash()202 fn multibin_project_name_clash() {
203     let path = paths::root().join("foo");
204     fs::create_dir(&path).unwrap();
205 
206     let path1 = path.join("foo.rs");
207     fs::write(path1, r#"fn main () { println!("Hello, world 2!"); }"#).unwrap();
208 
209     let path2 = path.join("main.rs");
210     fs::write(path2, r#"fn main () { println!("Hello, world 3!"); }"#).unwrap();
211 
212     cargo_process("init --lib --vcs none")
213         .cwd(&path)
214         .with_status(101)
215         .with_stderr(
216             "\
217 [ERROR] multiple possible binary sources found:
218   main.rs
219   foo.rs
220 cannot automatically generate Cargo.toml as the main target would be ambiguous
221 ",
222         )
223         .run();
224 
225     assert!(!paths::root().join("foo/Cargo.toml").is_file());
226 }
227 
lib_already_exists(rellocation: &str)228 fn lib_already_exists(rellocation: &str) {
229     let path = paths::root().join("foo");
230     fs::create_dir_all(&path.join("src")).unwrap();
231 
232     let sourcefile_path = path.join(rellocation);
233 
234     let content = "pub fn qqq() {}";
235     fs::write(&sourcefile_path, content).unwrap();
236 
237     cargo_process("init --vcs none").cwd(&path).run();
238 
239     assert!(paths::root().join("foo/Cargo.toml").is_file());
240     assert!(!paths::root().join("foo/src/main.rs").is_file());
241 
242     // Check that our file is not overwritten
243     let new_content = fs::read_to_string(&sourcefile_path).unwrap();
244     assert_eq!(content, new_content);
245 }
246 
247 #[cargo_test]
lib_already_exists_src()248 fn lib_already_exists_src() {
249     lib_already_exists("src/lib.rs");
250 }
251 
252 #[cargo_test]
lib_already_exists_nosrc()253 fn lib_already_exists_nosrc() {
254     lib_already_exists("lib.rs");
255 }
256 
257 #[cargo_test]
simple_git()258 fn simple_git() {
259     cargo_process("init --lib --vcs git").run();
260 
261     assert!(paths::root().join("Cargo.toml").is_file());
262     assert!(paths::root().join("src/lib.rs").is_file());
263     assert!(paths::root().join(".git").is_dir());
264     assert!(paths::root().join(".gitignore").is_file());
265 }
266 
267 #[cargo_test]
auto_git()268 fn auto_git() {
269     cargo_process("init --lib").run();
270 
271     assert!(paths::root().join("Cargo.toml").is_file());
272     assert!(paths::root().join("src/lib.rs").is_file());
273     assert!(paths::root().join(".git").is_dir());
274     assert!(paths::root().join(".gitignore").is_file());
275 }
276 
277 #[cargo_test]
invalid_dir_name()278 fn invalid_dir_name() {
279     let foo = &paths::root().join("foo.bar");
280     fs::create_dir_all(&foo).unwrap();
281     cargo_process("init")
282         .cwd(foo.clone())
283         .with_status(101)
284         .with_stderr(
285             "\
286 [ERROR] invalid character `.` in package name: `foo.bar`, [..]
287 If you need a package name to not match the directory name, consider using --name flag.
288 If you need a binary with the name \"foo.bar\", use a valid package name, \
289 and set the binary name to be different from the package. \
290 This can be done by setting the binary filename to `src/bin/foo.bar.rs` \
291 or change the name in Cargo.toml with:
292 
293     [[bin]]
294     name = \"foo.bar\"
295     path = \"src/main.rs\"
296 
297 ",
298         )
299         .run();
300 
301     assert!(!foo.join("Cargo.toml").is_file());
302 }
303 
304 #[cargo_test]
reserved_name()305 fn reserved_name() {
306     let test = &paths::root().join("test");
307     fs::create_dir_all(&test).unwrap();
308     cargo_process("init")
309         .cwd(test.clone())
310         .with_status(101)
311         .with_stderr(
312             "\
313 [ERROR] the name `test` cannot be used as a package name, it conflicts [..]\n\
314 If you need a package name to not match the directory name, consider using --name flag.
315 If you need a binary with the name \"test\", use a valid package name, \
316 and set the binary name to be different from the package. \
317 This can be done by setting the binary filename to `src/bin/test.rs` \
318 or change the name in Cargo.toml with:
319 
320     [[bin]]
321     name = \"test\"
322     path = \"src/main.rs\"
323 
324 ",
325         )
326         .run();
327 
328     assert!(!test.join("Cargo.toml").is_file());
329 }
330 
331 #[cargo_test]
git_autodetect()332 fn git_autodetect() {
333     fs::create_dir(&paths::root().join(".git")).unwrap();
334 
335     cargo_process("init --lib").run();
336 
337     assert!(paths::root().join("Cargo.toml").is_file());
338     assert!(paths::root().join("src/lib.rs").is_file());
339     assert!(paths::root().join(".git").is_dir());
340     assert!(paths::root().join(".gitignore").is_file());
341 }
342 
343 #[cargo_test]
mercurial_autodetect()344 fn mercurial_autodetect() {
345     fs::create_dir(&paths::root().join(".hg")).unwrap();
346 
347     cargo_process("init --lib").run();
348 
349     assert!(paths::root().join("Cargo.toml").is_file());
350     assert!(paths::root().join("src/lib.rs").is_file());
351     assert!(!paths::root().join(".git").is_dir());
352     assert!(paths::root().join(".hgignore").is_file());
353 }
354 
355 #[cargo_test]
gitignore_appended_not_replaced()356 fn gitignore_appended_not_replaced() {
357     fs::create_dir(&paths::root().join(".git")).unwrap();
358 
359     fs::write(&paths::root().join(".gitignore"), "qqqqqq\n").unwrap();
360 
361     cargo_process("init --lib").run();
362 
363     assert!(paths::root().join("Cargo.toml").is_file());
364     assert!(paths::root().join("src/lib.rs").is_file());
365     assert!(paths::root().join(".git").is_dir());
366     assert!(paths::root().join(".gitignore").is_file());
367 
368     let contents = fs::read_to_string(&paths::root().join(".gitignore")).unwrap();
369     assert!(contents.contains("qqqqqq"));
370 }
371 
372 #[cargo_test]
gitignore_added_newline_in_existing()373 fn gitignore_added_newline_in_existing() {
374     fs::create_dir(&paths::root().join(".git")).unwrap();
375 
376     fs::write(&paths::root().join(".gitignore"), "first").unwrap();
377 
378     cargo_process("init --lib").run();
379 
380     assert!(paths::root().join(".gitignore").is_file());
381 
382     let contents = fs::read_to_string(&paths::root().join(".gitignore")).unwrap();
383     assert!(contents.starts_with("first\n"));
384 }
385 
386 #[cargo_test]
gitignore_no_newline_in_new()387 fn gitignore_no_newline_in_new() {
388     fs::create_dir(&paths::root().join(".git")).unwrap();
389 
390     cargo_process("init --lib").run();
391 
392     assert!(paths::root().join(".gitignore").is_file());
393 
394     let contents = fs::read_to_string(&paths::root().join(".gitignore")).unwrap();
395     assert!(!contents.starts_with('\n'));
396 }
397 
398 #[cargo_test]
mercurial_added_newline_in_existing()399 fn mercurial_added_newline_in_existing() {
400     fs::create_dir(&paths::root().join(".hg")).unwrap();
401 
402     fs::write(&paths::root().join(".hgignore"), "first").unwrap();
403 
404     cargo_process("init --lib").run();
405 
406     assert!(paths::root().join(".hgignore").is_file());
407 
408     let contents = fs::read_to_string(&paths::root().join(".hgignore")).unwrap();
409     assert!(contents.starts_with("first\n"));
410 }
411 
412 #[cargo_test]
mercurial_no_newline_in_new()413 fn mercurial_no_newline_in_new() {
414     fs::create_dir(&paths::root().join(".hg")).unwrap();
415 
416     cargo_process("init --lib").run();
417 
418     assert!(paths::root().join(".hgignore").is_file());
419 
420     let contents = fs::read_to_string(&paths::root().join(".hgignore")).unwrap();
421     assert!(!contents.starts_with('\n'));
422 }
423 
424 #[cargo_test]
terminating_newline_in_new_git_ignore()425 fn terminating_newline_in_new_git_ignore() {
426     cargo_process("init --vcs git --lib").run();
427 
428     let content = fs::read_to_string(&paths::root().join(".gitignore")).unwrap();
429 
430     let mut last_chars = content.chars().rev();
431     assert_eq!(last_chars.next(), Some('\n'));
432     assert_ne!(last_chars.next(), Some('\n'));
433 }
434 
435 #[cargo_test]
terminating_newline_in_new_mercurial_ignore()436 fn terminating_newline_in_new_mercurial_ignore() {
437     if !mercurial_available() {
438         return;
439     }
440     cargo_process("init --vcs hg --lib").run();
441 
442     let content = fs::read_to_string(&paths::root().join(".hgignore")).unwrap();
443 
444     let mut last_chars = content.chars().rev();
445     assert_eq!(last_chars.next(), Some('\n'));
446     assert_ne!(last_chars.next(), Some('\n'));
447 }
448 
449 #[cargo_test]
terminating_newline_in_existing_git_ignore()450 fn terminating_newline_in_existing_git_ignore() {
451     fs::create_dir(&paths::root().join(".git")).unwrap();
452     fs::write(&paths::root().join(".gitignore"), b"first").unwrap();
453 
454     cargo_process("init --lib").run();
455 
456     let content = fs::read_to_string(&paths::root().join(".gitignore")).unwrap();
457 
458     let mut last_chars = content.chars().rev();
459     assert_eq!(last_chars.next(), Some('\n'));
460     assert_ne!(last_chars.next(), Some('\n'));
461 }
462 
463 #[cargo_test]
terminating_newline_in_existing_mercurial_ignore()464 fn terminating_newline_in_existing_mercurial_ignore() {
465     fs::create_dir(&paths::root().join(".hg")).unwrap();
466     fs::write(&paths::root().join(".hgignore"), b"first").unwrap();
467 
468     cargo_process("init --lib").run();
469 
470     let content = fs::read_to_string(&paths::root().join(".hgignore")).unwrap();
471 
472     let mut last_chars = content.chars().rev();
473     assert_eq!(last_chars.next(), Some('\n'));
474     assert_ne!(last_chars.next(), Some('\n'));
475 }
476 
477 #[cargo_test]
cargo_lock_gitignored_if_lib1()478 fn cargo_lock_gitignored_if_lib1() {
479     fs::create_dir(&paths::root().join(".git")).unwrap();
480 
481     cargo_process("init --lib --vcs git").run();
482 
483     assert!(paths::root().join(".gitignore").is_file());
484 
485     let contents = fs::read_to_string(&paths::root().join(".gitignore")).unwrap();
486     assert!(contents.contains(r#"Cargo.lock"#));
487 }
488 
489 #[cargo_test]
cargo_lock_gitignored_if_lib2()490 fn cargo_lock_gitignored_if_lib2() {
491     fs::create_dir(&paths::root().join(".git")).unwrap();
492 
493     fs::write(&paths::root().join("lib.rs"), "").unwrap();
494 
495     cargo_process("init --vcs git").run();
496 
497     assert!(paths::root().join(".gitignore").is_file());
498 
499     let contents = fs::read_to_string(&paths::root().join(".gitignore")).unwrap();
500     assert!(contents.contains(r#"Cargo.lock"#));
501 }
502 
503 #[cargo_test]
cargo_lock_not_gitignored_if_bin1()504 fn cargo_lock_not_gitignored_if_bin1() {
505     fs::create_dir(&paths::root().join(".git")).unwrap();
506 
507     cargo_process("init --vcs git --bin").run();
508 
509     assert!(paths::root().join(".gitignore").is_file());
510 
511     let contents = fs::read_to_string(&paths::root().join(".gitignore")).unwrap();
512     assert!(!contents.contains(r#"Cargo.lock"#));
513 }
514 
515 #[cargo_test]
cargo_lock_not_gitignored_if_bin2()516 fn cargo_lock_not_gitignored_if_bin2() {
517     fs::create_dir(&paths::root().join(".git")).unwrap();
518 
519     fs::write(&paths::root().join("main.rs"), "").unwrap();
520 
521     cargo_process("init --vcs git").run();
522 
523     assert!(paths::root().join(".gitignore").is_file());
524 
525     let contents = fs::read_to_string(&paths::root().join(".gitignore")).unwrap();
526     assert!(!contents.contains(r#"Cargo.lock"#));
527 }
528 
529 #[cargo_test]
with_argument()530 fn with_argument() {
531     cargo_process("init foo --vcs none").run();
532     assert!(paths::root().join("foo/Cargo.toml").is_file());
533 }
534 
535 #[cargo_test]
unknown_flags()536 fn unknown_flags() {
537     cargo_process("init foo --flag")
538         .with_status(1)
539         .with_stderr_contains(
540             "error: Found argument '--flag' which wasn't expected, or isn't valid in this context",
541         )
542         .run();
543 }
544 
545 #[cfg(not(windows))]
546 #[cargo_test]
no_filename()547 fn no_filename() {
548     cargo_process("init /")
549         .with_status(101)
550         .with_stderr(
551             "[ERROR] cannot auto-detect package name from path \"/\" ; use --name to override"
552                 .to_string(),
553         )
554         .run();
555 }
556 
557 #[cargo_test]
formats_source()558 fn formats_source() {
559     if !command_is_available("rustfmt") {
560         return;
561     }
562 
563     fs::write(&paths::root().join("rustfmt.toml"), "tab_spaces = 2").unwrap();
564 
565     cargo_process("init --lib")
566         .with_stderr("[CREATED] library package")
567         .run();
568 
569     assert_eq!(
570         fs::read_to_string(paths::root().join("src/lib.rs")).unwrap(),
571         r#"#[cfg(test)]
572 mod tests {
573   #[test]
574   fn it_works() {
575     let result = 2 + 2;
576     assert_eq!(result, 4);
577   }
578 }
579 "#
580     );
581 }
582 
583 #[cargo_test]
ignores_failure_to_format_source()584 fn ignores_failure_to_format_source() {
585     cargo_process("init --lib")
586         .env("PATH", "") // pretend that `rustfmt` is missing
587         .with_stderr("[CREATED] library package")
588         .run();
589 
590     assert_eq!(
591         fs::read_to_string(paths::root().join("src/lib.rs")).unwrap(),
592         r#"#[cfg(test)]
593 mod tests {
594     #[test]
595     fn it_works() {
596         let result = 2 + 2;
597         assert_eq!(result, 4);
598     }
599 }
600 "#
601     );
602 }
603 
604 #[cargo_test]
creates_binary_when_instructed_and_has_lib_file_no_warning()605 fn creates_binary_when_instructed_and_has_lib_file_no_warning() {
606     let path = paths::root().join("foo");
607     fs::create_dir(&path).unwrap();
608     fs::write(path.join("foo.rs"), "fn not_main() {}").unwrap();
609     cargo_process("init --bin")
610         .cwd(&path)
611         .with_stderr(
612             "\
613 [WARNING] file `foo.rs` seems to be a library file
614 [CREATED] binary (application) package
615 ",
616         )
617         .run();
618 
619     let cargo_toml = fs::read_to_string(path.join("Cargo.toml")).unwrap();
620     assert!(cargo_toml.contains("[[bin]]"));
621     assert!(!cargo_toml.contains("[lib]"));
622 }
623 
624 #[cargo_test]
creates_library_when_instructed_and_has_bin_file()625 fn creates_library_when_instructed_and_has_bin_file() {
626     let path = paths::root().join("foo");
627     fs::create_dir(&path).unwrap();
628     fs::write(path.join("foo.rs"), "fn main() {}").unwrap();
629     cargo_process("init --lib")
630         .cwd(&path)
631         .with_stderr(
632             "\
633 [WARNING] file `foo.rs` seems to be a binary (application) file
634 [CREATED] library package
635 ",
636         )
637         .run();
638 
639     let cargo_toml = fs::read_to_string(path.join("Cargo.toml")).unwrap();
640     assert!(!cargo_toml.contains("[[bin]]"));
641     assert!(cargo_toml.contains("[lib]"));
642 }
643 
644 #[cargo_test]
creates_binary_when_both_binlib_present()645 fn creates_binary_when_both_binlib_present() {
646     let path = paths::root().join("foo");
647     fs::create_dir(&path).unwrap();
648     fs::write(path.join("foo.rs"), "fn main() {}").unwrap();
649     fs::write(path.join("lib.rs"), "fn notmain() {}").unwrap();
650     cargo_process("init --bin")
651         .cwd(&path)
652         .with_stderr("[CREATED] binary (application) package")
653         .run();
654 
655     let cargo_toml = fs::read_to_string(path.join("Cargo.toml")).unwrap();
656     assert!(cargo_toml.contains("[[bin]]"));
657     assert!(cargo_toml.contains("[lib]"));
658 }
659 
660 #[cargo_test]
cant_create_library_when_both_binlib_present()661 fn cant_create_library_when_both_binlib_present() {
662     let path = paths::root().join("foo");
663     fs::create_dir(&path).unwrap();
664     fs::write(path.join("foo.rs"), "fn main() {}").unwrap();
665     fs::write(path.join("lib.rs"), "fn notmain() {}").unwrap();
666     cargo_process("init --lib")
667         .cwd(&path)
668         .with_status(101)
669         .with_stderr(
670             "[ERROR] cannot have a package with multiple libraries, found both `foo.rs` and `lib.rs`"
671             )
672         .run();
673 }
674