#[cfg(test)]
mod integration {
    use std::path;

    use assert_cmd::Command;
    use predicates::prelude::*;

    use sequoia_openpgp as openpgp;

    use openpgp::Fingerprint;
    use openpgp::Result;
    use openpgp::packet::UserID;

    fn dir() -> path::PathBuf {
        path::Path::new("tests").join("data")
    }

    fn test<S>(keyring: &str, trust_root: &Fingerprint,
               command: &str, args: &[&str],
               amount: usize,
               userid: Option<&UserID>,
               target: Option<&Fingerprint>,
               success: bool, output: &[(usize, S)]) -> Result<()>
        where S: AsRef<str>
    {
        let mut cmd = Command::cargo_bin("sq-wot")?;
        cmd.current_dir(&dir())
            .args(&[
                "--keyring", keyring,
                "--trust-root", &trust_root.to_string(),
                command,
                "--trust-amount", &format!("{}", amount),
            ]);
        if let Some(userid) = userid {
            cmd.arg(format!("{}", String::from_utf8_lossy(userid.value())));
        }
        if let Some(target) = target {
            cmd.arg(&target.to_string());
        }
        for arg in args {
            cmd.arg(arg);
        }

        let assertion = cmd.assert();

        if success {
            let assertion = assertion.success();

            let stdout
                = String::from_utf8_lossy(&assertion.get_output().stdout);
            for (expected_occurrences, s) in output {
                let s = s.as_ref();
                let occurrences = stdout.split(s).count() - 1;

                assert_eq!(occurrences, *expected_occurrences,
                           "Failed to find: '{}' {} times\n\
                            in stdout:\n\
                            {}",
                           s, *expected_occurrences, stdout);
            }
        } else {
            assertion.code(predicate::eq(1));
        }

        Ok(())
    }

    // Test authenticating a binding (User ID and certificate).
    #[test]
    #[allow(unused)]
    fn authenticate() -> Result<()> {
        let alice_fpr: Fingerprint =
            "85DAB65713B2D0ABFC5A4F28BC10C9CE4A699D8D"
           .parse().expect("valid fingerprint");
        let alice_uid
            = UserID::from("<alice@example.org>");

        let dave_fpr: Fingerprint =
            "329D5AAF73DC70B4E3DD2D11677CB70FFBFE1281"
           .parse().expect("valid fingerprint");
        let dave_uid
            = UserID::from("<dave@example.org>");

        let ellen_fpr: Fingerprint =
            "A7319A9B166AB530A5FBAC8AB43CA77F7C176AF4"
           .parse().expect("valid fingerprint");
        let ellen_uid
            = UserID::from("<ellen@example.org>");
        // Certified by: 329D5AAF73DC70B4E3DD2D11677CB70FFBFE1281

        let t = |amount: usize, userid: &UserID,
                 target: &Fingerprint, success: bool|
        {
            let output: &[(usize, &str)] = &[
                (1, &format!("[✓] {} {}: ", target, userid)),
                (1, "[✓] ")];
            test("simple.pgp", &alice_fpr, "authenticate", &[],
                 amount, Some(userid), Some(target),
                 success, output)
        };

        t(100, &dave_uid, &dave_fpr, true)?;
        t(120, &dave_uid, &dave_fpr, false)?;

        // Not enough depth.
        t(100, &ellen_uid, &ellen_fpr, false)?;
        t(120, &ellen_uid, &ellen_fpr, false)?;

        // No such User ID on dave's key.
        t(100, &ellen_uid, &dave_fpr, false)?;
        t(120, &ellen_uid, &dave_fpr, false)?;

        Ok(())
    }

    // Test authenticating bindings where we match on just the email
    // address, not the whole User ID.
    #[test]
    #[allow(unused)]
    fn authenticate_email() -> Result<()> {
        let alice_fpr: Fingerprint =
            "85DAB65713B2D0ABFC5A4F28BC10C9CE4A699D8D"
           .parse().expect("valid fingerprint");
        let alice_uid
            = UserID::from("<alice@example.org>");

        let dave_fpr: Fingerprint =
            "329D5AAF73DC70B4E3DD2D11677CB70FFBFE1281"
           .parse().expect("valid fingerprint");
        let dave_uid
            = UserID::from("<dave@example.org>");
        let dave_email = UserID::from("dave@example.org");
        let dave_email_uc1 = UserID::from("DAVE@example.org");
        let dave_email_uc2 = UserID::from("DAVE@EXAMPLE.ORG");

        let ellen_fpr: Fingerprint =
            "A7319A9B166AB530A5FBAC8AB43CA77F7C176AF4"
           .parse().expect("valid fingerprint");
        let ellen_uid
            = UserID::from("<ellen@example.org>");
        let ellen_email = UserID::from("ellen@example.org");
        // Certified by: 329D5AAF73DC70B4E3DD2D11677CB70FFBFE1281

        let t = |amount: usize, pattern: &UserID,
                 userid: &UserID, target: &Fingerprint, success: bool|
        {
            let output: &[(usize, &str)] = &[
                (1, &format!("[✓] {} {}: ", target, userid)),
                (1, "[✓] ")];
            test("simple.pgp", &alice_fpr,
                 "authenticate", &["--email"],
                 amount, Some(pattern), Some(target),
                 success, output)
        };

        t(100, &dave_uid, &dave_uid, &dave_fpr, false)?;
        t(100, &dave_email, &dave_uid, &dave_fpr, true)?;
        t(120, &dave_uid, &dave_uid, &dave_fpr, false)?;
        t(120, &dave_email, &dave_uid, &dave_fpr, false)?;

        // Not enough depth.
        t(100, &ellen_uid, &ellen_uid, &ellen_fpr, false)?;
        t(100, &ellen_email, &ellen_uid, &ellen_fpr, false)?;
        t(120, &ellen_uid, &ellen_uid, &ellen_fpr, false)?;
        t(120, &ellen_email, &ellen_uid, &ellen_fpr, false)?;

        // No such User ID on dave's key.
        t(100, &ellen_uid, &ellen_uid, &dave_fpr, false)?;
        t(100, &ellen_email, &ellen_uid, &dave_fpr, false)?;
        t(120, &ellen_uid, &ellen_uid, &dave_fpr, false)?;
        t(120, &ellen_email, &ellen_uid, &dave_fpr, false)?;

        // Normalized.
        t(100, &dave_email_uc1, &dave_uid, &dave_fpr, true)?;
        t(100, &dave_email_uc2, &dave_uid, &dave_fpr, true)?;


        // Puny code and case normalization.
        let alice_fpr: Fingerprint =
            "B8DA8B318149B1C8C0CBD1ECB1CEC6D3CD00E69D"
           .parse().expect("valid fingerprint");
        let alice_uid
            = UserID::from("<alice@example.org>");
        let alice_email
            = UserID::from("alice@example.org");

        let hans_fpr: Fingerprint =
            "74767C4F2B15F57F3394FCA99DE867E6CA6A2756"
           .parse().expect("valid fingerprint");
        let hans_uid
            = UserID::from("<hÄNS@bücher.tld>");
        // Certified by: B8DA8B318149B1C8C0CBD1ECB1CEC6D3CD00E69D

        let hans_email
            = UserID::from("hÄNS@bücher.tld");
        let hans_email_lowercase
            = UserID::from("häns@bücher.tld");
        let hans_email_punycode
            = UserID::from("hÄNS@xn--bcher-kva.tld");
        let hans_email_punycode_lowercase
            = UserID::from("häns@xn--bcher-kva.tld");

        let carol_fpr: Fingerprint =
            "7432C123761B94EC50D50CF6562B9ADEE7F789F6"
           .parse().expect("valid fingerprint");
        let carol_uid
            = UserID::from("<carol@example.org>");
        // Certified by: 74767C4F2B15F57F3394FCA99DE867E6CA6A2756

        let carol_email
            = UserID::from("carol@example.org");

        let t = |amount: usize, pattern: &UserID,
                 userid: &UserID, target: &Fingerprint, success: bool|
        {
            let output: &[(usize, &str)] = &[
                (1, &format!("[✓] {} {}: ", target, userid)),
                (1, "[✓] ")];
            test("puny-code.pgp", &alice_fpr,
                 "authenticate", &["--email"],
                 amount, Some(pattern), Some(target),
                 success, output)
        };

        t(120, &alice_email, &alice_uid, &alice_fpr, true)?;

        t(100, &hans_email, &hans_uid, &hans_fpr, true)?;
        t(100, &hans_email_lowercase, &hans_uid, &hans_fpr, true)?;
        t(100, &hans_email_punycode, &hans_uid, &hans_fpr, true)?;
        t(100, &hans_email_punycode_lowercase, &hans_uid, &hans_fpr, true)?;

        t(100, &carol_email, &carol_uid, &carol_fpr, true)?;

        Ok(())
    }

    // Test looking up a certificate by User ID.
    #[test]
    #[allow(unused)]
    fn lookup() -> Result<()> {
        let alice_fpr: Fingerprint =
            "85DAB65713B2D0ABFC5A4F28BC10C9CE4A699D8D"
           .parse().expect("valid fingerprint");
        let alice_uid = UserID::from("<alice@example.org>");
        let alice_uid_uppercase = UserID::from("<ALICE@EXAMPLE.ORG>");
        let alice_uid_uppercase2 = UserID::from("<alice@EXAMPLE.ORG>");

        let dave_fpr: Fingerprint =
            "329D5AAF73DC70B4E3DD2D11677CB70FFBFE1281"
           .parse().expect("valid fingerprint");
        let dave_uid = UserID::from("<dave@example.org>");
        let dave_uid_uppercase = UserID::from("<DAVE@EXAMPLE.ORG>");
        let dave_uid_uppercase2 = UserID::from("<dave@EXAMPLE.ORG>");

        let ellen_fpr: Fingerprint =
            "A7319A9B166AB530A5FBAC8AB43CA77F7C176AF4"
           .parse().expect("valid fingerprint");
        let ellen_uid
            = UserID::from("<ellen@example.org>");
        // Certified by: 329D5AAF73DC70B4E3DD2D11677CB70FFBFE1281

        let t = |amount: usize, pattern: &UserID,
                 userid: &UserID, target: &Fingerprint,
                 success: bool|
        {
            let output: &[(usize, &str)] = &[
                (1, &format!("[✓] {} {}: ", target, userid)),
                (1, "[✓] ")];

            test("simple.pgp", &alice_fpr, "lookup", &[],
                 amount, Some(pattern), None,
                 success, output)
        };

        t(100, &alice_uid, &alice_uid, &alice_fpr, true)?;
        t(120, &alice_uid, &alice_uid, &alice_fpr, true)?;

        t(100, &dave_uid, &dave_uid, &dave_fpr, true)?;
        t(120, &dave_uid, &dave_uid, &dave_fpr, false)?;

        // Not enough depth.
        t(100, &ellen_uid, &ellen_uid, &ellen_fpr, false)?;
        t(120, &ellen_uid, &ellen_uid, &ellen_fpr, false)?;

        // No such User ID.
        let gary = UserID::from("Gary <gary@some.org>");
        t(100, &gary, &gary, &dave_fpr, false)?;

        // We need an exact match.
        t(100, &alice_uid_uppercase, &alice_uid, &alice_fpr, false)?;
        t(100, &alice_uid_uppercase2, &alice_uid, &alice_fpr, false)?;
        t(100, &dave_uid_uppercase, &dave_uid, &dave_fpr, false)?;
        t(100, &dave_uid_uppercase2, &dave_uid, &dave_fpr, false)?;

        Ok(())
    }

    // Test looking up a certificate by email address.
    #[test]
    #[allow(unused)]
    fn lookup_email() -> Result<()> {
        let alice_fpr: Fingerprint =
            "85DAB65713B2D0ABFC5A4F28BC10C9CE4A699D8D"
           .parse().expect("valid fingerprint");
        let alice_uid = UserID::from("<alice@example.org>");
        let alice_email = UserID::from("alice@example.org");

        let dave_fpr: Fingerprint =
            "329D5AAF73DC70B4E3DD2D11677CB70FFBFE1281"
           .parse().expect("valid fingerprint");
        let dave_uid = UserID::from("<dave@example.org>");
        let dave_email = UserID::from("dave@example.org");

        let ellen_fpr: Fingerprint =
            "A7319A9B166AB530A5FBAC8AB43CA77F7C176AF4"
           .parse().expect("valid fingerprint");
        let ellen_uid
            = UserID::from("<ellen@example.org>");
        let ellen_email = UserID::from("ellen@example.org");
        // Certified by: 329D5AAF73DC70B4E3DD2D11677CB70FFBFE1281

        let t = |amount: usize, pattern: &UserID,
                 userid: &UserID, target: &Fingerprint,
                 success: bool|
        {
            let output: &[(usize, &str)] = &[
                (1, &format!("[✓] {} {}: ", target, userid)),
                (1, "[✓] ")];

            test("simple.pgp", &alice_fpr, "lookup", &["--email"],
                 amount, Some(pattern), None,
                 success, output)
        };

        t(100, &alice_email, &alice_uid, &alice_fpr, true)?;
        t(120, &alice_email, &alice_uid, &alice_fpr, true)?;

        t(100, &dave_email, &dave_uid, &dave_fpr, true)?;
        t(120, &dave_email, &dave_uid, &dave_fpr, false)?;

        // Not enough depth.
        t(100, &ellen_email, &ellen_uid, &ellen_fpr, false)?;
        t(120, &ellen_email, &ellen_uid, &ellen_fpr, false)?;

        // No such User ID.
        let gary = UserID::from("gary@some.org");
        t(100, &gary, &gary, &dave_fpr, false)?;

        Ok(())
    }

    // Test identifying a certificate.
    #[test]
    #[allow(unused)]
    fn identify() -> Result<()> {
        let alice_fpr: Fingerprint =
            "2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA"
           .parse().expect("valid fingerprint");
        let alice_uid
            = UserID::from("<alice@example.org>");

        let bob_fpr: Fingerprint =
            "03182611B91B1E7E20B848E83DFC151ABFAD85D5"
           .parse().expect("valid fingerprint");
        let bob_uid
            = UserID::from("<bob@other.org>");
        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA
        let bob_some_org_uid
            = UserID::from("<bob@some.org>");
        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA

        let carol_fpr: Fingerprint =
            "9CA36907B46FE7B6B9EE9601E78064C12B6D7902"
           .parse().expect("valid fingerprint");
        let carol_uid
            = UserID::from("<carol@example.org>");
        // Certified by: 03182611B91B1E7E20B848E83DFC151ABFAD85D5

        let dave_fpr: Fingerprint =
            "C1BC6794A6C6281B968A6A41ACE2055D610CEA03"
           .parse().expect("valid fingerprint");
        let dave_uid
            = UserID::from("<dave@other.org>");
        // Certified by: 9CA36907B46FE7B6B9EE9601E78064C12B6D7902


        let t = |amount: usize, userids: &[&UserID], target: &Fingerprint,
                 success: bool|
        {
            let output = userids
                .iter()
                .map(|userid| {
                    (1, format!("[✓] {} {}: ", target, userid))
                })
                .chain(vec![(userids.len(), String::from("[✓] "))].into_iter())
                .collect::<Vec<_>>();
            test("multiple-userids-1.pgp", &alice_fpr, "identify", &[],
                 amount, None, Some(target),
                 success, &output)
        };

        t(100, &[&alice_uid], &alice_fpr, true)?;
        t(120, &[&alice_uid], &alice_fpr, true)?;

        t(50, &[&dave_uid], &dave_fpr, true)?;
        t(120, &[&dave_uid], &dave_fpr, false)?;

        t(50, &[&bob_uid, &bob_some_org_uid], &bob_fpr, true)?;
        t(120, &[&bob_uid, &bob_some_org_uid], &bob_fpr, false)?;

        Ok(())
    }

    // List all authenticated bindings.
    #[test]
    #[allow(unused)]
    fn list() -> Result<()> {
        let alice_fpr: Fingerprint =
            "85DAB65713B2D0ABFC5A4F28BC10C9CE4A699D8D"
           .parse().expect("valid fingerprint");
        let alice_uid
            = UserID::from("<alice@example.org>");

        let bob_fpr: Fingerprint =
            "39A479816C934B9E0464F1F4BC1DCFDEADA4EE90"
           .parse().expect("valid fingerprint");
        let bob_uid
            = UserID::from("<bob@example.org>");
        // Certified by: 85DAB65713B2D0ABFC5A4F28BC10C9CE4A699D8D

        let carol_fpr: Fingerprint =
            "43530F91B450EDB269AA58821A1CF4DC7F500F04"
           .parse().expect("valid fingerprint");
        let carol_uid
            = UserID::from("<carol@example.org>");
        // Certified by: 39A479816C934B9E0464F1F4BC1DCFDEADA4EE90

        let dave_fpr: Fingerprint =
            "329D5AAF73DC70B4E3DD2D11677CB70FFBFE1281"
           .parse().expect("valid fingerprint");
        let dave_uid
            = UserID::from("<dave@example.org>");
        // Certified by: 43530F91B450EDB269AA58821A1CF4DC7F500F04

        let ellen_fpr: Fingerprint =
            "A7319A9B166AB530A5FBAC8AB43CA77F7C176AF4"
           .parse().expect("valid fingerprint");
        let ellen_uid
            = UserID::from("<ellen@example.org>");
        // Certified by: 329D5AAF73DC70B4E3DD2D11677CB70FFBFE1281

        let frank_fpr: Fingerprint =
            "2693237D2CED0BB68F118D78DC86A97CD2C819D9"
           .parse().expect("valid fingerprint");
        let frank_uid
            = UserID::from("<frank@example.org>");

        let t = |amount: usize, bindings: &[(&UserID, &Fingerprint)],
                 success: bool|
        {
            let output = bindings
                .iter()
                .map(|(userid, target)| {
                    (1, format!("[✓] {} {}: ", target, userid))
                })
                .chain(vec![(bindings.len(), String::from("[✓] "))].into_iter())
                .collect::<Vec<_>>();
            test("simple.pgp", &alice_fpr, "list", &[],
                 amount, None, None,
                 success, &output)
        };

        t(120,
          &[(&alice_uid, &alice_fpr)],
          true)?;

        t(50,
          &[(&alice_uid, &alice_fpr),
            (&bob_uid, &bob_fpr),
            (&carol_uid, &carol_fpr),
            (&dave_uid, &dave_fpr),
          ],
          true)?;

        Ok(())
    }

    // List all authenticated bindings matching a pattern.
    #[test]
    #[allow(unused)]
    fn list_pattern() -> Result<()> {
        let alice_fpr: Fingerprint =
            "2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA"
           .parse().expect("valid fingerprint");
        let alice_uid
            = UserID::from("<alice@example.org>");

        let bob_fpr: Fingerprint =
            "03182611B91B1E7E20B848E83DFC151ABFAD85D5"
           .parse().expect("valid fingerprint");
        let bob_uid
            = UserID::from("<bob@other.org>");
        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA
        let bob_some_org_uid
            = UserID::from("<bob@some.org>");
        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA

        let carol_fpr: Fingerprint =
            "9CA36907B46FE7B6B9EE9601E78064C12B6D7902"
           .parse().expect("valid fingerprint");
        let carol_uid
            = UserID::from("<carol@example.org>");
        // Certified by: 03182611B91B1E7E20B848E83DFC151ABFAD85D5

        let dave_fpr: Fingerprint =
            "C1BC6794A6C6281B968A6A41ACE2055D610CEA03"
           .parse().expect("valid fingerprint");
        let dave_uid
            = UserID::from("<dave@other.org>");
        // Certified by: 9CA36907B46FE7B6B9EE9601E78064C12B6D7902


        let t = |amount: usize, pattern: &str,
                 bindings: &[(&UserID, &Fingerprint)],
                 success: bool|
        {
            let output = bindings
                .iter()
                .map(|(userid, target)| {
                    (1, format!("[✓] {} {}: ", target, userid))
                })
                .chain(vec![(bindings.len(), String::from("[✓] "))].into_iter())
                .collect::<Vec<_>>();
            test("multiple-userids-1.pgp", &alice_fpr, "list", &[pattern],
                 amount, None, None,
                 success, &output)
        };

        t(50, "bob",
          &[(&bob_uid, &bob_fpr),
            (&bob_some_org_uid, &bob_fpr)
          ],
          true)?;

        t(50, "BOB",
          &[(&bob_uid, &bob_fpr),
            (&bob_some_org_uid, &bob_fpr)
          ],
          true)?;

        t(50, "@example.org",
          &[(&alice_uid, &alice_fpr),
            (&carol_uid, &carol_fpr),
          ],
          true)?;

        t(50, "@EXAMPLE.ORG",
          &[(&alice_uid, &alice_fpr),
            (&carol_uid, &carol_fpr),
          ],
          true)?;

        t(50, "@OTHER.ORG",
          &[(&bob_uid, &bob_fpr),
            (&dave_uid, &dave_fpr),
          ],
          true)?;

        t(50, "ORG",
          &[(&alice_uid, &alice_fpr),
            (&bob_uid, &bob_fpr),
            (&bob_some_org_uid, &bob_fpr),
            (&carol_uid, &carol_fpr),
            (&dave_uid, &dave_fpr),
          ],
          true)?;


        // Puny code.
        let alice_fpr: Fingerprint =
            "B8DA8B318149B1C8C0CBD1ECB1CEC6D3CD00E69D"
           .parse().expect("valid fingerprint");
        let alice_uid
            = UserID::from("<alice@example.org>");
        let alice_email
            = UserID::from("alice@example.org");

        let hans_fpr: Fingerprint =
            "74767C4F2B15F57F3394FCA99DE867E6CA6A2756"
           .parse().expect("valid fingerprint");
        let hans_uid
            = UserID::from("<hÄNS@bücher.tld>");
        // Certified by: B8DA8B318149B1C8C0CBD1ECB1CEC6D3CD00E69D

        let hans_email = "hÄNS@bücher.tld";
        let hans_email_punycode = "hÄNS@xn--bcher-kva.tld";
        let hans_email_punycode_lowercase = "häns@xn--bcher-kva.tld";

        let carol_fpr: Fingerprint =
            "7432C123761B94EC50D50CF6562B9ADEE7F789F6"
           .parse().expect("valid fingerprint");
        let carol_uid
            = UserID::from("<carol@example.org>");
        // Certified by: 74767C4F2B15F57F3394FCA99DE867E6CA6A2756

        let carol_email
            = UserID::from("carol@example.org");

        let t = |amount: usize, pattern: &str,
                 bindings: &[(&UserID, &Fingerprint)],
                 success: bool|
        {
            let output = bindings
                .iter()
                .map(|(userid, target)| {
                    (1, format!("[✓] {} {}: ", target, userid))
                })
                .chain(vec![(bindings.len(), String::from("[✓] "))].into_iter())
                .collect::<Vec<_>>();
            test("puny-code.pgp", &alice_fpr, "list", &[pattern],
                 amount, None, None,
                 success, &output)
        };

        // If we don't provide --email, then we only case
        // insensitively match on the raw User ID; we don't perform
        // puny code normalization.
        t(100, "bücher.tld",
          &[(&hans_uid, &hans_fpr)],
          true)?;

        t(100, "BÜCHER.TLD",
          &[(&hans_uid, &hans_fpr)],
          true)?;

        t(100, hans_email,
          &[(&hans_uid, &hans_fpr)],
          true)?;

        t(100, &format!("<{}>", hans_email),
          &[(&hans_uid, &hans_fpr)],
          true)?;

        t(100, hans_email_punycode,
          &[(&hans_uid, &hans_fpr)],
          false)?;

        t(100, hans_email_punycode_lowercase,
          &[(&hans_uid, &hans_fpr)],
          false)?;


        Ok(())
    }

    // List all authenticated bindings where the email address matches
    // a pattern.
    #[test]
    #[allow(unused)]
    fn list_email_pattern() -> Result<()> {
        // Puny code and case normalization.
        let alice_fpr: Fingerprint =
            "B8DA8B318149B1C8C0CBD1ECB1CEC6D3CD00E69D"
           .parse().expect("valid fingerprint");
        let alice_uid
            = UserID::from("<alice@example.org>");
        let alice_email
            = UserID::from("alice@example.org");

        let hans_fpr: Fingerprint =
            "74767C4F2B15F57F3394FCA99DE867E6CA6A2756"
           .parse().expect("valid fingerprint");
        let hans_uid
            = UserID::from("<hÄNS@bücher.tld>");
        // Certified by: B8DA8B318149B1C8C0CBD1ECB1CEC6D3CD00E69D

        let hans_email = "hÄNS@bücher.tld";
        let hans_email_punycode = "hÄNS@xn--bcher-kva.tld";
        let hans_email_punycode_lowercase = "häns@xn--bcher-kva.tld";

        let carol_fpr: Fingerprint =
            "7432C123761B94EC50D50CF6562B9ADEE7F789F6"
           .parse().expect("valid fingerprint");
        let carol_uid
            = UserID::from("<carol@example.org>");
        // Certified by: 74767C4F2B15F57F3394FCA99DE867E6CA6A2756

        let carol_email
            = UserID::from("carol@example.org");

        let t = |amount: usize, pattern: &str,
                 bindings: &[(&UserID, &Fingerprint)],
                 success: bool|
        {
            let output = bindings
                .iter()
                .map(|(userid, target)| {
                    (1, format!("[✓] {} {}: ", target, userid))
                })
                .chain(vec![(bindings.len(), String::from("[✓] "))].into_iter())
                .collect::<Vec<_>>();
            test("puny-code.pgp", &alice_fpr, "list", &["--email", pattern],
                 amount, None, None,
                 success, &output)
        };

        t(100, "bücher.tld",
          &[(&hans_uid, &hans_fpr)],
          true)?;

        t(100, "BÜCHER.TLD",
          &[(&hans_uid, &hans_fpr)],
          true)?;

        t(100, hans_email,
          &[(&hans_uid, &hans_fpr)],
          true)?;

        t(100, &format!("<{}>", hans_email),
          &[(&hans_uid, &hans_fpr)],
          false)?;

        t(100, hans_email_punycode,
          &[(&hans_uid, &hans_fpr)],
          true)?;

        t(100, hans_email_punycode_lowercase,
          &[(&hans_uid, &hans_fpr)],
          true)?;

        Ok(())
    }
}
