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