use super::each_split_within;
use super::Fail::*;
use super::{HasArg, Name, Occur, Opt, Options, ParsingStyle};

#[test]
fn test_split_within() {
    fn t(s: &str, i: usize, u: &[String]) {
        let v = each_split_within(&(s.to_string()), i);
        assert!(v.iter().zip(u.iter()).all(|(a, b)| a == b));
    }
    t("", 0, &[]);
    t("", 15, &[]);
    t("hello", 15, &["hello".to_string()]);
    t(
        "\nMary had a little lamb\nLittle lamb\n",
        15,
        &[
            "Mary had a".to_string(),
            "little lamb".to_string(),
            "Little lamb".to_string(),
        ],
    );
    t(
        "\nMary had a little lamb\nLittle lamb\n",
        ::std::usize::MAX,
        &[
            "Mary had a little lamb".to_string(),
            "Little lamb".to_string(),
        ],
    );
}

// Tests for reqopt
#[test]
fn test_reqopt() {
    let long_args = vec!["--test=20".to_string()];
    let mut opts = Options::new();
    opts.reqopt("t", "test", "testing", "TEST");
    match opts.parse(&long_args) {
        Ok(ref m) => {
            assert!(m.opt_present("test"));
            assert_eq!(m.opt_str("test").unwrap(), "20");
            assert!(m.opt_present("t"));
            assert_eq!(m.opt_str("t").unwrap(), "20");
        }
        _ => {
            panic!("test_reqopt failed (long arg)");
        }
    }
    let short_args = vec!["-t".to_string(), "20".to_string()];
    match opts.parse(&short_args) {
        Ok(ref m) => {
            assert!((m.opt_present("test")));
            assert_eq!(m.opt_str("test").unwrap(), "20");
            assert!((m.opt_present("t")));
            assert_eq!(m.opt_str("t").unwrap(), "20");
        }
        _ => {
            panic!("test_reqopt failed (short arg)");
        }
    }
}

#[test]
fn test_reqopt_missing() {
    let args = vec!["blah".to_string()];
    match Options::new()
        .reqopt("t", "test", "testing", "TEST")
        .parse(&args)
    {
        Err(OptionMissing(_)) => {}
        _ => panic!(),
    }
}

#[test]
fn test_reqopt_no_arg() {
    let long_args = vec!["--test".to_string()];
    let mut opts = Options::new();
    opts.reqopt("t", "test", "testing", "TEST");
    match opts.parse(&long_args) {
        Err(ArgumentMissing(_)) => {}
        _ => panic!(),
    }
    let short_args = vec!["-t".to_string()];
    match opts.parse(&short_args) {
        Err(ArgumentMissing(_)) => {}
        _ => panic!(),
    }
}

#[test]
fn test_reqopt_multi() {
    let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
    match Options::new()
        .reqopt("t", "test", "testing", "TEST")
        .parse(&args)
    {
        Err(OptionDuplicated(_)) => {}
        _ => panic!(),
    }
}

// Tests for optopt
#[test]
fn test_optopt() {
    let long_args = vec!["--test=20".to_string()];
    let mut opts = Options::new();
    opts.optopt("t", "test", "testing", "TEST");
    match opts.parse(&long_args) {
        Ok(ref m) => {
            assert!(m.opt_present("test"));
            assert_eq!(m.opt_str("test").unwrap(), "20");
            assert!((m.opt_present("t")));
            assert_eq!(m.opt_str("t").unwrap(), "20");
        }
        _ => panic!(),
    }
    let short_args = vec!["-t".to_string(), "20".to_string()];
    match opts.parse(&short_args) {
        Ok(ref m) => {
            assert!((m.opt_present("test")));
            assert_eq!(m.opt_str("test").unwrap(), "20");
            assert!((m.opt_present("t")));
            assert_eq!(m.opt_str("t").unwrap(), "20");
        }
        _ => panic!(),
    }
}

#[test]
fn test_optopt_missing() {
    let args = vec!["blah".to_string()];
    match Options::new()
        .optopt("t", "test", "testing", "TEST")
        .parse(&args)
    {
        Ok(ref m) => {
            assert!(!m.opt_present("test"));
            assert!(!m.opt_present("t"));
        }
        _ => panic!(),
    }
}

#[test]
fn test_optopt_no_arg() {
    let long_args = vec!["--test".to_string()];
    let mut opts = Options::new();
    opts.optopt("t", "test", "testing", "TEST");
    match opts.parse(&long_args) {
        Err(ArgumentMissing(_)) => {}
        _ => panic!(),
    }
    let short_args = vec!["-t".to_string()];
    match opts.parse(&short_args) {
        Err(ArgumentMissing(_)) => {}
        _ => panic!(),
    }
}

#[test]
fn test_optopt_multi() {
    let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
    match Options::new()
        .optopt("t", "test", "testing", "TEST")
        .parse(&args)
    {
        Err(OptionDuplicated(_)) => {}
        _ => panic!(),
    }
}

// Tests for optflag
#[test]
fn test_optflag() {
    let long_args = vec!["--test".to_string()];
    let mut opts = Options::new();
    opts.optflag("t", "test", "testing");
    match opts.parse(&long_args) {
        Ok(ref m) => {
            assert!(m.opt_present("test"));
            assert!(m.opt_present("t"));
        }
        _ => panic!(),
    }
    let short_args = vec!["-t".to_string()];
    match opts.parse(&short_args) {
        Ok(ref m) => {
            assert!(m.opt_present("test"));
            assert!(m.opt_present("t"));
        }
        _ => panic!(),
    }
}

#[test]
fn test_optflag_missing() {
    let args = vec!["blah".to_string()];
    match Options::new().optflag("t", "test", "testing").parse(&args) {
        Ok(ref m) => {
            assert!(!m.opt_present("test"));
            assert!(!m.opt_present("t"));
        }
        _ => panic!(),
    }
}

#[test]
fn test_opt_end() {
    let args = vec!["--".to_owned(), "-t".to_owned()];
    match Options::new().optflag("t", "test", "testing").parse(&args) {
        Ok(ref m) => {
            assert!(!m.opt_present("test"));
            assert!(!m.opt_present("t"));
            assert_eq!(m.free.len(), 1);
            assert_eq!(m.free[0], "-t");
        }
        _ => panic!(),
    }
}

#[test]
fn test_opt_only_end() {
    let args = vec!["--".to_owned()];
    match Options::new().optflag("t", "test", "testing").parse(&args) {
        Ok(ref m) => {
            assert!(!m.opt_present("test"));
            assert!(!m.opt_present("t"));
            assert_eq!(m.free.len(), 0);
        }
        _ => panic!(),
    }
}

#[test]
fn test_optflag_long_arg() {
    let args = vec!["--test=20".to_string()];
    match Options::new().optflag("t", "test", "testing").parse(&args) {
        Err(UnexpectedArgument(_)) => {}
        _ => panic!(),
    }
}

#[test]
fn test_optflag_multi() {
    let args = vec!["--test".to_string(), "-t".to_string()];
    match Options::new().optflag("t", "test", "testing").parse(&args) {
        Err(OptionDuplicated(_)) => {}
        _ => panic!(),
    }
}

#[test]
fn test_optflag_short_arg() {
    let args = vec!["-t".to_string(), "20".to_string()];
    match Options::new().optflag("t", "test", "testing").parse(&args) {
        Ok(ref m) => {
            // The next variable after the flag is just a free argument

            assert!(m.free[0] == "20");
        }
        _ => panic!(),
    }
}

// Tests for optflagmulti
#[test]
fn test_optflagmulti_short1() {
    let args = vec!["-v".to_string()];
    match Options::new()
        .optflagmulti("v", "verbose", "verbosity")
        .parse(&args)
    {
        Ok(ref m) => {
            assert_eq!(m.opt_count("v"), 1);
        }
        _ => panic!(),
    }
}

#[test]
fn test_optflagmulti_short2a() {
    let args = vec!["-v".to_string(), "-v".to_string()];
    match Options::new()
        .optflagmulti("v", "verbose", "verbosity")
        .parse(&args)
    {
        Ok(ref m) => {
            assert_eq!(m.opt_count("v"), 2);
        }
        _ => panic!(),
    }
}

#[test]
fn test_optflagmulti_short2b() {
    let args = vec!["-vv".to_string()];
    match Options::new()
        .optflagmulti("v", "verbose", "verbosity")
        .parse(&args)
    {
        Ok(ref m) => {
            assert_eq!(m.opt_count("v"), 2);
        }
        _ => panic!(),
    }
}

#[test]
fn test_optflagmulti_long1() {
    let args = vec!["--verbose".to_string()];
    match Options::new()
        .optflagmulti("v", "verbose", "verbosity")
        .parse(&args)
    {
        Ok(ref m) => {
            assert_eq!(m.opt_count("verbose"), 1);
        }
        _ => panic!(),
    }
}

#[test]
fn test_optflagmulti_long2() {
    let args = vec!["--verbose".to_string(), "--verbose".to_string()];
    match Options::new()
        .optflagmulti("v", "verbose", "verbosity")
        .parse(&args)
    {
        Ok(ref m) => {
            assert_eq!(m.opt_count("verbose"), 2);
        }
        _ => panic!(),
    }
}

#[test]
fn test_optflagmulti_mix() {
    let args = vec![
        "--verbose".to_string(),
        "-v".to_string(),
        "-vv".to_string(),
        "verbose".to_string(),
    ];
    match Options::new()
        .optflagmulti("v", "verbose", "verbosity")
        .parse(&args)
    {
        Ok(ref m) => {
            assert_eq!(m.opt_count("verbose"), 4);
            assert_eq!(m.opt_count("v"), 4);
        }
        _ => panic!(),
    }
}

// Tests for optflagopt
#[test]
fn test_optflagopt() {
    let long_args = vec!["--test".to_string()];
    let mut opts = Options::new();
    opts.optflagopt("t", "test", "testing", "ARG");
    match opts.parse(&long_args) {
        Ok(ref m) => {
            assert!(m.opt_present("test"));
            assert!(m.opt_present("t"));
        }
        _ => panic!(),
    }
    let short_args = vec!["-t".to_string()];
    match opts.parse(&short_args) {
        Ok(ref m) => {
            assert!(m.opt_present("test"));
            assert!(m.opt_present("t"));
        }
        _ => panic!(),
    }
    let short_args = vec!["-t".to_string(), "x".to_string()];
    match opts.parse(&short_args) {
        Ok(ref m) => {
            assert_eq!(m.opt_str("t").unwrap(), "x");
            assert_eq!(m.opt_str("test").unwrap(), "x");
        }
        _ => panic!(),
    }
    let long_args = vec!["--test=x".to_string()];
    match opts.parse(&long_args) {
        Ok(ref m) => {
            assert_eq!(m.opt_str("t").unwrap(), "x");
            assert_eq!(m.opt_str("test").unwrap(), "x");
        }
        _ => panic!(),
    }
    let long_args = vec!["--test".to_string(), "x".to_string()];
    match opts.parse(&long_args) {
        Ok(ref m) => {
            assert_eq!(m.opt_str("t"), None);
            assert_eq!(m.opt_str("test"), None);
        }
        _ => panic!(),
    }
    let no_args: Vec<String> = vec![];
    match opts.parse(&no_args) {
        Ok(ref m) => {
            assert!(!m.opt_present("test"));
            assert!(!m.opt_present("t"));
        }
        _ => panic!(),
    }
}

// Tests for optmulti
#[test]
fn test_optmulti() {
    let long_args = vec!["--test=20".to_string()];
    let mut opts = Options::new();
    opts.optmulti("t", "test", "testing", "TEST");
    match opts.parse(&long_args) {
        Ok(ref m) => {
            assert!((m.opt_present("test")));
            assert_eq!(m.opt_str("test").unwrap(), "20");
            assert!((m.opt_present("t")));
            assert_eq!(m.opt_str("t").unwrap(), "20");
        }
        _ => panic!(),
    }
    let short_args = vec!["-t".to_string(), "20".to_string()];
    match opts.parse(&short_args) {
        Ok(ref m) => {
            assert!((m.opt_present("test")));
            assert_eq!(m.opt_str("test").unwrap(), "20");
            assert!((m.opt_present("t")));
            assert_eq!(m.opt_str("t").unwrap(), "20");
        }
        _ => panic!(),
    }
}

#[test]
fn test_optmulti_missing() {
    let args = vec!["blah".to_string()];
    match Options::new()
        .optmulti("t", "test", "testing", "TEST")
        .parse(&args)
    {
        Ok(ref m) => {
            assert!(!m.opt_present("test"));
            assert!(!m.opt_present("t"));
        }
        _ => panic!(),
    }
}

#[test]
fn test_optmulti_no_arg() {
    let long_args = vec!["--test".to_string()];
    let mut opts = Options::new();
    opts.optmulti("t", "test", "testing", "TEST");
    match opts.parse(&long_args) {
        Err(ArgumentMissing(_)) => {}
        _ => panic!(),
    }
    let short_args = vec!["-t".to_string()];
    match opts.parse(&short_args) {
        Err(ArgumentMissing(_)) => {}
        _ => panic!(),
    }
}

#[test]
fn test_optmulti_multi() {
    let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
    match Options::new()
        .optmulti("t", "test", "testing", "TEST")
        .parse(&args)
    {
        Ok(ref m) => {
            assert!(m.opt_present("test"));
            assert_eq!(m.opt_str("test").unwrap(), "20");
            assert!(m.opt_present("t"));
            assert_eq!(m.opt_str("t").unwrap(), "20");
            let pair = m.opt_strs("test");
            assert!(pair[0] == "20");
            assert!(pair[1] == "30");
        }
        _ => panic!(),
    }
}

#[test]
fn test_free_argument_is_hyphen() {
    let args = vec!["-".to_string()];
    match Options::new().parse(&args) {
        Ok(ref m) => {
            assert_eq!(m.free.len(), 1);
            assert_eq!(m.free[0], "-");
        }
        _ => panic!(),
    }
}

#[test]
fn test_unrecognized_option() {
    let long_args = vec!["--untest".to_string()];
    let mut opts = Options::new();
    opts.optmulti("t", "test", "testing", "TEST");
    match opts.parse(&long_args) {
        Err(UnrecognizedOption(_)) => {}
        _ => panic!(),
    }
    let short_args = vec!["-u".to_string()];
    match opts.parse(&short_args) {
        Err(UnrecognizedOption(_)) => {}
        _ => panic!(),
    }
}

#[test]
fn test_combined() {
    let args = vec![
        "prog".to_string(),
        "free1".to_string(),
        "-s".to_string(),
        "20".to_string(),
        "free2".to_string(),
        "--flag".to_string(),
        "--long=30".to_string(),
        "-f".to_string(),
        "-m".to_string(),
        "40".to_string(),
        "-m".to_string(),
        "50".to_string(),
        "-n".to_string(),
        "-A B".to_string(),
        "-n".to_string(),
        "-60 70".to_string(),
    ];
    match Options::new()
        .optopt("s", "something", "something", "SOMETHING")
        .optflag("", "flag", "a flag")
        .reqopt("", "long", "hi", "LONG")
        .optflag("f", "", "another flag")
        .optmulti("m", "", "mmmmmm", "YUM")
        .optmulti("n", "", "nothing", "NOTHING")
        .optopt("", "notpresent", "nothing to see here", "NOPE")
        .parse(&args)
    {
        Ok(ref m) => {
            assert!(m.free[0] == "prog");
            assert!(m.free[1] == "free1");
            assert_eq!(m.opt_str("s").unwrap(), "20");
            assert!(m.free[2] == "free2");
            assert!((m.opt_present("flag")));
            assert_eq!(m.opt_str("long").unwrap(), "30");
            assert!((m.opt_present("f")));
            let pair = m.opt_strs("m");
            assert!(pair[0] == "40");
            assert!(pair[1] == "50");
            let pair = m.opt_strs("n");
            assert!(pair[0] == "-A B");
            assert!(pair[1] == "-60 70");
            assert!((!m.opt_present("notpresent")));
        }
        _ => panic!(),
    }
}

#[test]
fn test_mixed_stop() {
    let args = vec![
        "-a".to_string(),
        "b".to_string(),
        "-c".to_string(),
        "d".to_string(),
    ];
    match Options::new()
        .parsing_style(ParsingStyle::StopAtFirstFree)
        .optflag("a", "", "")
        .optopt("c", "", "", "")
        .parse(&args)
    {
        Ok(ref m) => {
            println!("{}", m.opt_present("c"));
            assert!(m.opt_present("a"));
            assert!(!m.opt_present("c"));
            assert_eq!(m.free.len(), 3);
            assert_eq!(m.free[0], "b");
            assert_eq!(m.free[1], "-c");
            assert_eq!(m.free[2], "d");
        }
        _ => panic!(),
    }
}

#[test]
fn test_mixed_stop_hyphen() {
    let args = vec![
        "-a".to_string(),
        "-".to_string(),
        "-c".to_string(),
        "d".to_string(),
    ];
    match Options::new()
        .parsing_style(ParsingStyle::StopAtFirstFree)
        .optflag("a", "", "")
        .optopt("c", "", "", "")
        .parse(&args)
    {
        Ok(ref m) => {
            println!("{}", m.opt_present("c"));
            assert!(m.opt_present("a"));
            assert!(!m.opt_present("c"));
            assert_eq!(m.free.len(), 3);
            assert_eq!(m.free[0], "-");
            assert_eq!(m.free[1], "-c");
            assert_eq!(m.free[2], "d");
        }
        _ => panic!(),
    }
}

#[test]
fn test_multi() {
    let mut opts = Options::new();
    opts.optopt("e", "", "encrypt", "ENCRYPT");
    opts.optopt("", "encrypt", "encrypt", "ENCRYPT");
    opts.optopt("f", "", "flag", "FLAG");

    let args_single = vec!["-e".to_string(), "foo".to_string()];
    let matches_single = &match opts.parse(&args_single) {
        Ok(m) => m,
        _ => panic!(),
    };
    assert!(matches_single.opts_present(&["e".to_string()]));
    assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
    assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
    assert!(!matches_single.opts_present(&["encrypt".to_string()]));
    assert!(!matches_single.opts_present(&["thing".to_string()]));
    assert!(!matches_single.opts_present(&[]));

    assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
    assert_eq!(
        matches_single
            .opts_str(&["e".to_string(), "encrypt".to_string()])
            .unwrap(),
        "foo"
    );
    assert_eq!(
        matches_single
            .opts_str(&["encrypt".to_string(), "e".to_string()])
            .unwrap(),
        "foo"
    );

    let args_both = vec![
        "-e".to_string(),
        "foo".to_string(),
        "--encrypt".to_string(),
        "foo".to_string(),
    ];
    let matches_both = &match opts.parse(&args_both) {
        Ok(m) => m,
        _ => panic!(),
    };
    assert!(matches_both.opts_present(&["e".to_string()]));
    assert!(matches_both.opts_present(&["encrypt".to_string()]));
    assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
    assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
    assert!(!matches_both.opts_present(&["f".to_string()]));
    assert!(!matches_both.opts_present(&["thing".to_string()]));
    assert!(!matches_both.opts_present(&[]));

    assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
    assert_eq!(
        matches_both.opts_str(&["encrypt".to_string()]).unwrap(),
        "foo"
    );
    assert_eq!(
        matches_both
            .opts_str(&["e".to_string(), "encrypt".to_string()])
            .unwrap(),
        "foo"
    );
    assert_eq!(
        matches_both
            .opts_str(&["encrypt".to_string(), "e".to_string()])
            .unwrap(),
        "foo"
    );
}

#[test]
fn test_nospace() {
    let args = vec!["-Lfoo".to_string(), "-M.".to_string()];
    let matches = &match Options::new()
        .optmulti("L", "", "library directory", "LIB")
        .optmulti("M", "", "something", "MMMM")
        .parse(&args)
    {
        Ok(m) => m,
        _ => panic!(),
    };
    assert!(matches.opts_present(&["L".to_string()]));
    assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
    assert!(matches.opts_present(&["M".to_string()]));
    assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
}

#[test]
fn test_nospace_conflict() {
    let args = vec!["-vvLverbose".to_string(), "-v".to_string()];
    let matches = &match Options::new()
        .optmulti("L", "", "library directory", "LIB")
        .optflagmulti("v", "verbose", "Verbose")
        .parse(&args)
    {
        Ok(m) => m,
        Err(e) => panic!("{}", e),
    };
    assert!(matches.opts_present(&["L".to_string()]));
    assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
    assert!(matches.opts_present(&["v".to_string()]));
    assert_eq!(3, matches.opt_count("v"));
}

#[test]
fn test_long_to_short() {
    let mut short = Opt {
        name: Name::Long("banana".to_string()),
        hasarg: HasArg::Yes,
        occur: Occur::Req,
        aliases: Vec::new(),
    };
    short.aliases = vec![Opt {
        name: Name::Short('b'),
        hasarg: HasArg::Yes,
        occur: Occur::Req,
        aliases: Vec::new(),
    }];
    let mut opts = Options::new();
    opts.reqopt("b", "banana", "some bananas", "VAL");
    let verbose = &opts.grps[0];
    assert!(verbose.long_to_short() == short);
}

#[test]
fn test_aliases_long_and_short() {
    let args = vec!["-a".to_string(), "--apple".to_string(), "-a".to_string()];

    let matches = Options::new()
        .optflagmulti("a", "apple", "Desc")
        .parse(&args)
        .unwrap();
    assert_eq!(3, matches.opt_count("a"));
    assert_eq!(3, matches.opt_count("apple"));
}

#[test]
fn test_usage() {
    let mut opts = Options::new();
    opts.reqopt("b", "banana", "Desc", "VAL");
    opts.optopt("a", "012345678901234567890123456789", "Desc", "VAL");
    opts.optflag("k", "kiwi", "Desc");
    opts.optflagopt("p", "", "Desc", "VAL");
    opts.optmulti("l", "", "Desc", "VAL");
    opts.optflag("", "starfruit", "Starfruit");

    let expected = "Usage: fruits

Options:
    -b, --banana VAL    Desc
    -a, --012345678901234567890123456789 VAL
                        Desc
    -k, --kiwi          Desc
    -p [VAL]            Desc
    -l VAL              Desc
        --starfruit     Starfruit
";

    let generated_usage = opts.usage("Usage: fruits");

    debug!("expected: <<{}>>", expected);
    debug!("generated: <<{}>>", generated_usage);
    assert_eq!(generated_usage, expected);
}

#[test]
fn test_usage_description_wrapping() {
    // indentation should be 24 spaces
    // lines wrap after 78: or rather descriptions wrap after 54

    let mut opts = Options::new();
    opts.optflag(
        "k",
        "kiwi",
        "This is a long description which won't be wrapped..+..",
    ); // 54
    opts.optflag(
        "a",
        "apple",
        "This is a long description which _will_ be wrapped..+..",
    );
    opts.optflag(
        "b",
        "banana",
        "HereWeNeedOneSingleWordThatIsLongerThanTheWrappingLengthAndThisIsIt",
    );

    let expected = "Usage: fruits

Options:
    -k, --kiwi          This is a long description which won't be wrapped..+..
    -a, --apple         This is a long description which _will_ be
                        wrapped..+..
    -b, --banana        HereWeNeedOneSingleWordThatIsLongerThanTheWrappingLengthAndThisIsIt
";

    let usage = opts.usage("Usage: fruits");

    debug!("expected: <<{}>>", expected);
    debug!("generated: <<{}>>", usage);
    assert!(usage == expected)
}

#[test]
fn test_usage_description_multibyte_handling() {
    let mut opts = Options::new();
    opts.optflag(
        "k",
        "k\u{2013}w\u{2013}",
        "The word kiwi is normally spelled with two i's",
    );
    opts.optflag(
        "a",
        "apple",
        "This \u{201C}description\u{201D} has some characters that could \
         confuse the line wrapping; an apple costs 0.51€ in some parts of Europe.",
    );

    let expected = "Usage: fruits

Options:
    -k, --k–w–          The word kiwi is normally spelled with two i's
    -a, --apple         This “description” has some characters that could
                        confuse the line wrapping; an apple costs 0.51€ in
                        some parts of Europe.
";

    let usage = opts.usage("Usage: fruits");

    debug!("expected: <<{}>>", expected);
    debug!("generated: <<{}>>", usage);
    assert!(usage == expected)
}

#[test]
fn test_usage_description_newline_handling() {
    let mut opts = Options::new();
    opts.optflag(
        "k",
        "k\u{2013}w\u{2013}",
        "The word kiwi is normally spelled with two i's",
    );
    opts.optflag(
        "a",
        "apple",
        "This description forces a new line.\n Here is a premature\n\
         newline",
    );

    let expected = "Usage: fruits

Options:
    -k, --k–w–          The word kiwi is normally spelled with two i's
    -a, --apple         This description forces a new line.
                        Here is a premature
                        newline
";

    let usage = opts.usage("Usage: fruits");

    debug!("expected: <<{}>>", expected);
    debug!("generated: <<{}>>", usage);
    assert!(usage == expected)
}

#[test]
fn test_usage_multiwidth() {
    let mut opts = Options::new();
    opts.optflag("a", "apple", "apple description");
    opts.optflag("b", "banana\u{00AB}", "banana description");
    opts.optflag("c", "brûlée", "brûlée quite long description");
    opts.optflag("k", "kiwi\u{20AC}", "kiwi description");
    opts.optflag("o", "orange\u{2039}", "orange description");
    opts.optflag(
        "r",
        "raspberry-but-making-this-option-way-too-long",
        "raspberry description is also quite long indeed longer than \
         every other piece of text we might encounter here and thus will \
         be automatically broken up",
    );

    let expected = "Usage: fruits

Options:
    -a, --apple         apple description
    -b, --banana«       banana description
    -c, --brûlée        brûlée quite long description
    -k, --kiwi€         kiwi description
    -o, --orange‹       orange description
    -r, --raspberry-but-making-this-option-way-too-long\u{0020}
                        raspberry description is also quite long indeed longer
                        than every other piece of text we might encounter here
                        and thus will be automatically broken up
";

    let usage = opts.usage("Usage: fruits");

    debug!("expected: <<{}>>", expected);
    debug!("generated: <<{}>>", usage);
    assert!(usage == expected)
}

#[test]
fn test_usage_short_only() {
    let mut opts = Options::new();
    opts.optopt("k", "", "Kiwi", "VAL");
    opts.optflag("s", "", "Starfruit");
    opts.optflagopt("a", "", "Apple", "TYPE");

    let expected = "Usage: fruits

Options:
    -k VAL              Kiwi
    -s                  Starfruit
    -a [TYPE]           Apple
";

    let usage = opts.usage("Usage: fruits");
    debug!("expected: <<{}>>", expected);
    debug!("generated: <<{}>>", usage);
    assert!(usage == expected)
}

#[test]
fn test_usage_long_only() {
    let mut opts = Options::new();
    opts.optopt("", "kiwi", "Kiwi", "VAL");
    opts.optflag("", "starfruit", "Starfruit");
    opts.optflagopt("", "apple", "Apple", "TYPE");

    let expected = "Usage: fruits

Options:
    --kiwi VAL          Kiwi
    --starfruit         Starfruit
    --apple [TYPE]      Apple
";

    let usage = opts.usage("Usage: fruits");
    debug!("expected: <<{}>>", expected);
    debug!("generated: <<{}>>", usage);
    assert!(usage == expected)
}

#[test]
fn test_short_usage() {
    let mut opts = Options::new();
    opts.reqopt("b", "banana", "Desc", "VAL");
    opts.optopt("a", "012345678901234567890123456789", "Desc", "VAL");
    opts.optflag("k", "kiwi", "Desc");
    opts.optflagopt("p", "", "Desc", "VAL");
    opts.optmulti("l", "", "Desc", "VAL");

    let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
    let generated_usage = opts.short_usage("fruits");

    debug!("expected: <<{}>>", expected);
    debug!("generated: <<{}>>", generated_usage);
    assert_eq!(generated_usage, expected);
}
#[test]
fn test_nonexistant_opt() {
    let mut opts = Options::new();
    opts.optflag("b", "bar", "Desc");
    let args: Vec<String> = Vec::new();
    let matches = opts.parse(&args).unwrap();
    assert_eq!(matches.opt_defined("foo"), false);
    assert_eq!(matches.opt_defined("bar"), true);
}
#[test]
fn test_args_with_equals() {
    let mut opts = Options::new();
    opts.optopt("o", "one", "One", "INFO");
    opts.optopt("t", "two", "Two", "INFO");

    let args = vec![
        "--one".to_string(),
        "A=B".to_string(),
        "--two=C=D".to_string(),
    ];
    let matches = &match opts.parse(&args) {
        Ok(m) => m,
        Err(e) => panic!("{}", e),
    };
    assert_eq!(matches.opts_str(&["o".to_string()]).unwrap(), "A=B");
    assert_eq!(matches.opts_str(&["t".to_string()]).unwrap(), "C=D");
}

#[test]
fn test_long_only_usage() {
    let mut opts = Options::new();
    opts.long_only(true);
    opts.optflag("k", "kiwi", "Description");
    opts.optflag("a", "apple", "Description");

    let expected = "Usage: fruits

Options:
    -k, -kiwi           Description
    -a, -apple          Description
";

    let usage = opts.usage("Usage: fruits");

    debug!("expected: <<{}>>", expected);
    debug!("generated: <<{}>>", usage);
    assert!(usage == expected)
}

#[test]
fn test_long_only_mode() {
    let mut opts = Options::new();
    opts.long_only(true);
    opts.optopt("a", "apple", "Description", "X");
    opts.optopt("b", "banana", "Description", "X");
    opts.optopt("c", "currant", "Description", "X");
    opts.optopt("", "durian", "Description", "X");
    opts.optopt("e", "", "Description", "X");
    opts.optopt("", "fruit", "Description", "X");

    let args = vec![
        "-a",
        "A",
        "-b=B",
        "--c=C",
        "-durian",
        "D",
        "--e",
        "E",
        "-fruit=any",
    ];
    let matches = &match opts.parse(&args) {
        Ok(m) => m,
        Err(e) => panic!("{}", e),
    };
    assert_eq!(matches.opts_str(&["a".to_string()]).unwrap(), "A");
    assert_eq!(matches.opts_str(&["b".to_string()]).unwrap(), "B");
    assert_eq!(matches.opts_str(&["c".to_string()]).unwrap(), "C");
    assert_eq!(matches.opts_str(&["durian".to_string()]).unwrap(), "D");
    assert_eq!(matches.opts_str(&["e".to_string()]).unwrap(), "E");
    assert_eq!(matches.opts_str(&["fruit".to_string()]).unwrap(), "any");
}

#[test]
fn test_long_only_mode_no_short_parse() {
    let mut opts = Options::new();
    opts.long_only(true);
    opts.optflag("h", "help", "Description");
    opts.optflag("i", "ignore", "Description");
    opts.optflag("", "hi", "Description");

    let args = vec!["-hi"];
    let matches = &match opts.parse(&args) {
        Ok(m) => m,
        Err(e) => panic!("{}", e),
    };
    assert!(matches.opt_present("hi"));
    assert!(!matches.opt_present("h"));
    assert!(!matches.opt_present("i"));
}

#[test]
fn test_normal_mode_no_long_parse() {
    // Like test_long_only_mode_no_short_parse, but we make sure
    // that long_only can be disabled, and the right thing
    // happens.
    let mut opts = Options::new();
    opts.long_only(true);
    opts.optflag("h", "help", "Description");
    opts.optflag("i", "ignore", "Description");
    opts.optflag("", "hi", "Description");
    opts.long_only(false);

    let args = vec!["-hi"];
    let matches = &match opts.parse(&args) {
        Ok(m) => m,
        Err(e) => panic!("{}", e),
    };
    assert!(!matches.opt_present("hi"));
    assert!(matches.opt_present("h"));
    assert!(matches.opt_present("i"));
}

#[test]
#[should_panic]
fn test_long_name_too_short() {
    let mut opts = Options::new();
    opts.optflag("", "a", "Oops, long option too short");
}

#[test]
#[should_panic]
fn test_undefined_opt_present() {
    let mut opts = Options::new();
    opts.optflag("h", "help", "Description");
    let args = vec!["-h"];
    match opts.parse(args) {
        Ok(matches) => assert!(!matches.opt_present("undefined")),
        Err(e) => panic!("{}", e),
    }
}

#[test]
fn test_opt_default() {
    let mut opts = Options::new();
    opts.optflag("h", "help", "Description");
    opts.optflag("i", "ignore", "Description");
    opts.optflag("r", "run", "Description");
    opts.long_only(false);

    let args: Vec<String> = ["-i", "-r", "10"].iter().map(|x| x.to_string()).collect();
    let matches = &match opts.parse(&args) {
        Ok(m) => m,
        Err(e) => panic!("{}", e),
    };
    assert_eq!(matches.opt_default("help", ""), None);
    assert_eq!(matches.opt_default("i", "def"), Some("def".to_string()));
}

#[test]
fn test_opt_get() {
    let mut opts = Options::new();
    opts.optflag("h", "help", "Description");
    opts.optflagopt("i", "ignore", "Description", "true | false");
    opts.optflagopt("r", "run", "Description", "0 .. 10");
    opts.optflagopt("p", "percent", "Description", "0.0 .. 10.0");
    opts.long_only(false);

    let args: Vec<String> = ["-i", "true", "-p", "1.1"]
        .iter()
        .map(|x| x.to_string())
        .collect();
    let matches = &match opts.parse(&args) {
        Ok(m) => m,
        Err(e) => panic!("{}", e),
    };
    let h_arg = matches.opt_get::<i32>("help");
    assert_eq!(h_arg, Ok(None));
    let i_arg = matches.opt_get("i");
    assert_eq!(i_arg, Ok(Some(true)));
    let p_arg = matches.opt_get("p");
    assert_eq!(p_arg, Ok(Some(1.1)));
}

#[test]
fn test_opt_get_default() {
    let mut opts = Options::new();
    opts.optflag("h", "help", "Description");
    opts.optflagopt("i", "ignore", "Description", "true | false");
    opts.optflagopt("r", "run", "Description", "0 .. 10");
    opts.optflagopt("p", "percent", "Description", "0.0 .. 10.0");
    opts.long_only(false);

    let args: Vec<String> = ["-i", "true", "-p", "1.1"]
        .iter()
        .map(|x| x.to_string())
        .collect();
    let matches = &match opts.parse(&args) {
        Ok(m) => m,
        Err(e) => panic!("{}", e),
    };
    let h_arg = matches.opt_get_default("help", 10);
    assert_eq!(h_arg, Ok(10));
    let i_arg = matches.opt_get_default("i", false);
    assert_eq!(i_arg, Ok(true));
    let p_arg = matches.opt_get_default("p", 10.2);
    assert_eq!(p_arg, Ok(1.1));
}

#[test]
fn test_opt_positions() {
    let mut opts = Options::new();
    opts.optflagmulti("a", "act", "Description");
    opts.optflagmulti("e", "enact", "Description");
    opts.optflagmulti("r", "react", "Description");

    let args: Vec<String> = ["-a", "-a", "-r", "-a", "-r", "-r"]
        .iter()
        .map(|x| x.to_string())
        .collect();

    let matches = &match opts.parse(&args) {
        Ok(m) => m,
        Err(e) => panic!("{}", e),
    };

    let a_pos = matches.opt_positions("a");
    assert_eq!(a_pos, vec![0, 1, 3]);
    let e_pos = matches.opt_positions("e");
    assert_eq!(e_pos, vec![]);
    let r_pos = matches.opt_positions("r");
    assert_eq!(r_pos, vec![2, 4, 5]);
}

#[test]
fn test_opt_strs_pos() {
    let mut opts = Options::new();
    opts.optmulti("a", "act", "Description", "NUM");
    opts.optmulti("e", "enact", "Description", "NUM");
    opts.optmulti("r", "react", "Description", "NUM");

    let args: Vec<String> = ["-a1", "-a2", "-r3", "-a4", "-r5", "-r6"]
        .iter()
        .map(|x| x.to_string())
        .collect();

    let matches = &match opts.parse(&args) {
        Ok(m) => m,
        Err(e) => panic!("{}", e),
    };

    let a_pos = matches.opt_strs_pos("a");
    assert_eq!(
        a_pos,
        vec![
            (0, "1".to_string()),
            (1, "2".to_string()),
            (3, "4".to_string())
        ]
    );
    let e_pos = matches.opt_strs_pos("e");
    assert_eq!(e_pos, vec![]);
    let r_pos = matches.opt_strs_pos("r");
    assert_eq!(
        r_pos,
        vec![
            (2, "3".to_string()),
            (4, "5".to_string()),
            (5, "6".to_string())
        ]
    );
}
