diff --git a/.gitignore b/.gitignore index 3b23b477f2b..9fabb9698d8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ # Generated by Cargo /target/ +/clap-test/target/ # Cargo files Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index 92e676553af..4660957ff85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ clippy = { version = "=0.0.64", optional = true } unicode-width = { version = "~0.1.3", optional = true } [dev-dependencies] -regex = "~0.1.69" +clap-test = { path = "clap-test/" } [features] default = ["suggestions", "color", "wrap_help"] diff --git a/clap-test/Cargo.toml b/clap-test/Cargo.toml new file mode 100644 index 00000000000..2f7e9c864c8 --- /dev/null +++ b/clap-test/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "clap-test" +version = "0.1.0" +authors = ["Kevin K. "] +description = "functions and macros to assist in testing clap" +license = "MIT" + +[dependencies] +regex = "~0.1.69" +clap = { path = "../" } diff --git a/clap-test/src/lib.rs b/clap-test/src/lib.rs new file mode 100644 index 00000000000..369784f8326 --- /dev/null +++ b/clap-test/src/lib.rs @@ -0,0 +1,164 @@ +extern crate clap; +extern crate regex; + +use std::str; +use std::io::Write; + +use regex::Regex; + +use clap::{App, Arg, SubCommand}; + +pub fn check_err_output(a: App, args: &str, out: &str, use_stderr: bool) { + let res = a.get_matches_from_safe(args.split(' ').collect::>()); + let re = Regex::new("\x1b[^m]*m").unwrap(); + + let mut w = vec![]; + let err = res.unwrap_err(); + err.write_to(&mut w).unwrap(); + let err_s = str::from_utf8(&w).unwrap(); + assert_eq!(re.replace_all(err_s, ""), out); + assert_eq!(use_stderr, err.use_stderr()); +} + +pub fn check_help(mut a: App, out: &str) { + // We call a get_matches method to cause --help and --version to be built + let _ = a.get_matches_from_safe_borrow(vec![""]); + + // Now we check the output of print_help() + let mut help = vec![]; + a.write_help(&mut help).ok().expect("failed to print help"); + assert_eq!(str::from_utf8(&help).unwrap(), out); +} + +pub fn complex_app() -> App<'static, 'static> { + let args = "-o --option=[opt]... 'tests options' + [positional] 'tests positionals'"; + let opt3_vals = ["fast", "slow"]; + let pos3_vals = ["vi", "emacs"]; + App::new("clap-test") + .version("v1.4.8") + .about("tests clap library") + .author("Kevin K. ") + .args_from_usage(args) + .arg(Arg::from_usage("-f --flag... 'tests flags'") + .global(true)) + .args(&[ + Arg::from_usage("[flag2] -F 'tests flags with exclusions'").conflicts_with("flag").requires("long-option-2"), + Arg::from_usage("--long-option-2 [option2] 'tests long options with exclusions'").conflicts_with("option").requires("positional2"), + Arg::from_usage("[positional2] 'tests positionals with exclusions'"), + Arg::from_usage("-O --Option [option3] 'specific vals'").possible_values(&opt3_vals), + Arg::from_usage("[positional3]... 'tests specific values'").possible_values(&pos3_vals), + Arg::from_usage("--multvals [one] [two] 'Tests mutliple values, not mult occs'"), + Arg::from_usage("--multvalsmo... [one] [two] 'Tests mutliple values, and mult occs'"), + Arg::from_usage("--minvals2 [minvals]... 'Tests 2 min vals'").min_values(2), + Arg::from_usage("--maxvals3 [maxvals]... 'Tests 3 max vals'").max_values(3) + ]) + .subcommand(SubCommand::with_name("subcmd") + .about("tests subcommands") + .version("0.1") + .author("Kevin K. ") + .arg_from_usage("-o --option [scoption]... 'tests options'") + .arg_from_usage("[scpositional] 'tests positionals'")) +} + +pub fn check_complex_output(args: &str, out: &str) { + let mut w = vec![]; + let matches = complex_app().get_matches_from(args.split(' ').collect::>()); + if matches.is_present("flag") { + writeln!(w, "flag present {} times", matches.occurrences_of("flag")).unwrap(); + } else { + writeln!(w, "flag NOT present").unwrap(); + } + + if matches.is_present("option") { + if let Some(v) = matches.value_of("option") { + writeln!(w, "option present {} times with value: {}",matches.occurrences_of("option"), v).unwrap(); + } + if let Some(ov) = matches.values_of("option") { + for o in ov { + writeln!(w, "An option: {}", o).unwrap(); + } + } + } else { + writeln!(w, "option NOT present").unwrap(); + } + + if let Some(p) = matches.value_of("positional") { + writeln!(w, "positional present with value: {}", p).unwrap(); + } else { + writeln!(w, "positional NOT present").unwrap(); + } + + if matches.is_present("flag2") { + writeln!(w, "flag2 present").unwrap(); + writeln!(w, "option2 present with value of: {}", matches.value_of("long-option-2").unwrap()).unwrap(); + writeln!(w, "positional2 present with value of: {}", matches.value_of("positional2").unwrap()).unwrap(); + } else { + writeln!(w, "flag2 NOT present").unwrap(); + writeln!(w, "option2 maybe present with value of: {}", matches.value_of("long-option-2").unwrap_or("Nothing")).unwrap(); + writeln!(w, "positional2 maybe present with value of: {}", matches.value_of("positional2").unwrap_or("Nothing")).unwrap(); + } + + let _ = match matches.value_of("Option3").unwrap_or("") { + "fast" => writeln!(w, "option3 present quickly"), + "slow" => writeln!(w, "option3 present slowly"), + _ => writeln!(w, "option3 NOT present") + }; + + let _ = match matches.value_of("positional3").unwrap_or("") { + "vi" => writeln!(w, "positional3 present in vi mode"), + "emacs" => writeln!(w, "positional3 present in emacs mode"), + _ => writeln!(w, "positional3 NOT present") + }; + + if matches.is_present("option") { + if let Some(v) = matches.value_of("option") { + writeln!(w, "option present {} times with value: {}",matches.occurrences_of("option"), v).unwrap(); + } + if let Some(ov) = matches.values_of("option") { + for o in ov { + writeln!(w, "An option: {}", o).unwrap(); + } + } + } else { + writeln!(w, "option NOT present").unwrap(); + } + + if let Some(p) = matches.value_of("positional") { + writeln!(w, "positional present with value: {}", p).unwrap(); + } else { + writeln!(w, "positional NOT present").unwrap(); + } + if matches.is_present("subcmd") { + writeln!(w, "subcmd present").unwrap(); + if let Some(matches) = matches.subcommand_matches("subcmd") { + if matches.is_present("flag") { + writeln!(w, "flag present {} times", matches.occurrences_of("flag")).unwrap(); + } else { + writeln!(w, "flag NOT present").unwrap(); + } + + if matches.is_present("option") { + if let Some(v) = matches.value_of("option") { + writeln!(w, "scoption present with value: {}", v).unwrap(); + } + if let Some(ov) = matches.values_of("option") { + for o in ov { + writeln!(w, "An scoption: {}", o).unwrap(); + } + } + } else { + writeln!(w, "scoption NOT present").unwrap(); + } + + if let Some(p) = matches.value_of("scpositional") { + writeln!(w, "scpositional present with value: {}", p).unwrap(); + } + } + } else { + writeln!(w, "subcmd NOT present").unwrap(); + } + + let res = str::from_utf8(&w).unwrap(); + assert_eq!(res, out); +} diff --git a/clap-tests/.gitignore b/clap-tests/.gitignore deleted file mode 100644 index 37727f91cbe..00000000000 --- a/clap-tests/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# Compiled files -*.o -*.so -*.rlib -*.dll - -# Executables -*.exe - -# Generated by Cargo -/target/ diff --git a/clap-tests/Cargo.toml b/clap-tests/Cargo.toml deleted file mode 100644 index c7d218024f8..00000000000 --- a/clap-tests/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] - -name = "claptests" -version = "0.0.1" -authors = ["Kevin K. "] - -[dependencies.clap] -path = ".." diff --git a/clap-tests/Makefile b/clap-tests/Makefile deleted file mode 100644 index 6c7a7af6e99..00000000000 --- a/clap-tests/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -.PHONY: build test clean - -build: - @# It may be that this project was never built, and no Cargo.lock exists. - @# Thus it may be ignored - cargo update 2>/dev/null || : - cargo build --release - -test: - $(MAKE) build || ($(MAKE) clean && false) - ./run_tests.py - -clean: - cargo clean diff --git a/clap-tests/run_tests.py b/clap-tests/run_tests.py deleted file mode 100755 index 7d8f9d44c2f..00000000000 --- a/clap-tests/run_tests.py +++ /dev/null @@ -1,264 +0,0 @@ -#!/usr/bin/env python -import sys -import subprocess -import re -import difflib - -failed = False - -_ansi = re.compile(r'\x1b[^m]*m') - -_help = '''claptests v1.4.8 -Kevin K. -tests clap library - -USAGE: - claptests [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND] - -FLAGS: - -f, --flag tests flags - -F tests flags with exclusions - -h, --help Prints help information - -V, --version Prints version information - -OPTIONS: - -O, --Option tests options with specific value sets [values: fast, slow] - --long-option-2 tests long options with exclusions - --maxvals3 ... Tests 3 max vals - --minvals2 ... Tests 2 min vals - --multvals Tests mutliple values, not mult occs - --multvalsmo Tests mutliple values, and mult occs - -o, --option ... tests options - -ARGS: - tests positionals - tests positionals with exclusions - ... tests positionals with specific values [values: vi, emacs] - -SUBCOMMANDS: - help Prints this message or the help of the given subcommand(s) - subcmd tests subcommands''' - -_version = "claptests v1.4.8" - -_sc_dym_usage = '''error: The subcommand 'subcm' wasn't recognized -\tDid you mean 'subcmd' ? - -If you believe you received this message in error, try re-running with 'claptests -- subcm' - -USAGE: - claptests [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND] - -For more information try --help''' - -_arg_dym_usage = '''error: Found argument '--optio' which wasn't expected, or isn't valid in this context -\tDid you mean --option ? - -USAGE: - claptests --option ... - -For more information try --help''' - -_pv_dym_usage = '''error: 'slo' isn't a valid value for '--Option ' -\t[valid values: fast slow] - - Did you mean 'slow' ? - -USAGE: - claptests --Option - -For more information try --help''' - -_excluded = '''error: The argument '--flag' cannot be used with '-F' - -USAGE: - claptests -F --long-option-2 - -For more information try --help''' - -_excluded_l = '''error: The argument '-f' cannot be used with '-F' - -USAGE: - claptests -F --long-option-2 - -For more information try --help''' - -_required = '''error: The following required arguments were not provided: - - --long-option-2 - -USAGE: - claptests -F --long-option-2 - -For more information try --help''' - -_fop = '''flag present 1 times -option present 1 times with value: some -An option: some -positional present with value: value -flag2 NOT present -option2 maybe present with value of: Nothing -positional2 maybe present with value of: Nothing -option3 NOT present -positional3 NOT present -option present 1 times with value: some -An option: some -positional present with value: value -subcmd NOT present''' - -_f2op = '''flag present 2 times -option present 1 times with value: some -An option: some -positional present with value: value -flag2 NOT present -option2 maybe present with value of: Nothing -positional2 maybe present with value of: Nothing -option3 NOT present -positional3 NOT present -option present 1 times with value: some -An option: some -positional present with value: value -subcmd NOT present''' - -_o2p = '''flag NOT present -option present 2 times with value: some -An option: some -An option: other -positional present with value: value -flag2 NOT present -option2 maybe present with value of: Nothing -positional2 maybe present with value of: Nothing -option3 NOT present -positional3 NOT present -option present 2 times with value: some -An option: some -An option: other -positional present with value: value -subcmd NOT present''' - -_schelp = '''claptests-subcmd 0.1 -Kevin K. -tests subcommands - -USAGE: - claptests subcmd [FLAGS] [OPTIONS] [--] [ARGS] - -FLAGS: - -f, --flag tests flags - -h, --help Prints help information - -V, --version Prints version information - -OPTIONS: - -o, --option ... tests options - -ARGS: - tests positionals''' - -_scfop = '''flag NOT present -option NOT present -positional NOT present -flag2 NOT present -option2 maybe present with value of: Nothing -positional2 maybe present with value of: Nothing -option3 NOT present -positional3 NOT present -option NOT present -positional NOT present -subcmd present -flag present 1 times -scoption present with value: some -An scoption: some -scpositional present with value: value''' - -_scf2op = '''flag NOT present -option NOT present -positional NOT present -flag2 NOT present -option2 maybe present with value of: Nothing -positional2 maybe present with value of: Nothing -option3 NOT present -positional3 NOT present -option NOT present -positional NOT present -subcmd present -flag present 2 times -scoption present with value: some -An scoption: some -scpositional present with value: value''' - -_bin = './target/release/claptests' - -cmds = {'help short: ': ['{} -h'.format(_bin), _help, 0], - 'help long: ': ['{} --help'.format(_bin), _help, 0], - 'version short: ': ['{} -V'.format(_bin), _version, 0], - 'version long: ': ['{} --version'.format(_bin), _version, 0], - 'help subcmd: ': ['{} help'.format(_bin), _help, 0], - 'missing required: ': ['{} -F'.format(_bin), _required, 1], - 'F2(ss),O(s),P: ': ['{} value -f -f -o some'.format(_bin), _f2op, 0], - 'arg dym: ': ['{} --optio=foo'.format(_bin), _arg_dym_usage, 1], - 'O2(ll)P: ': ['{} value --option some --option other'.format(_bin), _o2p, 0], - 'O2(l=l=)P: ': ['{} value --option=some --option=other'.format(_bin), _o2p, 0], - 'O2(ss)P: ': ['{} value -o some -o other'.format(_bin), _o2p, 0], - 'F2(s2),O(s),P: ': ['{} value -ff -o some'.format(_bin), _f2op, 0], - 'F(s),O(s),P: ': ['{} value -f -o some'.format(_bin), _fop, 0], - 'F(l),O(l),P: ': ['{} value --flag --option some'.format(_bin), _fop, 0], - 'F(l),O(l=),P: ': ['{} value --flag --option=some'.format(_bin), _fop, 0], - 'sc dym: ': ['{} subcm'.format(_bin), _sc_dym_usage, 1], - 'sc help short: ': ['{} subcmd -h'.format(_bin), _schelp, 0], - 'sc help long: ': ['{} subcmd --help'.format(_bin), _schelp, 0], - 'scF(l),O(l),P: ': ['{} subcmd value --flag --option some'.format(_bin), _scfop, 0], - 'scF(l),O(s),P: ': ['{} subcmd value --flag -o some'.format(_bin), _scfop, 0], - 'scF(l),O(l=),P: ': ['{} subcmd value --flag --option=some'.format(_bin), _scfop, 0], - 'scF(s),O(l),P: ': ['{} subcmd value -f --option some'.format(_bin), _scfop, 0], - 'scF(s),O(s),P: ': ['{} subcmd value -f -o some'.format(_bin), _scfop, 0], - 'scF(s),O(l=),P: ': ['{} subcmd value -f --option=some'.format(_bin), _scfop, 0], - 'scF2(s),O(l),P: ': ['{} subcmd value -ff --option some'.format(_bin), _scf2op, 0], - 'scF2(s),O(s),P: ': ['{} subcmd value -ff -o some'.format(_bin), _scf2op, 0], - 'scF2(s),O(l=),P: ': ['{} subcmd value -ff --option=some'.format(_bin), _scf2op, 0], - 'scF2(l2),O(l),P: ': ['{} subcmd value --flag --flag --option some'.format(_bin), _scf2op, 0], - 'scF2(l2),O(s),P: ': ['{} subcmd value --flag --flag -o some'.format(_bin), _scf2op, 0], - 'scF2(l2),O(l=),P: ': ['{} subcmd value --flag --flag --option=some'.format(_bin), _scf2op, 0], - 'scF2(s2),O(l),P: ': ['{} subcmd value -f -f --option some'.format(_bin), _scf2op, 0], - 'scF2(s2),O(s),P: ': ['{} subcmd value -f -f -o some'.format(_bin), _scf2op, 0], - 'scF2(s2),O(l=),P: ': ['{} subcmd value -f -f --option=some'.format(_bin), _scf2op, 0] - } - -def pass_fail(name, cmd, check, good): - sys.stdout.write(name) - if check == good: - print('Pass') - return 0 - print('Fail') - print('\n\n{}\n# Should be:\n$ {}\n{}\n\n{}\n# But is:\n$ {}\n{}\n\n'.format('#'*25, cmd, good, '#'*25, cmd, check)) - for line in difflib.context_diff(good.splitlines(), check.splitlines(), fromfile="Should Be", tofile="Currently Is", lineterm=""): - print(line) - print() - return 1 - - -def main(): - num_failed = 0 - total = len(cmds) - for cmd, cmd_v in cmds.items(): - proc = subprocess.Popen(cmd_v[0], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) - out, err = proc.communicate() - out = _ansi.sub('', out.strip()) - err = _ansi.sub('', err.strip()) - rc = proc.returncode - if rc != cmd_v[-1]: - print('{}Fail (Exit Code={}; Should be={})'.format(cmd, rc, cmd_v[-1])) - num_failed += 1 - continue - if out and rc == cmd_v[-1]: - num_failed += pass_fail(cmd, cmd_v[0], out, cmd_v[1]) - elif rc == cmd_v[-1]: - num_failed += pass_fail(cmd, cmd_v[0], err, cmd_v[1]) - - print() - if num_failed: - print('{}/{} tests failed'.format(num_failed, total)) - return 1 - print('{}/{} tests passed!'.format(total, total)) - -if __name__ == '__main__': - sys.exit(main()) diff --git a/clap-tests/src/main.rs b/clap-tests/src/main.rs deleted file mode 100644 index ba7e72c99d4..00000000000 --- a/clap-tests/src/main.rs +++ /dev/null @@ -1,132 +0,0 @@ -#[macro_use] -extern crate clap; - -use clap::{App, Arg, SubCommand}; - - -fn main() { - let args = "-o --option=[opt]... 'tests options' - [positional] 'tests positionals'"; - let opt3_vals = ["fast", "slow"]; - let pos3_vals = ["vi", "emacs"]; - let matches = App::new("claptests") - .version("v1.4.8") - .about("tests clap library") - .author("Kevin K. ") - .args_from_usage(args) - .arg(Arg::from_usage("-f --flag... 'tests flags'") - .global(true)) - .args(&[ - Arg::from_usage("[flag2] -F 'tests flags with exclusions'").conflicts_with("flag").requires("long-option-2"), - Arg::from_usage("--long-option-2 [option2] 'tests long options with exclusions'").conflicts_with("option").requires("positional2"), - Arg::from_usage("[positional2] 'tests positionals with exclusions'"), - Arg::from_usage("-O --Option [option3] 'tests options with specific value sets'").possible_values(&opt3_vals), - Arg::from_usage("[positional3]... 'tests positionals with specific values'").possible_values(&pos3_vals), - Arg::from_usage("--multvals [one] [two] 'Tests mutliple values, not mult occs'"), - Arg::from_usage("--multvalsmo... [one] [two] 'Tests mutliple values, and mult occs'"), - Arg::from_usage("--minvals2 [minvals]... 'Tests 2 min vals'").min_values(2), - Arg::from_usage("--maxvals3 [maxvals]... 'Tests 3 max vals'").max_values(3) - ]) - .subcommand(SubCommand::with_name("subcmd") - .about("tests subcommands") - .version("0.1") - .author("Kevin K. ") - .arg_from_usage("-o --option [scoption]... 'tests options'") - .arg_from_usage("[scpositional] 'tests positionals'")) - .get_matches(); - - if matches.is_present("flag") { - println!("flag present {} times", matches.occurrences_of("flag")); - } else { - println!("flag NOT present"); - } - - if matches.is_present("option") { - if let Some(v) = matches.value_of("option") { - println!("option present {} times with value: {}",matches.occurrences_of("option"), v); - } - if let Some(ov) = matches.values_of("option") { - for o in ov { - println!("An option: {}", o); - } - } - } else { - println!("option NOT present"); - } - - if let Some(p) = matches.value_of("positional") { - println!("positional present with value: {}", p); - } else { - println!("positional NOT present"); - } - - if matches.is_present("flag2") { - println!("flag2 present"); - println!("option2 present with value of: {}", matches.value_of("long-option-2").unwrap()); - println!("positional2 present with value of: {}", matches.value_of("positional2").unwrap()); - } else { - println!("flag2 NOT present"); - println!("option2 maybe present with value of: {}", matches.value_of("long-option-2").unwrap_or("Nothing")); - println!("positional2 maybe present with value of: {}", matches.value_of("positional2").unwrap_or("Nothing")); - } - - match matches.value_of("Option3").unwrap_or("") { - "fast" => println!("option3 present quickly"), - "slow" => println!("option3 present slowly"), - _ => println!("option3 NOT present") - } - - match matches.value_of("positional3").unwrap_or("") { - "vi" => println!("positional3 present in vi mode"), - "emacs" => println!("positional3 present in emacs mode"), - _ => println!("positional3 NOT present") - } - - if matches.is_present("option") { - if let Some(v) = matches.value_of("option") { - println!("option present {} times with value: {}",matches.occurrences_of("option"), v); - } - if let Some(ov) = matches.values_of("option") { - for o in ov { - println!("An option: {}", o); - } - } - } else { - println!("option NOT present"); - } - - if let Some(p) = matches.value_of("positional") { - println!("positional present with value: {}", p); - } else { - println!("positional NOT present"); - } - if matches.is_present("subcmd") { - println!("subcmd present"); - if let Some(matches) = matches.subcommand_matches("subcmd") { - if matches.is_present("flag") { - println!("flag present {} times", matches.occurrences_of("flag")); - } else { - println!("flag NOT present"); - } - - if matches.is_present("option") { - if let Some(v) = matches.value_of("option") { - println!("scoption present with value: {}", v); - } - if let Some(ov) = matches.values_of("option") { - for o in ov { - println!("An scoption: {}", o); - } - } - } else { - println!("scoption NOT present"); - } - - if let Some(p) = matches.value_of("scpositional") { - println!("scpositional present with value: {}", p); - } - } - } else { - println!("subcmd NOT present"); - } -} diff --git a/src/app/parser.rs b/src/app/parser.rs index dbd6ba97d5a..fe30dca0432 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -296,6 +296,9 @@ impl<'a, 'b> Parser<'a, 'b> grps, tmp }); let mut ret_val = VecDeque::new(); + c_pos.dedup(); + c_flags.dedup(); + c_opt.dedup(); let mut pmap = BTreeMap::new(); for p in c_pos.into_iter() { @@ -303,7 +306,7 @@ impl<'a, 'b> Parser<'a, 'b> continue; } if let Some(p) = self.positionals.values().filter(|x| &x.name == &p).next() { - pmap.insert(p.index, p.name.to_owned()); + pmap.insert(p.index, p.to_string()); } } for (_, s) in pmap { @@ -1156,6 +1159,7 @@ impl<'a, 'b> Parser<'a, 'b> debugln!("macro=build_err;"); let c_with = $me.blacklisted_from($name, &$matcher); debugln!("'{:?}' conflicts with '{}'", c_with, $name); + $matcher.remove($name); let usg = $me.create_current_usage($matcher); if let Some(f) = $me.flags.iter().filter(|f| f.name == $name).next() { debugln!("It was a flag..."); diff --git a/tests/conflicts.rs b/tests/conflicts.rs index 40117a1a968..b9398bd1d10 100644 --- a/tests/conflicts.rs +++ b/tests/conflicts.rs @@ -1,7 +1,22 @@ +extern crate clap_test; extern crate clap; use clap::{App, Arg, ErrorKind, ArgGroup}; +static CONFLICT_ERR: &'static str = "error: The argument '--flag' cannot be used with '-F' + +USAGE: + clap-test -F --long-option-2 + +For more information try --help"; + +static CONFLICT_ERR_REV: &'static str = "error: The argument '--flag' cannot be used with '-F' + +USAGE: + clap-test -F --long-option-2 + +For more information try --help"; + #[test] fn flag_conflict() { let result = App::new("flag_conflict") @@ -59,3 +74,13 @@ fn group_conflict_2() { let err = result.err().unwrap(); assert_eq!(err.kind, ErrorKind::ArgumentConflict); } + +#[test] +fn conflict_output() { + clap_test::check_err_output(clap_test::complex_app(), "clap-test val1 --flag --long-option-2 val2 -F", CONFLICT_ERR, true); +} + +#[test] +fn conflict_output_rev() { + clap_test::check_err_output(clap_test::complex_app(), "clap-test val1 -F --long-option-2 val2 --flag", CONFLICT_ERR_REV, true); +} diff --git a/tests/groups.rs b/tests/groups.rs index a0333df2266..c57595ca5fe 100644 --- a/tests/groups.rs +++ b/tests/groups.rs @@ -1,9 +1,5 @@ extern crate clap; -extern crate regex; - -use std::str; - -use regex::Regex; +extern crate clap_test; use clap::{App, Arg, ArgGroup, ErrorKind}; @@ -97,25 +93,20 @@ fn empty_group() { #[test] fn req_group_usage_string() { - let re = Regex::new("\x1b[^m]*m").unwrap(); - let r = App::new("req_group") + let app = App::new("req_group") .args_from_usage("[base] 'Base commit' -d, --delete 'Remove the base commit information'") .group(ArgGroup::with_name("base_or_delete") .args(&["base", "delete"]) - .required(true)) - .get_matches_from_safe(vec!["req_group"]); + .required(true)); - let mut w = vec![]; - let err = r.unwrap_err(); - err.write_to(&mut w).unwrap(); - let err_s = str::from_utf8(&w).unwrap(); - assert_eq!(re.replace_all(err_s, ""), + clap_test::check_err_output(app, "clap-test", "error: The following required arguments were not provided: USAGE: - req_group [FLAGS] [ARGS] + clap-test [FLAGS] [ARGS] + +For more information try --help", true); -For more information try --help") } diff --git a/tests/help.rs b/tests/help.rs index ec7e6d2dc29..cf866c06c77 100644 --- a/tests/help.rs +++ b/tests/help.rs @@ -1,6 +1,56 @@ +extern crate clap_test; extern crate clap; -use clap::{App, SubCommand, ErrorKind, Arg}; +use clap::{App, SubCommand, ErrorKind}; + +static HELP: &'static str = "clap-test v1.4.8 +Kevin K. +tests clap library + +USAGE: + clap-test [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND] + +FLAGS: + -f, --flag tests flags + -F tests flags with exclusions + -h, --help Prints help information + -V, --version Prints version information + +OPTIONS: + -O, --Option specific vals [values: fast, slow] + --long-option-2 tests long options with exclusions + --maxvals3 ... Tests 3 max vals + --minvals2 ... Tests 2 min vals + --multvals Tests mutliple values, not mult occs + --multvalsmo Tests mutliple values, and mult occs + -o, --option ... tests options + +ARGS: + tests positionals + tests positionals with exclusions + ... tests specific values [values: vi, emacs] + +SUBCOMMANDS: + help Prints this message or the help of the given subcommand(s) + subcmd tests subcommands +"; + +static SC_HELP: &'static str = "subcmd 0.1 +Kevin K. +tests subcommands + +USAGE: + subcmd [FLAGS] [OPTIONS] [--] [ARGS] + +FLAGS: + -f, --flag tests flags + +OPTIONS: + -o, --option ... tests options + +ARGS: + tests positionals +"; #[test] fn help_short() { @@ -54,69 +104,44 @@ fn help_subcommand() { } #[test] -fn print_app_help() { - let mut app = App::new("test") - .author("Kevin K.") - .about("tests stuff") - .version("1.3") - .args_from_usage("-f, --flag 'some flag' - --option [opt] 'some option'") - .arg(Arg::with_name("other") - .short("O") - .long("other-opt") - .takes_value(true) - .help("some other opt")); - // We call a get_matches method to cause --help and --version to be built - let _ = app.get_matches_from_safe_borrow(vec![""]); +fn subcommand_short_help() { + let m = clap_test::complex_app() + .get_matches_from_safe(vec!["clap-test", "subcmd", "-h"]); - // Now we check the output of print_help() - let mut help = vec![]; - app.write_help(&mut help).ok().expect("failed to print help"); - assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\ -Kevin K. -tests stuff + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed); +} -USAGE: - test [FLAGS] [OPTIONS] +#[test] +fn subcommand_long_help() { + let m = clap_test::complex_app() + .get_matches_from_safe(vec!["clap-test", "subcmd", "--help"]); -FLAGS: - -f, --flag some flag - -h, --help Prints help information - -V, --version Prints version information + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed); +} -OPTIONS: - --option some option - -O, --other-opt some other opt\n")); +#[test] +fn subcommand_help_rev() { + let m = clap_test::complex_app() + .get_matches_from_safe(vec!["clap-test", "help", "subcmd"]); + + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed); } #[test] -fn possible_values() { - let mut app = App::new("test") - .author("Kevin K.") - .about("tests stuff") - .version("1.3") - .args(&[Arg::from_usage("-o, --opt [opt] 'some option'").possible_values(&["one", "two"]), - Arg::from_usage("[arg1] 'some pos arg'").possible_values(&["three", "four"])]); - // We call a get_matches method to cause --help and --version to be built - let _ = app.get_matches_from_safe_borrow(vec![""]); +fn complex_help_output() { + clap_test::check_help(clap_test::complex_app(), HELP); +} +#[test] +fn complex_subcommand_help_output() { + let mut a = clap_test::complex_app(); + let _ = a.get_matches_from_safe_borrow(vec![""]); + let sc = a.p.subcommands.iter().filter(|s| s.p.meta.name == "subcmd").next().unwrap(); // Now we check the output of print_help() let mut help = vec![]; - app.write_help(&mut help).expect("failed to print help"); - assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\ -Kevin K. -tests stuff - -USAGE: - test [FLAGS] [OPTIONS] [ARGS] - -FLAGS: - -h, --help Prints help information - -V, --version Prints version information - -OPTIONS: - -o, --opt some option [values: one, two] - -ARGS: - some pos arg [values: three, four]\n")); + sc.write_help(&mut help).ok().expect("failed to print help"); + assert_eq!(&*String::from_utf8(help).unwrap(), SC_HELP); } diff --git a/tests/opts.rs b/tests/opts.rs index 357ce2e7467..c4dffbc97a5 100644 --- a/tests/opts.rs +++ b/tests/opts.rs @@ -1,3 +1,4 @@ +extern crate clap_test; extern crate clap; use clap::{App, Arg}; @@ -168,3 +169,15 @@ fn default_values_user_value() { assert!(m.is_present("o")); assert_eq!(m.value_of("o").unwrap(), "value"); } + +#[test] +fn did_you_mean() { + clap_test::check_err_output(clap_test::complex_app(), "clap-test --optio=foo", +"error: Found argument '--optio' which wasn't expected, or isn't valid in this context +\tDid you mean --option ? + +USAGE: + clap-test --option ... + +For more information try --help", true); +} diff --git a/tests/possible_values.rs b/tests/possible_values.rs index cf013e04dc5..b07df9aae5e 100644 --- a/tests/possible_values.rs +++ b/tests/possible_values.rs @@ -1,7 +1,18 @@ +extern crate clap_test; extern crate clap; use clap::{App, Arg, ErrorKind}; +static PV_ERROR: &'static str = "error: 'slo' isn't a valid value for '--Option ' +\t[values:fast slow] + +\tDid you mean 'slow' ? + +USAGE: + clap-test --Option + +For more information try --help"; + #[test] fn possible_values_of_positional() { let m = App::new("possible_values") @@ -133,3 +144,8 @@ fn possible_values_of_option_multiple_fail() { assert!(m.is_err()); assert_eq!(m.unwrap_err().kind, ErrorKind::InvalidValue); } + +#[test] +fn possible_values_output() { + clap_test::check_err_output(clap_test::complex_app(), "clap-test -O slo", PV_ERROR, true); +} diff --git a/tests/require.rs b/tests/require.rs index b01d64e029b..5ebd1b1415d 100644 --- a/tests/require.rs +++ b/tests/require.rs @@ -1,4 +1,5 @@ extern crate clap; +extern crate clap_test; use clap::{App, Arg, ErrorKind, ArgGroup}; @@ -295,3 +296,16 @@ fn required_unless_one_err() { assert!(res.is_err()); assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); } + +#[test] +fn missing_required_output() { + clap_test::check_err_output(clap_test::complex_app(), "clap-test -F", +"error: The following required arguments were not provided: + + --long-option-2 + +USAGE: + clap-test -F --long-option-2 + +For more information try --help", true) +} diff --git a/tests/subcommands.rs b/tests/subcommands.rs index 2423a9d0f02..a9641f6fd55 100644 --- a/tests/subcommands.rs +++ b/tests/subcommands.rs @@ -1,3 +1,4 @@ +extern crate clap_test; extern crate clap; use clap::{App, Arg, SubCommand}; @@ -59,3 +60,16 @@ fn subcommand_multiple() { assert_eq!(sub_m.value_of("test").unwrap(), "testing"); } +#[test] +fn subcmd_did_you_mean_output() { + clap_test::check_err_output(clap_test::complex_app(), "clap-test subcm", +"error: The subcommand 'subcm' wasn't recognized +\tDid you mean 'subcmd' ? + +If you believe you received this message in error, try re-running with 'clap-test -- subcm' + +USAGE: + clap-test [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND] + +For more information try --help", true); +} diff --git a/tests/tests.rs b/tests/tests.rs index fe01d891d49..fd355321649 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,8 +1,90 @@ +extern crate clap_test; #[macro_use] extern crate clap; use clap::{App, Arg}; +static SCF2OP: &'static str = "flag NOT present +option NOT present +positional NOT present +flag2 NOT present +option2 maybe present with value of: Nothing +positional2 maybe present with value of: Nothing +option3 NOT present +positional3 NOT present +option NOT present +positional NOT present +subcmd present +flag present 2 times +scoption present with value: some +An scoption: some +scpositional present with value: value +"; + +static SCFOP: &'static str = "flag NOT present +option NOT present +positional NOT present +flag2 NOT present +option2 maybe present with value of: Nothing +positional2 maybe present with value of: Nothing +option3 NOT present +positional3 NOT present +option NOT present +positional NOT present +subcmd present +flag present 1 times +scoption present with value: some +An scoption: some +scpositional present with value: value +"; + +static O2P: &'static str = "flag NOT present +option present 2 times with value: some +An option: some +An option: other +positional present with value: value +flag2 NOT present +option2 maybe present with value of: Nothing +positional2 maybe present with value of: Nothing +option3 NOT present +positional3 NOT present +option present 2 times with value: some +An option: some +An option: other +positional present with value: value +subcmd NOT present +"; + +static F2OP: &'static str = "flag present 2 times +option present 1 times with value: some +An option: some +positional present with value: value +flag2 NOT present +option2 maybe present with value of: Nothing +positional2 maybe present with value of: Nothing +option3 NOT present +positional3 NOT present +option present 1 times with value: some +An option: some +positional present with value: value +subcmd NOT present +"; + +static FOP: &'static str = "flag present 1 times +option present 1 times with value: some +An option: some +positional present with value: value +flag2 NOT present +option2 maybe present with value of: Nothing +positional2 maybe present with value of: Nothing +option3 NOT present +positional3 NOT present +option present 1 times with value: some +An option: some +positional present with value: value +subcmd NOT present +"; + arg_enum!{ #[derive(Debug)] enum Val1 { @@ -92,3 +174,156 @@ fn add_multiple_arg() { Arg::with_name("test2").short("l")]) .get_matches(); } +#[test] +fn flag_x2_opt() { + clap_test::check_complex_output("clap-test value -f -f -o some", +"flag present 2 times +option present 1 times with value: some +An option: some +positional present with value: value +flag2 NOT present +option2 maybe present with value of: Nothing +positional2 maybe present with value of: Nothing +option3 NOT present +positional3 NOT present +option present 1 times with value: some +An option: some +positional present with value: value +subcmd NOT present +"); +} + +#[test] +fn long_opt_x2_pos() { + clap_test::check_complex_output("clap-test value --option some --option other", O2P); +} + +#[test] +fn long_opt_eq_x2_pos() { + clap_test::check_complex_output("clap-test value --option=some --option=other", O2P); +} + +#[test] +fn short_opt_x2_pos() { + clap_test::check_complex_output("clap-test value -o some -o other", O2P); +} + +#[test] +fn short_opt_eq_x2_pos() { + clap_test::check_complex_output("clap-test value -o=some -o=other", O2P); +} + +#[test] +fn short_flag_x2_comb_short_opt_pos() { + clap_test::check_complex_output("clap-test value -ff -o some", F2OP); +} + +#[test] +fn short_flag_short_opt_pos() { + clap_test::check_complex_output("clap-test value -f -o some", FOP); +} + +#[test] +fn long_flag_long_opt_pos() { + clap_test::check_complex_output("clap-test value --flag --option some", FOP); +} + +#[test] +fn long_flag_long_opt_eq_pos() { + clap_test::check_complex_output("clap-test value --flag --option=some", FOP); +} + +#[test] +fn sc_long_flag_long_opt() { + clap_test::check_complex_output("clap-test subcmd value --flag --option some", SCFOP); +} + +#[test] +fn sc_long_flag_short_opt_pos() { + clap_test::check_complex_output("clap-test subcmd value --flag -o some", SCFOP); +} + +#[test] +fn sc_long_flag_long_opt_eq_pos() { + clap_test::check_complex_output("clap-test subcmd value --flag --option=some", SCFOP); +} + +#[test] +fn sc_short_flag_long_opt_pos() { + clap_test::check_complex_output("clap-test subcmd value -f --option some", SCFOP); +} + +#[test] +fn sc_short_flag_short_opt_pos() { + clap_test::check_complex_output("clap-test subcmd value -f -o some", SCFOP); +} + +#[test] +fn sc_short_flag_short_opt_eq_pos() { + clap_test::check_complex_output("clap-test subcmd value -f -o=some", SCFOP); +} + +#[test] +fn sc_short_flag_long_opt_eq_pos() { + clap_test::check_complex_output("clap-test subcmd value -f --option=some", SCFOP); +} + +#[test] +fn sc_short_flag_x2_comb_long_opt_pos() { + clap_test::check_complex_output("clap-test subcmd value -ff --option some", SCF2OP); +} + +#[test] +fn sc_short_flag_x2_comb_short_opt_pos() { + clap_test::check_complex_output("clap-test subcmd value -ff -o some", SCF2OP); +} + +#[test] +fn sc_short_flag_x2_comb_long_opt_eq_pos() { + clap_test::check_complex_output("clap-test subcmd value -ff --option=some", SCF2OP); +} + +#[test] +fn sc_short_flag_x2_comb_short_opt_eq_pos() { + clap_test::check_complex_output("clap-test subcmd value -ff -o=some", SCF2OP); +} + +#[test] +fn sc_long_flag_x2_long_opt_pos() { + clap_test::check_complex_output("clap-test subcmd value --flag --flag --option some", SCF2OP); +} + +#[test] +fn sc_long_flag_x2_short_opt_pos() { + clap_test::check_complex_output("clap-test subcmd value --flag --flag -o some", SCF2OP); +} + +#[test] +fn sc_long_flag_x2_short_opt_eq_pos() { + clap_test::check_complex_output("clap-test subcmd value --flag --flag -o=some", SCF2OP); +} + +#[test] +fn sc_long_flag_x2_long_opt_eq_pos() { + clap_test::check_complex_output("clap-test subcmd value --flag --flag --option=some", SCF2OP); +} + +#[test] +fn sc_short_flag_x2_long_opt_pos() { + clap_test::check_complex_output("clap-test subcmd value -f -f --option some", SCF2OP); +} + +#[test] +fn sc_short_flag_x2_short_opt_pos() { + clap_test::check_complex_output("clap-test subcmd value -f -f -o some", SCF2OP); +} + +#[test] +fn sc_short_flag_x2_short_opt_eq_pos() { + clap_test::check_complex_output("clap-test subcmd value -f -f -o=some", SCF2OP); +} + +#[test] +fn sc_short_flag_x2_long_opt_eq_pos() { + clap_test::check_complex_output("clap-test subcmd value -f -f --option=some", SCF2OP); +}