diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs index 72db84d251d..62556279d59 100644 --- a/src/bin/cargo.rs +++ b/src/bin/cargo.rs @@ -69,6 +69,7 @@ macro_rules! each_subcommand{ $mac!(generate_lockfile); $mac!(git_checkout); $mac!(help); + $mac!(init); $mac!(install); $mac!(locate_project); $mac!(login); diff --git a/src/bin/init.rs b/src/bin/init.rs new file mode 100644 index 00000000000..c5e4cb66d47 --- /dev/null +++ b/src/bin/init.rs @@ -0,0 +1,53 @@ +use std::env; + +use cargo::ops; +use cargo::util::{CliResult, Config}; + +#[derive(RustcDecodable)] +struct Options { + flag_verbose: bool, + flag_quiet: bool, + flag_color: Option, + flag_bin: bool, + arg_path: Option, + flag_name: Option, + flag_vcs: Option, +} + +pub const USAGE: &'static str = " +Create a new cargo package in current directory + +Usage: + cargo init [options] [] + cargo init -h | --help + +Options: + -h, --help Print this message + --vcs VCS Initialize a new repository for the given version + control system (git or hg) or do not initialize any version + control at all (none) overriding a global configuration. + --bin Use a binary instead of a library template + --name NAME Set the resulting package name + -v, --verbose Use verbose output + -q, --quiet No output printed to stdout + --color WHEN Coloring: auto, always, never +"; + +pub fn execute(options: Options, config: &Config) -> CliResult> { + debug!("executing; cmd=cargo-init; args={:?}", env::args().collect::>()); + try!(config.shell().set_verbosity(options.flag_verbose, options.flag_quiet)); + try!(config.shell().set_color_config(options.flag_color.as_ref().map(|s| &s[..]))); + + let Options { flag_bin, arg_path, flag_name, flag_vcs, .. } = options; + + let opts = ops::NewOptions { + version_control: flag_vcs, + bin: flag_bin, + path: &arg_path.unwrap_or(format!(".")), + name: flag_name.as_ref().map(|s| s.as_ref()), + }; + + try!(ops::init(opts, config)); + Ok(None) +} + diff --git a/src/cargo/ops/cargo_new.rs b/src/cargo/ops/cargo_new.rs index 18cfeb88ff2..a855403d1ee 100644 --- a/src/cargo/ops/cargo_new.rs +++ b/src/cargo/ops/cargo_new.rs @@ -2,6 +2,7 @@ use std::env; use std::fs; use std::io::prelude::*; use std::path::Path; +use std::collections::BTreeMap; use rustc_serialize::{Decodable, Decoder}; @@ -24,6 +25,19 @@ pub struct NewOptions<'a> { pub name: Option<&'a str>, } +struct SourceFileInformation { + relative_path: String, + target_name: String, + bin: bool, +} + +struct MkOptions<'a> { + version_control: Option, + path: &'a Path, + name: &'a str, + source_files: Vec, +} + impl Decodable for VersionControl { fn decode(d: &mut D) -> Result { Ok(match &try!(d.read_str())[..] { @@ -44,38 +58,234 @@ struct CargoNewConfig { version_control: Option, } -pub fn new(opts: NewOptions, config: &Config) -> CargoResult<()> { - let path = config.cwd().join(opts.path); - if fs::metadata(&path).is_ok() { - bail!("destination `{}` already exists", path.display()) +fn get_name<'a>(path: &'a Path, opts: &'a NewOptions, config: &Config) -> CargoResult<&'a str> { + if let Some(name) = opts.name { + return Ok(name); } - let name = match opts.name { - Some(name) => name, - None => { - let dir_name = try!(path.file_name().and_then(|s| s.to_str()).chain_error(|| { - human(&format!("cannot create a project with a non-unicode name: {:?}", - path.file_name().unwrap())) - })); - if opts.bin { - dir_name - } else { - let new_name = strip_rust_affixes(dir_name); - if new_name != dir_name { - let message = format!( - "note: package will be named `{}`; use --name to override", - new_name); - try!(config.shell().say(&message, BLACK)); - } - new_name - } + + if path.file_name().is_none() { + bail!("cannot auto-detect project name from path {:?} ; use --name to override", + path.as_os_str()); + } + + let dir_name = try!(path.file_name().and_then(|s| s.to_str()).chain_error(|| { + human(&format!("cannot create a project with a non-unicode name: {:?}", + path.file_name().unwrap())) + })); + + if opts.bin { + Ok(dir_name) + } else { + let new_name = strip_rust_affixes(dir_name); + if new_name != dir_name { + let message = format!( + "note: package will be named `{}`; use --name to override", + new_name); + try!(config.shell().say(&message, BLACK)); } - }; + Ok(new_name) + } +} + +fn check_name(name: &str) -> CargoResult<()> { for c in name.chars() { if c.is_alphanumeric() { continue } if c == '_' || c == '-' { continue } - bail!("Invalid character `{}` in crate name: `{}`", c, name) + bail!("Invalid character `{}` in crate name: `{}`\n\ + use --name to override crate name", + c, name) + } + Ok(()) +} + +fn detect_source_paths_and_types(project_path : &Path, + project_name: &str, + detected_files: &mut Vec, + ) -> CargoResult<()> { + let path = project_path; + let name = project_name; + + enum H { + Bin, + Lib, + Detect, + } + + struct Test { + proposed_path: String, + handling: H, + } + + let tests = vec![ + Test { proposed_path: format!("src/main.rs"), handling: H::Bin }, + Test { proposed_path: format!("main.rs"), handling: H::Bin }, + Test { proposed_path: format!("src/{}.rs", name), handling: H::Detect }, + Test { proposed_path: format!("{}.rs", name), handling: H::Detect }, + Test { proposed_path: format!("src/lib.rs"), handling: H::Lib }, + Test { proposed_path: format!("lib.rs"), handling: H::Lib }, + ]; + + for i in tests { + let pp = i.proposed_path; + + // path/pp does not exist or is not a file + if !fs::metadata(&path.join(&pp)).map(|x| x.is_file()).unwrap_or(false) { + continue; + } + + let sfi = match i.handling { + H::Bin => { + SourceFileInformation { + relative_path: pp, + target_name: project_name.to_string(), + bin: true + } + } + H::Lib => { + SourceFileInformation { + relative_path: pp, + target_name: project_name.to_string(), + bin: false + } + } + H::Detect => { + let content = try!(paths::read(&path.join(pp.clone()))); + let isbin = content.contains("fn main"); + SourceFileInformation { + relative_path: pp, + target_name: project_name.to_string(), + bin: isbin + } + } + }; + detected_files.push(sfi); + } + + // Check for duplicate lib attempt + + let mut previous_lib_relpath : Option<&str> = None; + let mut duplicates_checker : BTreeMap<&str, &SourceFileInformation> = BTreeMap::new(); + + for i in detected_files { + if i.bin { + if let Some(x) = BTreeMap::get::(&duplicates_checker, i.target_name.as_ref()) { + bail!("\ +multiple possible binary sources found: + {} + {} +cannot automatically generate Cargo.toml as the main target would be ambiguous", + &x.relative_path, &i.relative_path); + } + duplicates_checker.insert(i.target_name.as_ref(), i); + } else { + if let Some(plp) = previous_lib_relpath { + return Err(human(format!("cannot have a project with \ + multiple libraries, \ + found both `{}` and `{}`", + plp, i.relative_path))); + } + previous_lib_relpath = Some(&i.relative_path); + } + } + + Ok(()) +} + +fn plan_new_source_file(bin: bool, project_name: String) -> SourceFileInformation { + if bin { + SourceFileInformation { + relative_path: "src/main.rs".to_string(), + target_name: project_name, + bin: true, + } + } else { + SourceFileInformation { + relative_path: "src/lib.rs".to_string(), + target_name: project_name, + bin: false, + } } - mk(config, &path, name, &opts).chain_error(|| { +} + +pub fn new(opts: NewOptions, config: &Config) -> CargoResult<()> { + let path = config.cwd().join(opts.path); + if fs::metadata(&path).is_ok() { + bail!("destination `{}` already exists", + path.display()) + } + + let name = try!(get_name(&path, &opts, config)); + try!(check_name(name)); + + let mkopts = MkOptions { + version_control: opts.version_control, + path: &path, + name: name, + source_files: vec![plan_new_source_file(opts.bin, name.to_string())], + }; + + mk(config, &mkopts).chain_error(|| { + human(format!("Failed to create project `{}` at `{}`", + name, path.display())) + }) +} + +pub fn init(opts: NewOptions, config: &Config) -> CargoResult<()> { + let path = config.cwd().join(opts.path); + + let cargotoml_path = path.join("Cargo.toml"); + if fs::metadata(&cargotoml_path).is_ok() { + bail!("`cargo init` cannot be run on existing Cargo projects") + } + + let name = try!(get_name(&path, &opts, config)); + try!(check_name(name)); + + let mut src_paths_types = vec![]; + + try!(detect_source_paths_and_types(&path, name, &mut src_paths_types)); + + if src_paths_types.len() == 0 { + src_paths_types.push(plan_new_source_file(opts.bin, name.to_string())); + } else { + // --bin option may be ignored if lib.rs or src/lib.rs present + // Maybe when doing `cargo init --bin` inside a library project stub, + // user may mean "initialize for library, but also add binary target" + } + + let mut version_control = opts.version_control; + + if version_control == None { + let mut num_detected_vsces = 0; + + if fs::metadata(&path.join(".git")).is_ok() { + version_control = Some(VersionControl::Git); + num_detected_vsces += 1; + } + + if fs::metadata(&path.join(".hg")).is_ok() { + version_control = Some(VersionControl::Hg); + num_detected_vsces += 1; + } + + // if none exists, maybe create git, like in `cargo new` + + if num_detected_vsces > 1 { + bail!("both .git and .hg directories found \ + and the ignore file can't be \ + filled in as a result, \ + specify --vcs to override detection"); + } + } + + let mkopts = MkOptions { + version_control: version_control, + path: &path, + name: name, + source_files: src_paths_types, + }; + + mk(config, &mkopts).chain_error(|| { human(format!("Failed to create project `{}` at `{}`", name, path.display())) }) @@ -99,14 +309,13 @@ fn existing_vcs_repo(path: &Path, cwd: &Path) -> bool { GitRepo::discover(path, cwd).is_ok() || HgRepo::discover(path, cwd).is_ok() } -fn mk(config: &Config, path: &Path, name: &str, - opts: &NewOptions) -> CargoResult<()> { +fn mk(config: &Config, opts: &MkOptions) -> CargoResult<()> { + let path = opts.path; + let name = opts.name; let cfg = try!(global_config(config)); let mut ignore = "target\n".to_string(); let in_existing_vcs_repo = existing_vcs_repo(path.parent().unwrap(), config.cwd()); - if !opts.bin { - ignore.push_str("Cargo.lock\n"); - } + ignore.push_str("Cargo.lock\n"); let vcs = match (opts.version_control, cfg.version_control, in_existing_vcs_repo) { (None, None, false) => VersionControl::Git, @@ -117,15 +326,19 @@ fn mk(config: &Config, path: &Path, name: &str, match vcs { VersionControl::Git => { - try!(GitRepo::init(path, config.cwd())); - try!(paths::write(&path.join(".gitignore"), ignore.as_bytes())); + if !fs::metadata(&path.join(".git")).is_ok() { + try!(GitRepo::init(path, config.cwd())); + } + try!(paths::append(&path.join(".gitignore"), ignore.as_bytes())); }, VersionControl::Hg => { - try!(HgRepo::init(path, config.cwd())); - try!(paths::write(&path.join(".hgignore"), ignore.as_bytes())); + if !fs::metadata(&path.join(".hg")).is_ok() { + try!(HgRepo::init(path, config.cwd())); + } + try!(paths::append(&path.join(".hgignore"), ignore.as_bytes())); }, VersionControl::NoVcs => { - try!(fs::create_dir(path)); + try!(fs::create_dir_all(path)); }, }; @@ -139,6 +352,32 @@ fn mk(config: &Config, path: &Path, name: &str, (Some(name), None, _, None) | (None, None, name, None) => name, }; + + let mut cargotoml_path_specifier = String::new(); + + // Calculare what [lib] and [[bin]]s do we need to append to Cargo.toml + + for i in &opts.source_files { + if i.bin { + if i.relative_path != "src/main.rs" { + cargotoml_path_specifier.push_str(&format!(r#" +[[bin]] +name = "{}" +path = {} +"#, i.target_name, toml::Value::String(i.relative_path.clone()))); + } + } else { + if i.relative_path != "src/lib.rs" { + cargotoml_path_specifier.push_str(&format!(r#" +[lib] +name = "{}" +path = {} +"#, i.target_name, toml::Value::String(i.relative_path.clone()))); + } + } + } + + // Create Cargo.toml file with necessary [lib] and [[bin]] sections, if needed try!(paths::write(&path.join("Cargo.toml"), format!( r#"[package] @@ -147,25 +386,40 @@ version = "0.1.0" authors = [{}] [dependencies] -"#, name, toml::Value::String(author)).as_bytes())); +{}"#, name, toml::Value::String(author), cargotoml_path_specifier).as_bytes())); - try!(fs::create_dir(&path.join("src"))); - if opts.bin { - try!(paths::write(&path.join("src/main.rs"), b"\ + // Create all specified source files + // (with respective parent directories) + // if they are don't exist + + for i in &opts.source_files { + let path_of_source_file = path.join(i.relative_path.clone()); + + if let Some(src_dir) = path_of_source_file.parent() { + try!(fs::create_dir_all(src_dir)); + } + + let default_file_content : &[u8] = if i.bin { + b"\ fn main() { println!(\"Hello, world!\"); } -")); - } else { - try!(paths::write(&path.join("src/lib.rs"), b"\ +" + } else { + b"\ #[cfg(test)] mod test { #[test] fn it_works() { } } -")); +" + }; + + if !fs::metadata(&path_of_source_file).map(|x| x.is_file()).unwrap_or(false) { + return paths::write(&path_of_source_file, default_file_content) + } } Ok(()) diff --git a/src/cargo/ops/mod.rs b/src/cargo/ops/mod.rs index c8924833aa9..806d3921a6e 100644 --- a/src/cargo/ops/mod.rs +++ b/src/cargo/ops/mod.rs @@ -8,7 +8,7 @@ pub use self::cargo_rustc::{BuildOutput, BuildConfig, TargetConfig}; pub use self::cargo_rustc::{CommandType, CommandPrototype, ExecEngine, ProcessEngine}; pub use self::cargo_run::run; pub use self::cargo_install::{install, install_list, uninstall}; -pub use self::cargo_new::{new, NewOptions, VersionControl}; +pub use self::cargo_new::{new, init, NewOptions, VersionControl}; pub use self::cargo_doc::{doc, DocOptions}; pub use self::cargo_generate_lockfile::{generate_lockfile}; pub use self::cargo_generate_lockfile::{update_lockfile}; diff --git a/src/cargo/util/paths.rs b/src/cargo/util/paths.rs index 1f08ea6dd7d..2e9397df6c4 100644 --- a/src/cargo/util/paths.rs +++ b/src/cargo/util/paths.rs @@ -1,6 +1,7 @@ use std::env; use std::ffi::{OsStr, OsString}; use std::fs::File; +use std::fs::OpenOptions; use std::io::prelude::*; use std::path::{Path, PathBuf, Component}; @@ -87,6 +88,21 @@ pub fn write(path: &Path, contents: &[u8]) -> CargoResult<()> { }) } +pub fn append(path: &Path, contents: &[u8]) -> CargoResult<()> { + (|| -> CargoResult<()> { + let mut f = try!(OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(path)); + + try!(f.write_all(contents)); + Ok(()) + }).chain_error(|| { + internal(format!("failed to write `{}`", path.display())) + }) +} + #[cfg(unix)] pub fn path2bytes(path: &Path) -> CargoResult<&[u8]> { use std::os::unix::prelude::*; diff --git a/tests/test_cargo_init.rs b/tests/test_cargo_init.rs new file mode 100644 index 00000000000..f36ea06e9a9 --- /dev/null +++ b/tests/test_cargo_init.rs @@ -0,0 +1,315 @@ +use std::fs::{self, File}; +use std::io::prelude::*; +use std::env; +use tempdir::TempDir; +use support::{execs, paths, cargo_dir}; +use hamcrest::{assert_that, existing_file, existing_dir, is_not}; + +use cargo::util::{process, ProcessBuilder}; + +fn setup() { +} + +fn cargo_process(s: &str) -> ProcessBuilder { + let mut p = process(&cargo_dir().join("cargo")); + p.arg(s).cwd(&paths::root()).env("HOME", &paths::home()); + return p; +} + +test!(simple_lib { + assert_that(cargo_process("init").arg("--vcs").arg("none") + .env("USER", "foo"), + execs().with_status(0)); + + assert_that(&paths::root().join("Cargo.toml"), existing_file()); + assert_that(&paths::root().join("src/lib.rs"), existing_file()); + assert_that(&paths::root().join(".gitignore"), is_not(existing_file())); + + assert_that(cargo_process("build"), + execs().with_status(0)); +}); + +test!(simple_bin { + let path = paths::root().join("foo"); + fs::create_dir(&path).unwrap(); + assert_that(cargo_process("init").arg("--bin").arg("--vcs").arg("none") + .env("USER", "foo").cwd(&path), + execs().with_status(0)); + + assert_that(&paths::root().join("foo/Cargo.toml"), existing_file()); + assert_that(&paths::root().join("foo/src/main.rs"), existing_file()); + + assert_that(cargo_process("build").cwd(&path), + execs().with_status(0)); + assert_that(&paths::root().join(&format!("foo/target/debug/foo{}", + env::consts::EXE_SUFFIX)), + existing_file()); +}); + +fn bin_already_exists(explicit: bool, rellocation: &str) { + let path = paths::root().join("foo"); + fs::create_dir_all(&path.join("src")).unwrap(); + + let sourcefile_path = path.join(rellocation); + + let content = br#" + fn main() { + println!("Hello, world 2!"); + } + "#; + + File::create(&sourcefile_path).unwrap().write_all(content).unwrap(); + + if explicit { + assert_that(cargo_process("init").arg("--bin").arg("--vcs").arg("none") + .env("USER", "foo").cwd(&path), + execs().with_status(0)); + } else { + assert_that(cargo_process("init").arg("--vcs").arg("none") + .env("USER", "foo").cwd(&path), + execs().with_status(0)); + } + + assert_that(&paths::root().join("foo/Cargo.toml"), existing_file()); + assert_that(&paths::root().join("foo/src/lib.rs"), is_not(existing_file())); + + // Check that our file is not overwritten + let mut new_content = Vec::new(); + File::open(&sourcefile_path).unwrap().read_to_end(&mut new_content).unwrap(); + assert_eq!(Vec::from(content as &[u8]), new_content); +} + +test!(bin_already_exists_explicit { + bin_already_exists(true, "src/main.rs") +}); + +test!(bin_already_exists_implicit { + bin_already_exists(false, "src/main.rs") +}); + +test!(bin_already_exists_explicit_nosrc { + bin_already_exists(true, "main.rs") +}); + +test!(bin_already_exists_implicit_nosrc { + bin_already_exists(false, "main.rs") +}); + +test!(bin_already_exists_implicit_namenosrc { + bin_already_exists(false, "foo.rs") +}); + +test!(bin_already_exists_implicit_namesrc { + bin_already_exists(false, "src/foo.rs") +}); + +test!(confused_by_multiple_lib_files { + let path = paths::root().join("foo"); + fs::create_dir_all(&path.join("src")).unwrap(); + + let sourcefile_path1 = path.join("src/lib.rs"); + + File::create(&sourcefile_path1).unwrap().write_all(br#" + fn qqq () { + println!("Hello, world 2!"); + } + "#).unwrap(); + + let sourcefile_path2 = path.join("lib.rs"); + + File::create(&sourcefile_path2).unwrap().write_all(br#" + fn qqq () { + println!("Hello, world 3!"); + } + "#).unwrap(); + + assert_that(cargo_process("init").arg("--vcs").arg("none") + .env("USER", "foo").cwd(&path), + execs().with_status(101).with_stderr("\ +cannot have a project with multiple libraries, found both `src/lib.rs` and `lib.rs` +")); + + assert_that(&paths::root().join("foo/Cargo.toml"), is_not(existing_file())); +}); + + +test!(multibin_project_name_clash { + let path = paths::root().join("foo"); + fs::create_dir(&path).unwrap(); + + let sourcefile_path1 = path.join("foo.rs"); + + File::create(&sourcefile_path1).unwrap().write_all(br#" + fn main () { + println!("Hello, world 2!"); + } + "#).unwrap(); + + let sourcefile_path2 = path.join("main.rs"); + + File::create(&sourcefile_path2).unwrap().write_all(br#" + fn main () { + println!("Hello, world 3!"); + } + "#).unwrap(); + + assert_that(cargo_process("init").arg("--vcs").arg("none") + .env("USER", "foo").cwd(&path), + execs().with_status(101).with_stderr("\ +multiple possible binary sources found: + main.rs + foo.rs +cannot automatically generate Cargo.toml as the main target would be ambiguous +")); + + assert_that(&paths::root().join("foo/Cargo.toml"), is_not(existing_file())); +}); + +fn lib_already_exists(rellocation: &str) { + let path = paths::root().join("foo"); + fs::create_dir_all(&path.join("src")).unwrap(); + + let sourcefile_path = path.join(rellocation); + + let content = br#" + pub fn qqq() {} + "#; + + File::create(&sourcefile_path).unwrap().write_all(content).unwrap(); + + assert_that(cargo_process("init").arg("--vcs").arg("none") + .env("USER", "foo").cwd(&path), + execs().with_status(0)); + + assert_that(&paths::root().join("foo/Cargo.toml"), existing_file()); + assert_that(&paths::root().join("foo/src/main.rs"), is_not(existing_file())); + + // Check that our file is not overwritten + let mut new_content = Vec::new(); + File::open(&sourcefile_path).unwrap().read_to_end(&mut new_content).unwrap(); + assert_eq!(Vec::from(content as &[u8]), new_content); +} + +test!(lib_already_exists_src { + lib_already_exists("src/lib.rs") +}); + +test!(lib_already_exists_nosrc { + lib_already_exists("lib.rs") +}); + +test!(simple_git { + assert_that(cargo_process("init").arg("--vcs").arg("git") + .env("USER", "foo"), + execs().with_status(0)); + + assert_that(&paths::root().join("Cargo.toml"), existing_file()); + assert_that(&paths::root().join("src/lib.rs"), existing_file()); + assert_that(&paths::root().join(".git"), existing_dir()); + assert_that(&paths::root().join(".gitignore"), existing_file()); +}); + +test!(auto_git { + let td = TempDir::new("cargo").unwrap(); + let foo = &td.path().join("foo"); + fs::create_dir_all(&foo).unwrap(); + assert_that(cargo_process("init").cwd(foo.clone()) + .env("USER", "foo"), + execs().with_status(0)); + + assert_that(&foo.join("Cargo.toml"), existing_file()); + assert_that(&foo.join("src/lib.rs"), existing_file()); + assert_that(&foo.join(".git"), existing_dir()); + assert_that(&foo.join(".gitignore"), existing_file()); +}); + +test!(invalid_dir_name { + let foo = &paths::root().join("foo.bar"); + fs::create_dir_all(&foo).unwrap(); + assert_that(cargo_process("init").cwd(foo.clone()) + .env("USER", "foo"), + execs().with_status(101).with_stderr("\ +Invalid character `.` in crate name: `foo.bar` +use --name to override crate name +")); + + assert_that(&foo.join("Cargo.toml"), is_not(existing_file())); +}); + +test!(git_autodetect { + fs::create_dir(&paths::root().join(".git")).unwrap(); + + assert_that(cargo_process("init") + .env("USER", "foo"), + execs().with_status(0)); + + + assert_that(&paths::root().join("Cargo.toml"), existing_file()); + assert_that(&paths::root().join("src/lib.rs"), existing_file()); + assert_that(&paths::root().join(".git"), existing_dir()); + assert_that(&paths::root().join(".gitignore"), existing_file()); +}); + + +test!(mercurial_autodetect { + fs::create_dir(&paths::root().join(".hg")).unwrap(); + + assert_that(cargo_process("init") + .env("USER", "foo"), + execs().with_status(0)); + + + assert_that(&paths::root().join("Cargo.toml"), existing_file()); + assert_that(&paths::root().join("src/lib.rs"), existing_file()); + assert_that(&paths::root().join(".git"), is_not(existing_dir())); + assert_that(&paths::root().join(".hgignore"), existing_file()); +}); + +test!(gitignore_appended_not_replaced { + fs::create_dir(&paths::root().join(".git")).unwrap(); + + File::create(&paths::root().join(".gitignore")).unwrap().write_all(b"qqqqqq\n").unwrap(); + + assert_that(cargo_process("init") + .env("USER", "foo"), + execs().with_status(0)); + + + assert_that(&paths::root().join("Cargo.toml"), existing_file()); + assert_that(&paths::root().join("src/lib.rs"), existing_file()); + assert_that(&paths::root().join(".git"), existing_dir()); + assert_that(&paths::root().join(".gitignore"), existing_file()); + + let mut contents = String::new(); + File::open(&paths::root().join(".gitignore")).unwrap().read_to_string(&mut contents).unwrap(); + assert!(contents.contains(r#"qqqqqq"#)); +}); + +test!(with_argument { + assert_that(cargo_process("init").arg("foo").arg("--vcs").arg("none") + .env("USER", "foo"), + execs().with_status(0)); + assert_that(&paths::root().join("foo/Cargo.toml"), existing_file()); +}); + + +test!(unknown_flags { + assert_that(cargo_process("init").arg("foo").arg("--flag"), + execs().with_status(1) + .with_stderr("\ +Unknown flag: '--flag' + +Usage: + cargo init [options] [] + cargo init -h | --help +")); +}); + +#[cfg(not(windows))] +test!(no_filename { + assert_that(cargo_process("init").arg("/"), + execs().with_status(101) + .with_stderr("\ +cannot auto-detect project name from path \"/\" ; use --name to override +")); +}); diff --git a/tests/test_cargo_new.rs b/tests/test_cargo_new.rs index 8c61d05ae27..63868112fcd 100644 --- a/tests/test_cargo_new.rs +++ b/tests/test_cargo_new.rs @@ -94,7 +94,9 @@ test!(existing { test!(invalid_characters { assert_that(cargo_process("new").arg("foo.rs"), execs().with_status(101) - .with_stderr("Invalid character `.` in crate name: `foo.rs`")); + .with_stderr("\ +Invalid character `.` in crate name: `foo.rs` +use --name to override crate name")); }); test!(rust_prefix_stripped { diff --git a/tests/tests.rs b/tests/tests.rs index 761fee61bb0..a435f91b860 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -49,6 +49,7 @@ mod test_cargo_features; mod test_cargo_fetch; mod test_cargo_freshness; mod test_cargo_generate_lockfile; +mod test_cargo_init; mod test_cargo_install; mod test_cargo_new; mod test_cargo_package;