<commit_before>use delivery::git::git_command;
use delivery::utils::copy_recursive;
use delivery::utils::say;
use std::io::prelude::*;
use tempdir::TempDir;
use std::fs::File;
use std::path::Path;
use support::paths::fixture_file;
use std::process::{Command, Output};
use std::env;
use rustc_serialize::json::Json;
use delivery::utils::path_join_many::PathJoinMany;

// ** Functions used in tests **

fn setup() {
    say::turn_off_spinner();
}

/// Sets up a mock delivery git project from the test_repo fixture.
/// Includes copying in the `.delivery/config.json` that you plan
/// on using.
fn setup_mock_delivery_project_git(dot_config: &str) -> TempDir {
    let tmpdir = TempDir::new("mock-delivery-remote").unwrap();
    let test_repo_path = fixture_file("test_repo");
    panic_on_error!(copy_recursive(&test_repo_path.join(".delivery"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&test_repo_path.join("README.md"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&test_repo_path.join("cookbooks"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&fixture_file(dot_config), &tmpdir.path().join_many(&[".delivery", "config.json"])));
    panic_on_error!(git_command(&["init", tmpdir.path().to_str().unwrap()], tmpdir.path()));
    panic_on_error!(git_command(&["add", "."], tmpdir.path()));
    panic_on_error!(git_command(&["commit", "-a", "-m", "Initial Commit"], tmpdir.path()));
    tmpdir
}

/// Given a path, it copies the build cookbook into it, and turns it
/// into a git repository
fn setup_build_cookbook_project(tmpdir: &Path) {
    let build_cookbook_path = fixture_file("delivery_test");
    panic_on_error!(copy_recursive(&build_cookbook_path, &tmpdir.to_path_buf()));
    panic_on_error!(git_command(&["init", tmpdir.join("delivery_test").to_str().unwrap()], &tmpdir.join("delivery_test")));
    panic_on_error!(git_command(&["add", "."], &tmpdir.join("delivery_test")));
    panic_on_error!(git_command(&["commit", "-a", "-m", "Initial Commit"], &tmpdir.join("delivery_test")));
}

/// Clones a mock delivery git project to a local copy, as if it was
/// on a workstation. Also creates a mock delivery remote pointing at
/// the on-disk mocked delivery project.
fn setup_local_project_clone(delivery_project_git: &Path) -> TempDir {
    let tmpdir = TempDir::new("local-project").unwrap();
    panic_on_error!(git_command(&["clone",
                                  delivery_project_git.to_str().unwrap(),
                                  tmpdir.path().to_str().unwrap()
                                 ], tmpdir.path()));
    panic_on_error!(git_command(&["remote", "add", "delivery", delivery_project_git.to_str().unwrap()], tmpdir.path()));
    let mut command = delivery_cmd();
    command.arg("setup")
           .arg("--user").arg("cavalera")
           .arg("--server").arg("localhost")
           .arg("--ent").arg("family")
           .arg("--org").arg("sepultura")
           .arg("--for").arg("master")
           .arg("--config-path").arg(tmpdir.path().to_str().unwrap());
    assert_command_successful(&mut command, &tmpdir);
    tmpdir
}

/// Makes a change to a project on the named branch. Creates a
/// file named `filename` and writes some stuff to it.
///
/// When it returns, the project you pass in will be left on your
/// new branch.
fn setup_change(tmpdir: &Path, branch: &str, filename: &str) {
    panic_on_error!(git_command(&["checkout", "master"], tmpdir));
    panic_on_error!(git_command(&["branch", branch], tmpdir));
    {
        let mut f = panic_on_error!(File::create(&tmpdir.join(filename)));
        panic_on_error!(f.write_all(b"I like cookies"));
    }
    panic_on_error!(git_command(&["add", filename], tmpdir));
    panic_on_error!(git_command(&["commit", "-a", "-m", filename], tmpdir));
}

/// Checks out the named branch
fn setup_checkout_branch(tmpdir: &Path, branch: &str) {
    panic_on_error!(git_command(&["checkout", branch], tmpdir));
}

/// Runs the `Command` in `Dir`, and makes sure it exists with 0
/// Returns the result
fn assert_command_successful(command: &mut Command, dir: &TempDir) -> Output {
    let result = panic_on_error!(command.current_dir(&dir.path()).output());
    if ! result.status.success() {
        let output = String::from_utf8_lossy(&result.stdout);
        let error = String::from_utf8_lossy(&result.stderr);
        panic!("Failed command {:?}\nOUT: {}\nERR: {}\nPath: {}", command, &output, &error, dir.path().to_str().unwrap());
    };
    result
}

/// Same as `assert_command_successful` above, excepts it asserts the command
/// exits with a non-0 status code
fn assert_command_failed(command: &mut Command, dir: &TempDir) -> Output {
    let result = panic_on_error!(command.current_dir(&dir.path()).output());
    if result.status.success() {
        let output = String::from_utf8_lossy(&result.stdout);
        let error = String::from_utf8_lossy(&result.stderr);
        panic!("Command {:?} should have failed!\nOUT: {}\nERR: {}\nPath: {}", command, &output, &error, dir.path().to_str().unwrap());
    };
    result
}

/// Builds the command to run delivery review
fn delivery_review_command(pipeline: &str) -> Command {
    let mut command = delivery_cmd();
    command.arg("review").arg("--no-open").arg("--for").arg(pipeline);
    command
}

/// Builds the command to run a sample job
fn delivery_verify_command(job_root: &TempDir) -> Command {
    let mut command = delivery_cmd();
    command.arg("job")
           .arg("verify")
           .arg("unit")
           .arg("--no-spinner")
           .arg("--job-root").arg(job_root.path().to_str().unwrap());
    command
}

/// Calls delivery review, and creates the two stub branches that the
/// api would create (`_reviews/PIPELINE/BRANCH/1` and `_reviews/PIPELINE/BRANCH/latest`)
fn delivery_review(local: &TempDir, remote: &TempDir, branch: &str, pipeline: &str) {
    panic_on_error!(git_command(&["checkout", branch], local.path()));
    let mut command = delivery_review_command(pipeline);
    assert_command_successful(&mut command, local);

    // Stub out the behavior of the delivery-api
    panic_on_error!(git_command(&["branch", &format!("_reviews/{}/{}/1", pipeline, branch)], remote.path()));
    panic_on_error!(git_command(&["branch", &format!("_reviews/{}/{}/latest", pipeline, branch)], remote.path()));
}

/// Returns a Command set to the delivery binary created when you
/// ran `cargo test`.
fn delivery_cmd() -> Command {
    let mut delivery_path = env::current_exe().unwrap();
    delivery_path.pop();
    Command::new(delivery_path.join("delivery").to_str().unwrap())
}

/// A handy debugging function. Insert it when you want to sleep,
/// pass it a tmpdir, and you can inspect it.
///
/// Make sure you run `cargo test -- --nocapture` to see the output.
#[allow(dead_code)]
fn debug_sleep(tmpdir: &TempDir) {
    println!("Sleeping for 1000 seconds for {:?}", tmpdir.path());
    panic_on_error!(Command::new("sleep").arg("1000").output());
}

// ** Actual tests **

// Tests `delivery review`. Fails if the command fails, or if we fail to create
// the remote branch _for/master/rust/test, which is what we need to push to
// the API server.
test!(review {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_with_a_v1_config {
    let delivery_project_git = setup_mock_delivery_project_git("v1_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_without_dependencies {
    let delivery_project_git = setup_mock_delivery_project_git("no_deps_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_with_an_invalid_config {
    let delivery_project_git = setup_mock_delivery_project_git("invalid_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_review_command("rust/test");
    assert_command_failed(&mut command, &local_project);
});

test!(job_verify_unit_with_path_config {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project);
});

test!(job_verify_unit_with_git_config {
    let delivery_project_git = setup_mock_delivery_project_git("git_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_build_cookbook_project(&job_root.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project);
});

// TODO: This test requires internet access... we should move it out
// into an Acceptance-stage test instead of here in unit tests. It's
// impossible to run on a plane, for instance :(
test!(job_verify_unit_with_supermarket_config {
    let delivery_project_git = setup_mock_delivery_project_git("supermarket_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_failed(&mut command, &local_project);
    assert!(job_root.path().join_many(&["chef", "cookbooks", "httpd"]).is_dir());
    assert!(job_root.path().join_many(&["chef", "cookbooks", "httpd", "templates", "default", "magic.erb"]).is_file());
});

test!(job_verify_dna_json {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project);
    let mut dna_file = panic_on_error!(File::open(&job_root.path().join_many(&["chef", "dna.json"])));
    let mut dna_json = String::new();
    panic_on_error!(dna_file.read_to_string(&mut dna_json));
    let dna_data = panic_on_error!(Json::from_str(&dna_json));
    match dna_data.find_path(&["delivery", "workspace", "repo"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("repo").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/repo, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "chef"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("chef").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/chef, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "cache"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("cache").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/cache, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "root"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().to_str().unwrap());
        },
        None => panic!("No delivery/workspace/root, {}", dna_data)
    };
    match dna_data.find_path(&["delivery_builder", "build_user"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), "dbuild");
        },
        None => panic!("No delivery_builderl/build_user, {}", dna_data)
    };
});
<commit_msg>Path not TempDir for assert_command_successful<commit_after>use delivery::git::git_command;
use delivery::utils::copy_recursive;
use delivery::utils::say;
use std::io::prelude::*;
use tempdir::TempDir;
use std::fs::File;
use std::path::Path;
use support::paths::fixture_file;
use std::process::{Command, Output};
use std::env;
use rustc_serialize::json::Json;
use delivery::utils::path_join_many::PathJoinMany;

// ** Functions used in tests **

fn setup() {
    say::turn_off_spinner();
}

/// Sets up a mock delivery git project from the test_repo fixture.
/// Includes copying in the `.delivery/config.json` that you plan
/// on using.
fn setup_mock_delivery_project_git(dot_config: &str) -> TempDir {
    let tmpdir = TempDir::new("mock-delivery-remote").unwrap();
    let test_repo_path = fixture_file("test_repo");
    panic_on_error!(copy_recursive(&test_repo_path.join(".delivery"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&test_repo_path.join("README.md"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&test_repo_path.join("cookbooks"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&fixture_file(dot_config), &tmpdir.path().join_many(&[".delivery", "config.json"])));
    panic_on_error!(git_command(&["init", tmpdir.path().to_str().unwrap()], tmpdir.path()));
    panic_on_error!(git_command(&["add", "."], tmpdir.path()));
    panic_on_error!(git_command(&["commit", "-a", "-m", "Initial Commit"], tmpdir.path()));
    tmpdir
}

/// Given a path, it copies the build cookbook into it, and turns it
/// into a git repository
fn setup_build_cookbook_project(tmpdir: &Path) {
    let build_cookbook_path = fixture_file("delivery_test");
    panic_on_error!(copy_recursive(&build_cookbook_path, &tmpdir.to_path_buf()));
    panic_on_error!(git_command(&["init", tmpdir.join("delivery_test").to_str().unwrap()], &tmpdir.join("delivery_test")));
    panic_on_error!(git_command(&["add", "."], &tmpdir.join("delivery_test")));
    panic_on_error!(git_command(&["commit", "-a", "-m", "Initial Commit"], &tmpdir.join("delivery_test")));
}

/// Clones a mock delivery git project to a local copy, as if it was
/// on a workstation. Also creates a mock delivery remote pointing at
/// the on-disk mocked delivery project.
fn setup_local_project_clone(delivery_project_git: &Path) -> TempDir {
    let tmpdir = TempDir::new("local-project").unwrap();
    panic_on_error!(git_command(&["clone",
                                  delivery_project_git.to_str().unwrap(),
                                  tmpdir.path().to_str().unwrap()
                                 ], tmpdir.path()));
    panic_on_error!(git_command(&["remote", "add", "delivery", delivery_project_git.to_str().unwrap()], tmpdir.path()));
    let mut command = delivery_cmd();
    command.arg("setup")
           .arg("--user").arg("cavalera")
           .arg("--server").arg("localhost")
           .arg("--ent").arg("family")
           .arg("--org").arg("sepultura")
           .arg("--for").arg("master")
           .arg("--config-path").arg(tmpdir.path().to_str().unwrap());
    assert_command_successful(&mut command, &tmpdir.path());
    tmpdir
}

/// Makes a change to a project on the named branch. Creates a
/// file named `filename` and writes some stuff to it.
///
/// When it returns, the project you pass in will be left on your
/// new branch.
fn setup_change(tmpdir: &Path, branch: &str, filename: &str) {
    panic_on_error!(git_command(&["checkout", "master"], tmpdir));
    panic_on_error!(git_command(&["branch", branch], tmpdir));
    {
        let mut f = panic_on_error!(File::create(&tmpdir.join(filename)));
        panic_on_error!(f.write_all(b"I like cookies"));
    }
    panic_on_error!(git_command(&["add", filename], tmpdir));
    panic_on_error!(git_command(&["commit", "-a", "-m", filename], tmpdir));
}

/// Checks out the named branch
fn setup_checkout_branch(tmpdir: &Path, branch: &str) {
    panic_on_error!(git_command(&["checkout", branch], tmpdir));
}

/// Runs the `Command` in `Dir`, and makes sure it exists with 0
/// Returns the result
fn assert_command_successful(command: &mut Command, dir: &Path) -> Output {
    let result = panic_on_error!(command.current_dir(&dir).output());
    if ! result.status.success() {
        let output = String::from_utf8_lossy(&result.stdout);
        let error = String::from_utf8_lossy(&result.stderr);
        panic!("Failed command {:?}\nOUT: {}\nERR: {}\nPath: {}", command, &output, &error, dir.to_str().unwrap());
    };
    result
}

/// Same as `assert_command_successful` above, excepts it asserts the command
/// exits with a non-0 status code
fn assert_command_failed(command: &mut Command, dir: &TempDir) -> Output {
    let result = panic_on_error!(command.current_dir(&dir.path()).output());
    if result.status.success() {
        let output = String::from_utf8_lossy(&result.stdout);
        let error = String::from_utf8_lossy(&result.stderr);
        panic!("Command {:?} should have failed!\nOUT: {}\nERR: {}\nPath: {}", command, &output, &error, dir.path().to_str().unwrap());
    };
    result
}

/// Builds the command to run delivery review
fn delivery_review_command(pipeline: &str) -> Command {
    let mut command = delivery_cmd();
    command.arg("review").arg("--no-open").arg("--for").arg(pipeline);
    command
}

/// Builds the command to run a sample job
fn delivery_verify_command(job_root: &TempDir) -> Command {
    let mut command = delivery_cmd();
    command.arg("job")
           .arg("verify")
           .arg("unit")
           .arg("--no-spinner")
           .arg("--job-root").arg(job_root.path().to_str().unwrap());
    command
}

/// Calls delivery review, and creates the two stub branches that the
/// api would create (`_reviews/PIPELINE/BRANCH/1` and `_reviews/PIPELINE/BRANCH/latest`)
fn delivery_review(local: &TempDir, remote: &TempDir, branch: &str, pipeline: &str) {
    panic_on_error!(git_command(&["checkout", branch], local.path()));
    let mut command = delivery_review_command(pipeline);
    assert_command_successful(&mut command, local.path());

    // Stub out the behavior of the delivery-api
    panic_on_error!(git_command(&["branch", &format!("_reviews/{}/{}/1", pipeline, branch)], remote.path()));
    panic_on_error!(git_command(&["branch", &format!("_reviews/{}/{}/latest", pipeline, branch)], remote.path()));
}

/// Returns a Command set to the delivery binary created when you
/// ran `cargo test`.
fn delivery_cmd() -> Command {
    let mut delivery_path = env::current_exe().unwrap();
    delivery_path.pop();
    Command::new(delivery_path.join("delivery").to_str().unwrap())
}

/// A handy debugging function. Insert it when you want to sleep,
/// pass it a tmpdir, and you can inspect it.
///
/// Make sure you run `cargo test -- --nocapture` to see the output.
#[allow(dead_code)]
fn debug_sleep(tmpdir: &TempDir) {
    println!("Sleeping for 1000 seconds for {:?}", tmpdir.path());
    panic_on_error!(Command::new("sleep").arg("1000").output());
}

// ** Actual tests **

// Tests `delivery review`. Fails if the command fails, or if we fail to create
// the remote branch _for/master/rust/test, which is what we need to push to
// the API server.
test!(review {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_with_a_v1_config {
    let delivery_project_git = setup_mock_delivery_project_git("v1_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_without_dependencies {
    let delivery_project_git = setup_mock_delivery_project_git("no_deps_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_with_an_invalid_config {
    let delivery_project_git = setup_mock_delivery_project_git("invalid_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_review_command("rust/test");
    assert_command_failed(&mut command, &local_project);
});

test!(job_verify_unit_with_path_config {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project.path());
});

test!(job_verify_unit_with_git_config {
    let delivery_project_git = setup_mock_delivery_project_git("git_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_build_cookbook_project(&job_root.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project.path());
});

// TODO: This test requires internet access... we should move it out
// into an Acceptance-stage test instead of here in unit tests. It's
// impossible to run on a plane, for instance :(
test!(job_verify_unit_with_supermarket_config {
    let delivery_project_git = setup_mock_delivery_project_git("supermarket_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_failed(&mut command, &local_project);
    assert!(job_root.path().join_many(&["chef", "cookbooks", "httpd"]).is_dir());
    assert!(job_root.path().join_many(&["chef", "cookbooks", "httpd", "templates", "default", "magic.erb"]).is_file());
});

test!(job_verify_dna_json {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project.path());
    let mut dna_file = panic_on_error!(File::open(&job_root.path().join_many(&["chef", "dna.json"])));
    let mut dna_json = String::new();
    panic_on_error!(dna_file.read_to_string(&mut dna_json));
    let dna_data = panic_on_error!(Json::from_str(&dna_json));
    match dna_data.find_path(&["delivery", "workspace", "repo"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("repo").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/repo, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "chef"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("chef").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/chef, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "cache"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("cache").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/cache, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "root"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().to_str().unwrap());
        },
        None => panic!("No delivery/workspace/root, {}", dna_data)
    };
    match dna_data.find_path(&["delivery_builder", "build_user"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), "dbuild");
        },
        None => panic!("No delivery_builderl/build_user, {}", dna_data)
    };
});
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
extern crate quickcheck;

use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::path::{Path, PathBuf};
use quickcheck::{quickcheck, TestResult};

extern crate hyphenation;
use hyphenation::{load, Language, Corpus, Hyphenation, Standard};

fn fiat_io(lang: Language) -> Corpus {
    hyphenation::set_pattern_folder(&DATAPATH.as_path());
    load::language(lang).unwrap()
}

lazy_static! {
    static ref DATAPATH: PathBuf = {
        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.push("patterns");

        path
    };

    static ref EN_US: Corpus = fiat_io(Language::English_US);

    static ref WORDS: Vec<String> = {
        let file = File::open(Path::new("/usr/share/dict/words")).unwrap();
        let words: Vec<_> = BufReader::new(file).lines().map(|l| l.unwrap()).collect();

        words
    };
}

#[test]
fn collected_equals_original() {
    fn property(original: String) -> bool {
        let collected: String = original.hyphenate(&EN_US).collect();

        collected == original
    }

    quickcheck(property as fn(String) -> bool);
}

#[test]
fn opportunities_within_bounds() {
    fn property(s: String) -> bool {
        let os = s.opportunities(&EN_US);
        let l = s.len();

        os.iter().all(|&i| i < l)
    }

    quickcheck(property as fn(String) -> bool);
}

#[test]
fn punctuated_count() {
    fn property(s: String) -> bool {
        let l = s.chars().count();
        let os = s.opportunities(&EN_US);
        let h: String = s.hyphenate(&EN_US).punctuate().collect();

        h.chars().count() == l + os.len()
    }

    quickcheck(property as fn(String) -> bool);
}

#[test]
fn hyphenation_bounds() {
    fn property(s: String) -> TestResult {
        let ci: Vec<_> = s.char_indices().collect();
        let (l_min, r_min) = (&EN_US.left_min, &EN_US.right_min);
        let s_len = ci.len();
        if s_len < l_min + r_min {
            return TestResult::discard();
        }

        let os = s.opportunities(&EN_US);
        let ((l, _), (r, _)) = (ci[l_min - 1], ci[s_len - r_min]);
        let within_bounds = |&i| i > l && i <= r;

        TestResult::from_bool(os.iter().all(within_bounds))
    }

    quickcheck(property as fn(String) -> TestResult);
}

#[test]
fn basics() {
    let h1: Standard = "hyphenation".hyphenate(&EN_US);
    let h2: Standard = "project".hyphenate(&EN_US);
    let h3: Standard = "hypha".hyphenate(&EN_US);

    let v1: Vec<&str> = h1.clone().collect();
    let v2: Vec<&str> = h2.clone().collect();
    let v3: Vec<&str> = h3.clone().collect();
    assert_eq!(v1, vec!["hy", "phen", "ation"]);
    assert_eq!(v2, vec!["project"]);
    assert_eq!(v3, vec!["hy", "pha"]);

    let s1: String = h1.punctuate().collect();
    assert_eq!(s1, "hy\u{ad}phen\u{ad}ation".to_owned());
}
<commit_msg>Basic test for full text hyphenation<commit_after>#[macro_use]
extern crate lazy_static;
extern crate quickcheck;

use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::path::{Path, PathBuf};
use quickcheck::{quickcheck, TestResult};

extern crate hyphenation;
use hyphenation::{load, Language, Corpus, Hyphenation, Standard};

fn fiat_io(lang: Language) -> Corpus {
    hyphenation::set_pattern_folder(&DATAPATH.as_path());
    load::language(lang).unwrap()
}

lazy_static! {
    static ref DATAPATH: PathBuf = {
        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.push("patterns");

        path
    };

    static ref EN_US: Corpus = fiat_io(Language::English_US);

    static ref WORDS: Vec<String> = {
        let file = File::open(Path::new("/usr/share/dict/words")).unwrap();
        let words: Vec<_> = BufReader::new(file).lines().map(|l| l.unwrap()).collect();

        words
    };
}

#[test]
fn collected_equals_original() {
    fn property(original: String) -> bool {
        let collected: String = original.hyphenate(&EN_US).collect();

        collected == original
    }

    quickcheck(property as fn(String) -> bool);
}

#[test]
fn opportunities_within_bounds() {
    fn property(s: String) -> bool {
        let os = s.opportunities(&EN_US);
        let l = s.len();

        os.iter().all(|&i| i < l)
    }

    quickcheck(property as fn(String) -> bool);
}

#[test]
fn punctuated_count() {
    fn property(s: String) -> bool {
        let l = s.chars().count();
        let os = s.opportunities(&EN_US);
        let h: String = s.hyphenate(&EN_US).punctuate().collect();

        h.chars().count() == l + os.len()
    }

    quickcheck(property as fn(String) -> bool);
}

#[test]
fn hyphenation_bounds() {
    fn property(s: String) -> TestResult {
        let ci: Vec<_> = s.char_indices().collect();
        let (l_min, r_min) = (&EN_US.left_min, &EN_US.right_min);
        let s_len = ci.len();
        if s_len < l_min + r_min {
            return TestResult::discard();
        }

        let os = s.opportunities(&EN_US);
        let ((l, _), (r, _)) = (ci[l_min - 1], ci[s_len - r_min]);
        let within_bounds = |&i| i > l && i <= r;

        TestResult::from_bool(os.iter().all(within_bounds))
    }

    quickcheck(property as fn(String) -> TestResult);
}

#[test]
fn basics() {
    let h1: Standard = "hyphenation".hyphenate(&EN_US);
    let h2: Standard = "project".hyphenate(&EN_US);
    let h3: Standard = "hypha".hyphenate(&EN_US);
    let h4: Standard = "Word hyphenation by computer.".hyphenate(&EN_US);

    let v1: Vec<&str> = h1.clone().collect();
    let v2: Vec<&str> = h2.clone().collect();
    let v3: Vec<&str> = h3.clone().collect();
    let v4: Vec<&str> = h4.clone().collect();
    assert_eq!(v1, vec!["hy", "phen", "ation"]);
    assert_eq!(v2, vec!["project"]);
    assert_eq!(v3, vec!["hy", "pha"]);
    assert_eq!(v4, vec!["Word hy", "phen", "ation by com", "puter."]);

    let s1: String = h1.punctuate().collect();
    assert_eq!(s1, "hy\u{ad}phen\u{ad}ation".to_owned());
}
<|endoftext|>


<commit_before>extern crate fitsio;
extern crate libc;
extern crate tempdir;

use libc::c_int;
use std::ffi;
use std::ptr;
use fitsio::raw::*;

#[test]
fn raw_opening_an_existing_file() {
    let mut fptr: *mut fitsfile = ptr::null_mut();
    let mut status: c_int = -1;
    let c_filename = ffi::CString::new("testdata/full_example.fits").unwrap();

    unsafe {
        ffopen(&mut fptr as *mut *mut fitsfile, c_filename.as_ptr(), 0, &mut status);
        ffclos(fptr, &mut status);
    }

    assert_eq!(status, 0);
}

#[test]
fn raw_creating_a_new_file() {
    // Set up the test filename
    let tdir = tempdir::TempDir::new("rust-fitsio-").unwrap();
    let filename = tdir.path().join("test.fits");
    assert!(!filename.exists());

    let mut fptr = ptr::null_mut();
    let mut status = 0;
    let c_filename = ffi::CString::new(filename.to_str().unwrap()).unwrap();

    unsafe {
        ffinit(&mut fptr as *mut *mut fitsfile,
               c_filename.as_ptr(),
               &mut status);
    }

    assert!(filename.exists());
}

#[test]
fn getting_current_hdu_number() {
    let mut fptr: *mut fitsfile = ptr::null_mut();
    let mut status: c_int = -1;
    let c_filename = ffi::CString::new("testdata/full_example.fits").unwrap();

    let mut hdu_num: c_int = -1;

    unsafe {
        ffopen(&mut fptr as *mut *mut fitsfile, c_filename.as_ptr(), 0, &mut status);
        ffghdn(fptr, &mut hdu_num);
        ffclos(fptr, &mut status);
    }

    assert_eq!(hdu_num, 1);
}

#[test]
fn changing_hdu_by_absolute_number() {
    let mut fptr: *mut fitsfile = ptr::null_mut();
    let mut status: c_int = -1;
    let c_filename = ffi::CString::new("testdata/full_example.fits").unwrap();

    let mut hdu_type: c_int = 0;
    let mut hdu_num: c_int = 0;

    unsafe {
        ffopen(&mut fptr as *mut *mut fitsfile, c_filename.as_ptr(), 0, &mut status);
        ffmahd(fptr, 2, &mut hdu_type, &mut status);
        ffghdn(fptr, &mut hdu_num);
        ffclos(fptr, &mut status);
    }

    assert_eq!(hdu_num, 2);
}
<commit_msg>Remove non-needed type hints in tests<commit_after>extern crate fitsio;
extern crate libc;
extern crate tempdir;

use std::ffi;
use std::ptr;
use fitsio::raw::*;

#[test]
fn raw_opening_an_existing_file() {
    let mut fptr = ptr::null_mut();
    let mut status = -1;
    let c_filename = ffi::CString::new("testdata/full_example.fits").unwrap();

    unsafe {
        ffopen(&mut fptr as *mut *mut fitsfile,
               c_filename.as_ptr(),
               0,
               &mut status);
        ffclos(fptr, &mut status);
    }

    assert_eq!(status, 0);
}

#[test]
fn raw_creating_a_new_file() {
    // Set up the test filename
    let tdir = tempdir::TempDir::new("rust-fitsio-").unwrap();
    let filename = tdir.path().join("test.fits");
    assert!(!filename.exists());

    let mut fptr = ptr::null_mut();
    let mut status = 0;
    let c_filename = ffi::CString::new(filename.to_str().unwrap()).unwrap();

    unsafe {
        ffinit(&mut fptr as *mut *mut fitsfile,
               c_filename.as_ptr(),
               &mut status);
    }

    assert!(filename.exists());
}

#[test]
fn getting_current_hdu_number() {
    let mut fptr = ptr::null_mut();
    let mut status = -1;
    let c_filename = ffi::CString::new("testdata/full_example.fits").unwrap();
    let mut hdu_num = -1;

    unsafe {
        ffopen(&mut fptr as *mut *mut fitsfile,
               c_filename.as_ptr(),
               0,
               &mut status);
        ffghdn(fptr, &mut hdu_num);
        ffclos(fptr, &mut status);
    }

    assert_eq!(hdu_num, 1);
}

#[test]
fn changing_hdu_by_absolute_number() {
    let mut fptr = ptr::null_mut();
    let mut status = -1;
    let c_filename = ffi::CString::new("testdata/full_example.fits").unwrap();

    let mut hdu_type = 0;
    let mut hdu_num = 0;

    unsafe {
        ffopen(&mut fptr as *mut *mut fitsfile,
               c_filename.as_ptr(),
               0,
               &mut status);
        ffmahd(fptr, 2, &mut hdu_type, &mut status);
        ffghdn(fptr, &mut hdu_num);
        ffclos(fptr, &mut status);
    }

    assert_eq!(hdu_num, 2);
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate regex;

include!("../clap-test.rs");

use clap::{App, Arg};

#[test]
fn stdin_char() {
    let r = App::new("opts")
        .arg( Arg::from_usage("-f [flag] 'some flag'") )
        .get_matches_from_safe(vec!["", "-f", "-"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("f"));
    assert_eq!(m.value_of("f").unwrap(), "-");
}

#[test]
fn opts_using_short() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("-f [flag] 'some flag'"),
            Arg::from_usage("-c [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "-f", "some", "-c", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("f"));
    assert_eq!(m.value_of("f").unwrap(), "some");
    assert!(m.is_present("c"));
    assert_eq!(m.value_of("c").unwrap(), "other");
}

#[test]
fn lots_o_vals() {
    let r = App::new("opts")
        .arg(
            Arg::from_usage("-o [opt]... 'some opt'"),
            )
        .get_matches_from_safe(vec!["", "-o",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            ]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>().len(), 297); // i.e. more than u8
}

#[test]
fn opts_using_long_space() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("--flag [flag] 'some flag'"),
            Arg::from_usage("--color [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "--flag", "some", "--color", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn opts_with_empty_values() {
    let r = App::new("opts")
        .arg_from_usage("--flag [flag]... 'some flag'")
        .get_matches_from_safe(vec!["", "--flag", "", "test"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.values_of("flag").unwrap().collect::<Vec<_>>(), ["", "test"]);
}

#[test]
fn opts_using_long_equals() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("--flag [flag] 'some flag'"),
            Arg::from_usage("--color [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "--flag=some", "--color=other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn opts_using_mixed() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("-f, --flag [flag] 'some flag'"),
            Arg::from_usage("-c, --color [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "-f", "some", "--color", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn opts_using_mixed2() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("-f, --flag [flag] 'some flag'"),
            Arg::from_usage("-c, --color [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "--flag=some", "-c", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn default_values_default() {
    let r = App::new("df")
        .arg( Arg::from_usage("-o [opt] 'some opt'")
            .default_value("default"))
        .get_matches_from_safe(vec![""]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.value_of("o").unwrap(), "default");
}

#[test]
fn default_values_user_value() {
    let r = App::new("df")
        .arg( Arg::from_usage("-o [opt] 'some opt'")
            .default_value("default"))
        .get_matches_from_safe(vec!["", "-o", "value"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.value_of("o").unwrap(), "value");
}

#[test]
fn did_you_mean() {
    test::check_err_output(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 <opt>...

For more information try --help", true);
}
<commit_msg>tests(Options): adds tests to cover new stop parsing vals after spaces with equals or delims<commit_after>extern crate clap;
extern crate regex;

include!("../clap-test.rs");

use clap::{App, Arg};

#[test]
fn stdin_char() {
    let r = App::new("opts")
        .arg( Arg::from_usage("-f [flag] 'some flag'") )
        .get_matches_from_safe(vec!["", "-f", "-"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("f"));
    assert_eq!(m.value_of("f").unwrap(), "-");
}

#[test]
fn opts_using_short() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("-f [flag] 'some flag'"),
            Arg::from_usage("-c [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "-f", "some", "-c", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("f"));
    assert_eq!(m.value_of("f").unwrap(), "some");
    assert!(m.is_present("c"));
    assert_eq!(m.value_of("c").unwrap(), "other");
}

#[test]
fn lots_o_vals() {
    let r = App::new("opts")
        .arg(
            Arg::from_usage("-o [opt]... 'some opt'"),
            )
        .get_matches_from_safe(vec!["", "-o",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            "some", "some", "some", "some", "some", "some", "some", "some", "some", "some", "some",
            ]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>().len(), 297); // i.e. more than u8
}

#[test]
fn opts_using_long_space() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("--flag [flag] 'some flag'"),
            Arg::from_usage("--color [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "--flag", "some", "--color", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn opts_with_empty_values() {
    let r = App::new("opts")
        .arg_from_usage("--flag [flag]... 'some flag'")
        .get_matches_from_safe(vec!["", "--flag", "", "test"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.values_of("flag").unwrap().collect::<Vec<_>>(), ["", "test"]);
}

#[test]
fn opts_using_long_equals() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("--flag [flag] 'some flag'"),
            Arg::from_usage("--color [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "--flag=some", "--color=other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn opts_using_mixed() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("-f, --flag [flag] 'some flag'"),
            Arg::from_usage("-c, --color [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "-f", "some", "--color", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn opts_using_mixed2() {
    let r = App::new("opts")
        .args(&[
            Arg::from_usage("-f, --flag [flag] 'some flag'"),
            Arg::from_usage("-c, --color [color] 'some other flag'")
            ])
        .get_matches_from_safe(vec!["", "--flag=some", "-c", "other"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("flag").unwrap(), "some");
    assert!(m.is_present("color"));
    assert_eq!(m.value_of("color").unwrap(), "other");
}

#[test]
fn default_values_default() {
    let r = App::new("df")
        .arg( Arg::from_usage("-o [opt] 'some opt'")
            .default_value("default"))
        .get_matches_from_safe(vec![""]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.value_of("o").unwrap(), "default");
}

#[test]
fn default_values_user_value() {
    let r = App::new("df")
        .arg( Arg::from_usage("-o [opt] 'some opt'")
            .default_value("default"))
        .get_matches_from_safe(vec!["", "-o", "value"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.value_of("o").unwrap(), "value");
}

#[test]
fn multiple_vals_pos_arg_equals() {
    let r = App::new("mvae")
        .arg( Arg::from_usage("-o [opt]... 'some opt'") )
        .arg( Arg::from_usage("[file] 'some file'") )
        .get_matches_from_safe(vec!["", "-o=1", "some"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.value_of("o").unwrap(), "1");
    assert!(m.is_present("file"));
    assert_eq!(m.value_of("file").unwrap(), "some");
}

#[test]
fn multiple_vals_pos_arg_delim() {
    let r = App::new("mvae")
        .arg( Arg::from_usage("-o [opt]... 'some opt'") )
        .arg( Arg::from_usage("[file] 'some file'") )
        .get_matches_from_safe(vec!["", "-o", "1,2", "some"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("o"));
    assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["1", "2"]);
    assert!(m.is_present("file"));
    assert_eq!(m.value_of("file").unwrap(), "some");
}

#[test]
fn did_you_mean() {
    test::check_err_output(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 <opt>...

For more information try --help", true);
}
<|endoftext|>


<commit_before>pub mod cmd;
pub mod ctxt;

use std::cell::RefCell;
use std::collections::HashMap;

use self::ctxt::Context;
use error::diag::Diagnostic;

use parser::ast;
use parser::Parser;
use semck;
use sym::SymTable;

pub fn compile() {
    let args = cmd::parse();

    if args.flag_version {
        println!("dora v0.01b");
        return 0;
    }

    let mut parser = match Parser::from_file(&args.arg_file) {
        Err(_) => {
            println!("unable to read file `{}`", &args.arg_file);
            return 1;
        }

        Ok(parser) => parser
    };

    let (ast, interner) = match parser.parse() {
        Ok(ret) => ret,

        Err(error) => {
            error.print();
            panic!();
        }
    };

    if args.flag_emit_ast {
        ast::dump::dump(&ast, &interner);
    }

    let ast_map = ast::map::build(&ast, &interner);

    let mut ctxt = Context {
        args: &args,
        interner: &interner,
        map: &ast_map,
        ast: &ast,
        diag: RefCell::new(Diagnostic::new()),
        sym: RefCell::new(SymTable::new()),
        types: RefCell::new(HashMap::new())
    };

    semck::check(&ctxt);

    if ctxt.diag.borrow().has_errors() {
        ctxt.diag.borrow().dump();

        println!("{} error(s) found", ctxt.diag.borrow().errors());
        return 1;
    }

    0
}
<commit_msg>replaced panic with exit code<commit_after>pub mod cmd;
pub mod ctxt;

use std::cell::RefCell;
use std::collections::HashMap;

use self::ctxt::Context;
use error::diag::Diagnostic;

use parser::ast;
use parser::Parser;
use semck;
use sym::SymTable;

pub fn compile() {
    let args = cmd::parse();

    if args.flag_version {
        println!("dora v0.01b");
        return 0;
    }

    let mut parser = match Parser::from_file(&args.arg_file) {
        Err(_) => {
            println!("unable to read file `{}`", &args.arg_file);
            return 1;
        }

        Ok(parser) => parser
    };

    let (ast, interner) = match parser.parse() {
        Ok(ret) => ret,

        Err(error) => {
            error.print();
            return 1;
        }
    };

    if args.flag_emit_ast {
        ast::dump::dump(&ast, &interner);
    }

    let ast_map = ast::map::build(&ast, &interner);

    let mut ctxt = Context {
        args: &args,
        interner: &interner,
        map: &ast_map,
        ast: &ast,
        diag: RefCell::new(Diagnostic::new()),
        sym: RefCell::new(SymTable::new()),
        types: RefCell::new(HashMap::new())
    };

    semck::check(&ctxt);

    if ctxt.diag.borrow().has_errors() {
        ctxt.diag.borrow().dump();

        println!("{} error(s) found", ctxt.diag.borrow().errors());
        return 1;
    }

    0
}
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL was not
// distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.

//! The frame module provides a structred way to send and receive
//! message through streams.
//!
//! ## Data Framing
//!
//! ~~~
//! 0                   1                   2                   3
//! 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! | Frame Start |  Payload Len                  |   Payload   |
//! +-----------------------------------------------------------+
//! |           Payload Data Continued            |  Frame End  |
//! +-----------------------------------------------------------+
//!
//! Start Frame:    8 bits, must be 0x01
//! Payload Len:    16 bits
//! Payload Data:   (Payload Len) bytes
//! End Frame:      8 bits, must be 0x17
//! ~~~

use std::fmt;

/// Indicates start of frame
pub const START:    u8 = 0x01;
/// Indicates end of frame
pub const END:      u8 = 0x17;

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum FrameState {
    /// The stream is currently reading for start byte
    Start,
    /// The stream is currently reading for payload length
    PayloadLen,
    /// The stream is currently reading the payload
    Payload,
    /// The stream is currently reading for the end byte
    End,
}

impl fmt::Display for FrameState {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            FrameState::Start => "Start".fmt(f),
            FrameState::PayloadLen => "PayloadLen".fmt(f),
            FrameState::Payload => "Payload".fmt(f),
            FrameState::End => "End".fmt(f),
        }
    }
}

pub fn from_slice(slice: &[u8]) -> Vec<u8> {
    let len = slice.len() as u16;
    let mut buf = Vec::<u8>::with_capacity(slice.len() + 4);
    buf.push(START);
    buf.push((len >> 8) as u8);
    buf.push(len as u8);
    for byte in slice.iter() {
        buf.push(*byte);
    }
    buf.push(END);
    buf
}
<commit_msg>More doc stuff<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL was not
// distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.

//! The frame module provides a structred way to send and receive
//! message through streams.
//!
//! ## Data Framing
//!
//! ```ignore
//! 0                   1                   2                   3
//! 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! | Frame Start |  Payload Len                  |   Payload   |
//! +-----------------------------------------------------------+
//! |           Payload Data Continued            |  Frame End  |
//! +-----------------------------------------------------------+
//!
//! Start Frame:    8 bits, must be 0x01
//! Payload Len:    16 bits
//! Payload Data:   (Payload Len) bytes
//! End Frame:      8 bits, must be 0x17
//! ```

use std::fmt;

/// Indicates start of frame
pub const START:    u8 = 0x01;
/// Indicates end of frame
pub const END:      u8 = 0x17;

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum FrameState {
    /// The stream is currently reading for start byte
    Start,
    /// The stream is currently reading for payload length
    PayloadLen,
    /// The stream is currently reading the payload
    Payload,
    /// The stream is currently reading for the end byte
    End,
}

impl fmt::Display for FrameState {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            FrameState::Start => "Start".fmt(f),
            FrameState::PayloadLen => "PayloadLen".fmt(f),
            FrameState::Payload => "Payload".fmt(f),
            FrameState::End => "End".fmt(f),
        }
    }
}

pub fn from_slice(slice: &[u8]) -> Vec<u8> {
    let len = slice.len() as u16;
    let mut buf = Vec::<u8>::with_capacity(slice.len() + 4);
    buf.push(START);
    buf.push((len >> 8) as u8);
    buf.push(len as u8);
    for byte in slice.iter() {
        buf.push(*byte);
    }
    buf.push(END);
    buf
}
<|endoftext|>


<commit_before>//! A module to handle `Event` enumerator

pub mod attributes;

use std::borrow::Cow;
use std::str::from_utf8;
use std::ops::Deref;
use encoding_rs::Encoding;
use std::io::BufRead;

use escape::unescape;
use self::attributes::{Attributes, Attribute};
use errors::Result;
use reader::Reader;

/// A struct to manage `Event::Start` events
///
/// Provides in particular an iterator over attributes
#[derive(Clone, Debug)]
pub struct BytesStart<'a> {
    /// content of the element, before any utf8 conversion
    buf: Cow<'a, [u8]>,
    /// end of the element name, the name starts at that the start of `buf`
    name_len: usize,
}

impl<'a> BytesStart<'a> {
    /// Creates a new `BytesStart` from the given name.
    #[inline]
    pub fn borrowed(content: &'a [u8], name_len: usize) -> BytesStart<'a> {
        BytesStart {
            buf: Cow::Borrowed(content),
            name_len: name_len,
        }
    }

    /// Creates a new `BytesStart` from the given name. Owns its content
    #[inline]
    pub fn owned(content: Vec<u8>, name_len: usize) -> BytesStart<'static> {
        BytesStart {
            buf: Cow::Owned(content),
            name_len: name_len,
        }
    }

    /// Converts the event into an Owned event
    pub fn into_owned(self) -> BytesStart<'static> {
        BytesStart {
            buf: Cow::Owned(self.buf.into_owned()),
            name_len: self.name_len,
        }
    }

    /// Consumes self and adds attributes to this element from an iterator
    /// over (key, value) tuples.
    /// Key and value can be anything that implements the AsRef<[u8]> trait,
    /// like byte slices and strings.
    pub fn with_attributes<'b, I>(mut self, attributes: I) -> Self
        where I: IntoIterator,
              I::Item: Into<Attribute<'b>>
    {
        self.extend_attributes(attributes);
        self
    }

    /// name as &[u8] (without eventual attributes)
    pub fn name(&self) -> &[u8] {
        &self.buf[..self.name_len]
    }

    /// gets unescaped content
    ///
    /// Searches for '&' into content and try to escape the coded character if possible
    /// returns Malformed error with index within element if '&' is not followed by ';'
    pub fn unescaped(&self) -> Result<Cow<[u8]>> {
        unescape(&*self.buf)
    }

    /// gets attributes iterator
    pub fn attributes(&self) -> Attributes {
        Attributes::new(&self, self.name_len)
    }

    /// extend the attributes of this element from an iterator over (key, value) tuples.
    /// Key and value can be anything that implements the AsRef<[u8]> trait,
    /// like byte slices and strings.
    pub fn extend_attributes<'b, I>(&mut self, attributes: I) -> &mut BytesStart<'a>
        where I: IntoIterator,
              I::Item: Into<Attribute<'b>>
    {
        for attr in attributes {
            self.push_attribute(attr);
        }
        self
    }

    /// helper method to unescape then decode self using the reader encoding (utf8 per default)
    ///
    /// for performance reasons (could avoid allocating a `String`), it might be wiser to manually use
    /// 1. BytesStart::unescaped()
    /// 2. Reader::decode(...)
    pub fn unescape_and_decode<B: BufRead>(&self, reader: &Reader<B>) -> Result<String> {
        self.unescaped().map(|e| reader.decode(&*e).into_owned())
    }

    /// Adds an attribute to this element from the given key and value.
    /// Key and value can be anything that implements the AsRef<[u8]> trait,
    /// like byte slices and strings.
    pub fn push_attribute<'b, A: Into<Attribute<'b>>>(&mut self, attr: A) {
        let a = attr.into();
        let bytes = self.buf.to_mut();
        bytes.push(b' ');
        bytes.extend_from_slice(a.key);
        bytes.extend_from_slice(b"=\"");
        bytes.extend_from_slice(a.value);
        bytes.push(b'"');
    }
}

/// Wrapper around `BytesElement` to parse/write `XmlDecl`
///
/// Postpone element parsing only when needed.
///
/// [W3C XML 1.1 Prolog and Document Type Delcaration](http://w3.org/TR/xml11/#sec-prolog-dtd)
#[derive(Clone, Debug)]
pub struct BytesDecl<'a> {
    element: BytesStart<'a>,
}

impl<'a> BytesDecl<'a> {
    /// Creates a `BytesDecl` from a `BytesStart`
    pub fn from_start(start: BytesStart<'a>) -> BytesDecl<'a> {
        BytesDecl { element: start }
    }

    /// Gets xml version, including quotes (' or ")
    pub fn version(&self) -> Result<&[u8]> {
        match self.element.attributes().next() {
            Some(Err(e)) => Err(e),
            Some(Ok(Attribute { key: b"version", value: v })) => Ok(v),
            Some(Ok(a)) => {
                Err(format!("XmlDecl must start with 'version' attribute, found {:?}",
                            from_utf8(a.key))
                    .into())
            }
            None => Err("XmlDecl must start with 'version' attribute, found none".into()),
        }
    }

    /// Gets xml encoding, including quotes (' or ")
    pub fn encoding(&self) -> Option<Result<&[u8]>> {
        for a in self.element.attributes() {
            match a {
                Err(e) => return Some(Err(e)),
                Ok(Attribute { key: b"encoding", value: v }) => return Some(Ok(v)),
                _ => (),
            }
        }
        None
    }

    /// Gets xml standalone, including quotes (' or ")
    pub fn standalone(&self) -> Option<Result<&[u8]>> {
        for a in self.element.attributes() {
            match a {
                Err(e) => return Some(Err(e)),
                Ok(Attribute { key: b"standalone", value: v }) => return Some(Ok(v)),
                _ => (),
            }
        }
        None
    }

    /// Constructs a new `XmlDecl` from the (mandatory) _version_ (should be `1.0` or `1.1`),
    /// the optional _encoding_ (e.g., `UTF-8`) and the optional _standalone_ (`yes` or `no`)
    /// attribute.
    ///
    /// Does not escape any of its inputs. Always uses double quotes to wrap the attribute values.
    /// The caller is responsible for escaping attribute values. Shouldn't usually be relevant since
    /// the double quote character is not allowed in any of the attribute values.
    pub fn new(version: &[u8],
               encoding: Option<&[u8]>,
               standalone: Option<&[u8]>)
               -> BytesDecl<'static> {
        // Compute length of the buffer based on supplied attributes
        // ' encoding=""'   => 12
        let encoding_attr_len = if let Some(xs) = encoding {
            12 + xs.len()
        } else {
            0
        };
        // ' standalone=""' => 14
        let standalone_attr_len = if let Some(xs) = standalone {
            14 + xs.len()
        } else {
            0
        };
        // 'xml version=""' => 14
        let mut buf = Vec::with_capacity(14 + encoding_attr_len + standalone_attr_len);

        buf.extend_from_slice(b"xml version=\"");
        buf.extend_from_slice(version);

        if let Some(encoding_val) = encoding {
            buf.extend_from_slice(b"\" encoding=\"");
            buf.extend_from_slice(encoding_val);
        }

        if let Some(standalone_val) = standalone {
            buf.extend_from_slice(b"\" standalone=\"");
            buf.extend_from_slice(standalone_val);
        }
        buf.push(b'"');

        BytesDecl { element: BytesStart::owned(buf, 3) }
    }

    /// Gets the decoder struct
    pub fn encoder(&self) -> Option<&'static Encoding> {
        self.encoding()
            .and_then(|e| e.ok())
            .and_then(|e| Encoding::for_label(e))
    }
}

/// A struct to manage `Event::End` events
#[derive(Clone, Debug)]
pub struct BytesEnd<'a> {
    name: Cow<'a, [u8]>,
}

impl<'a> BytesEnd<'a> {
    /// Creates a new `BytesEnd` borrowing a slice
    #[inline]
    pub fn borrowed(name: &'a [u8]) -> BytesEnd<'a> {
        BytesEnd { name: Cow::Borrowed(name) }
    }

    /// Creates a new `BytesEnd` owning its name
    #[inline]
    pub fn owned(name: Vec<u8>) -> BytesEnd<'static> {
        BytesEnd { name: Cow::Owned(name) }
    }

    /// Gets `BytesEnd` event name
    #[inline]
    pub fn name(&self) -> &[u8] {
        &*self.name
    }
}

/// A struct to manage `Event::End` events
#[derive(Clone, Debug)]
pub struct BytesText<'a> {
    content: Cow<'a, [u8]>,
}

impl<'a> BytesText<'a> {
    /// Creates a new `BytesEnd` borrowing a slice
    #[inline]
    pub fn borrowed(content: &'a [u8]) -> BytesText<'a> {
        BytesText { content: Cow::Borrowed(content) }
    }

    /// Creates a new `BytesEnd` owning its name
    #[inline]
    pub fn owned(content: Vec<u8>) -> BytesText<'static> {
        BytesText { content: Cow::Owned(content) }
    }

    /// gets escaped content
    ///
    /// Searches for '&' into content and try to escape the coded character if possible
    /// returns Malformed error with index within element if '&' is not followed by ';'
    pub fn unescaped(&self) -> Result<Cow<[u8]>> {
        unescape(&self)
    }

    /// helper method to unescape then decode self using the reader encoding (utf8 per default)
    ///
    /// for performance reasons (could avoid allocating a `String`), it might be wiser to manually use
    /// 1. BytesText::unescaped()
    /// 2. Reader::decode(...)
    pub fn unescape_and_decode<B: BufRead>(&self, reader: &Reader<B>) -> Result<String> {
        self.unescaped().map(|e| reader.decode(&*e).into_owned())
    }
}

/// Event to interprete node as they are parsed
#[derive(Clone, Debug)]
pub enum Event<'a> {
    /// Start tag (with attributes) <...>
    Start(BytesStart<'a>),
    /// End tag </...>
    End(BytesEnd<'a>),
    /// Empty element tag (with attributes) <.../>
    Empty(BytesStart<'a>),
    /// Data between Start and End element
    Text(BytesText<'a>),
    /// Comment <!-- ... -->
    Comment(BytesText<'a>),
    /// CData <![CDATA[...]]>
    CData(BytesText<'a>),
    /// Xml declaration <?xml ...?>
    Decl(BytesDecl<'a>),
    /// Processing instruction <?...?>
    PI(BytesText<'a>),
    /// Doctype <!DOCTYPE...>
    DocType(BytesText<'a>),
    /// Eof of file event
    Eof,
}

impl<'a> Deref for BytesStart<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.buf
    }
}

impl<'a> Deref for BytesDecl<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.element
    }
}

impl<'a> Deref for BytesEnd<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.name
    }
}

impl<'a> Deref for BytesText<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.content
    }
}

impl<'a> Deref for Event<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        match *self {
            Event::Start(ref e) => &*e,
            Event::End(ref e) => &*e,
            Event::Text(ref e) => &*e,
            Event::Empty(ref e) => &*e,
            Event::Decl(ref e) => &*e,
            Event::PI(ref e) => &*e,
            Event::CData(ref e) => &*e,
            Event::Comment(ref e) => &*e,
            Event::DocType(ref e) => &*e,
            Event::Eof => &[],
        }
    }
}
<commit_msg>impl naive local_name() (#61)<commit_after>//! A module to handle `Event` enumerator

pub mod attributes;

use std::borrow::Cow;
use std::str::from_utf8;
use std::ops::Deref;
use encoding_rs::Encoding;
use std::io::BufRead;

use escape::unescape;
use self::attributes::{Attributes, Attribute};
use errors::Result;
use reader::Reader;

/// A struct to manage `Event::Start` events
///
/// Provides in particular an iterator over attributes
#[derive(Clone, Debug)]
pub struct BytesStart<'a> {
    /// content of the element, before any utf8 conversion
    buf: Cow<'a, [u8]>,
    /// end of the element name, the name starts at that the start of `buf`
    name_len: usize,
}

impl<'a> BytesStart<'a> {
    /// Creates a new `BytesStart` from the given name.
    #[inline]
    pub fn borrowed(content: &'a [u8], name_len: usize) -> BytesStart<'a> {
        BytesStart {
            buf: Cow::Borrowed(content),
            name_len: name_len,
        }
    }

    /// Creates a new `BytesStart` from the given name. Owns its content
    #[inline]
    pub fn owned(content: Vec<u8>, name_len: usize) -> BytesStart<'static> {
        BytesStart {
            buf: Cow::Owned(content),
            name_len: name_len,
        }
    }

    /// Converts the event into an Owned event
    pub fn into_owned(self) -> BytesStart<'static> {
        BytesStart {
            buf: Cow::Owned(self.buf.into_owned()),
            name_len: self.name_len,
        }
    }

    /// Consumes self and adds attributes to this element from an iterator
    /// over (key, value) tuples.
    /// Key and value can be anything that implements the AsRef<[u8]> trait,
    /// like byte slices and strings.
    pub fn with_attributes<'b, I>(mut self, attributes: I) -> Self
        where I: IntoIterator,
              I::Item: Into<Attribute<'b>>
    {
        self.extend_attributes(attributes);
        self
    }

    /// name as &[u8] (without eventual attributes)
    pub fn name(&self) -> &[u8] {
        &self.buf[..self.name_len]
    }

    /// local name (excluding namespace) as &[u8] (without eventual attributes)
    /// returns the name() with any leading namespace removed (all content up to
    /// and including the first ':' character)
    #[inline]
    pub fn local_name(&self) -> &[u8] {
        if let Some(i) = self.name().iter().position(|b| *b == b':') {
            &self.name()[i + 1..]
        } else {
            self.name()
        }
    }

    /// gets unescaped content
    ///
    /// Searches for '&' into content and try to escape the coded character if possible
    /// returns Malformed error with index within element if '&' is not followed by ';'
    pub fn unescaped(&self) -> Result<Cow<[u8]>> {
        unescape(&*self.buf)
    }

    /// gets attributes iterator
    pub fn attributes(&self) -> Attributes {
        Attributes::new(&self, self.name_len)
    }

    /// extend the attributes of this element from an iterator over (key, value) tuples.
    /// Key and value can be anything that implements the AsRef<[u8]> trait,
    /// like byte slices and strings.
    pub fn extend_attributes<'b, I>(&mut self, attributes: I) -> &mut BytesStart<'a>
        where I: IntoIterator,
              I::Item: Into<Attribute<'b>>
    {
        for attr in attributes {
            self.push_attribute(attr);
        }
        self
    }

    /// helper method to unescape then decode self using the reader encoding (utf8 per default)
    ///
    /// for performance reasons (could avoid allocating a `String`), it might be wiser to manually use
    /// 1. BytesStart::unescaped()
    /// 2. Reader::decode(...)
    pub fn unescape_and_decode<B: BufRead>(&self, reader: &Reader<B>) -> Result<String> {
        self.unescaped().map(|e| reader.decode(&*e).into_owned())
    }

    /// Adds an attribute to this element from the given key and value.
    /// Key and value can be anything that implements the AsRef<[u8]> trait,
    /// like byte slices and strings.
    pub fn push_attribute<'b, A: Into<Attribute<'b>>>(&mut self, attr: A) {
        let a = attr.into();
        let bytes = self.buf.to_mut();
        bytes.push(b' ');
        bytes.extend_from_slice(a.key);
        bytes.extend_from_slice(b"=\"");
        bytes.extend_from_slice(a.value);
        bytes.push(b'"');
    }
}

/// Wrapper around `BytesElement` to parse/write `XmlDecl`
///
/// Postpone element parsing only when needed.
///
/// [W3C XML 1.1 Prolog and Document Type Delcaration](http://w3.org/TR/xml11/#sec-prolog-dtd)
#[derive(Clone, Debug)]
pub struct BytesDecl<'a> {
    element: BytesStart<'a>,
}

impl<'a> BytesDecl<'a> {
    /// Creates a `BytesDecl` from a `BytesStart`
    pub fn from_start(start: BytesStart<'a>) -> BytesDecl<'a> {
        BytesDecl { element: start }
    }

    /// Gets xml version, including quotes (' or ")
    pub fn version(&self) -> Result<&[u8]> {
        match self.element.attributes().next() {
            Some(Err(e)) => Err(e),
            Some(Ok(Attribute { key: b"version", value: v })) => Ok(v),
            Some(Ok(a)) => {
                Err(format!("XmlDecl must start with 'version' attribute, found {:?}",
                            from_utf8(a.key))
                    .into())
            }
            None => Err("XmlDecl must start with 'version' attribute, found none".into()),
        }
    }

    /// Gets xml encoding, including quotes (' or ")
    pub fn encoding(&self) -> Option<Result<&[u8]>> {
        for a in self.element.attributes() {
            match a {
                Err(e) => return Some(Err(e)),
                Ok(Attribute { key: b"encoding", value: v }) => return Some(Ok(v)),
                _ => (),
            }
        }
        None
    }

    /// Gets xml standalone, including quotes (' or ")
    pub fn standalone(&self) -> Option<Result<&[u8]>> {
        for a in self.element.attributes() {
            match a {
                Err(e) => return Some(Err(e)),
                Ok(Attribute { key: b"standalone", value: v }) => return Some(Ok(v)),
                _ => (),
            }
        }
        None
    }

    /// Constructs a new `XmlDecl` from the (mandatory) _version_ (should be `1.0` or `1.1`),
    /// the optional _encoding_ (e.g., `UTF-8`) and the optional _standalone_ (`yes` or `no`)
    /// attribute.
    ///
    /// Does not escape any of its inputs. Always uses double quotes to wrap the attribute values.
    /// The caller is responsible for escaping attribute values. Shouldn't usually be relevant since
    /// the double quote character is not allowed in any of the attribute values.
    pub fn new(version: &[u8],
               encoding: Option<&[u8]>,
               standalone: Option<&[u8]>)
               -> BytesDecl<'static> {
        // Compute length of the buffer based on supplied attributes
        // ' encoding=""'   => 12
        let encoding_attr_len = if let Some(xs) = encoding {
            12 + xs.len()
        } else {
            0
        };
        // ' standalone=""' => 14
        let standalone_attr_len = if let Some(xs) = standalone {
            14 + xs.len()
        } else {
            0
        };
        // 'xml version=""' => 14
        let mut buf = Vec::with_capacity(14 + encoding_attr_len + standalone_attr_len);

        buf.extend_from_slice(b"xml version=\"");
        buf.extend_from_slice(version);

        if let Some(encoding_val) = encoding {
            buf.extend_from_slice(b"\" encoding=\"");
            buf.extend_from_slice(encoding_val);
        }

        if let Some(standalone_val) = standalone {
            buf.extend_from_slice(b"\" standalone=\"");
            buf.extend_from_slice(standalone_val);
        }
        buf.push(b'"');

        BytesDecl { element: BytesStart::owned(buf, 3) }
    }

    /// Gets the decoder struct
    pub fn encoder(&self) -> Option<&'static Encoding> {
        self.encoding()
            .and_then(|e| e.ok())
            .and_then(|e| Encoding::for_label(e))
    }
}

/// A struct to manage `Event::End` events
#[derive(Clone, Debug)]
pub struct BytesEnd<'a> {
    name: Cow<'a, [u8]>,
}

impl<'a> BytesEnd<'a> {
    /// Creates a new `BytesEnd` borrowing a slice
    #[inline]
    pub fn borrowed(name: &'a [u8]) -> BytesEnd<'a> {
        BytesEnd { name: Cow::Borrowed(name) }
    }

    /// Creates a new `BytesEnd` owning its name
    #[inline]
    pub fn owned(name: Vec<u8>) -> BytesEnd<'static> {
        BytesEnd { name: Cow::Owned(name) }
    }

    /// Gets `BytesEnd` event name
    #[inline]
    pub fn name(&self) -> &[u8] {
        &*self.name
    }

    /// local name (excluding namespace) as &[u8] (without eventual attributes)
    /// returns the name() with any leading namespace removed (all content up to
    /// and including the first ':' character)
    #[inline]
    pub fn local_name(&self) -> &[u8] {
        if let Some(i) = self.name().iter().position(|b| *b == b':') {
            &self.name()[i + 1..]
        } else {
            self.name()
        }
    }
}

/// A struct to manage `Event::End` events
#[derive(Clone, Debug)]
pub struct BytesText<'a> {
    content: Cow<'a, [u8]>,
}

impl<'a> BytesText<'a> {
    /// Creates a new `BytesEnd` borrowing a slice
    #[inline]
    pub fn borrowed(content: &'a [u8]) -> BytesText<'a> {
        BytesText { content: Cow::Borrowed(content) }
    }

    /// Creates a new `BytesEnd` owning its name
    #[inline]
    pub fn owned(content: Vec<u8>) -> BytesText<'static> {
        BytesText { content: Cow::Owned(content) }
    }

    /// gets escaped content
    ///
    /// Searches for '&' into content and try to escape the coded character if possible
    /// returns Malformed error with index within element if '&' is not followed by ';'
    pub fn unescaped(&self) -> Result<Cow<[u8]>> {
        unescape(&self)
    }

    /// helper method to unescape then decode self using the reader encoding (utf8 per default)
    ///
    /// for performance reasons (could avoid allocating a `String`), it might be wiser to manually use
    /// 1. BytesText::unescaped()
    /// 2. Reader::decode(...)
    pub fn unescape_and_decode<B: BufRead>(&self, reader: &Reader<B>) -> Result<String> {
        self.unescaped().map(|e| reader.decode(&*e).into_owned())
    }
}

/// Event to interprete node as they are parsed
#[derive(Clone, Debug)]
pub enum Event<'a> {
    /// Start tag (with attributes) <...>
    Start(BytesStart<'a>),
    /// End tag </...>
    End(BytesEnd<'a>),
    /// Empty element tag (with attributes) <.../>
    Empty(BytesStart<'a>),
    /// Data between Start and End element
    Text(BytesText<'a>),
    /// Comment <!-- ... -->
    Comment(BytesText<'a>),
    /// CData <![CDATA[...]]>
    CData(BytesText<'a>),
    /// Xml declaration <?xml ...?>
    Decl(BytesDecl<'a>),
    /// Processing instruction <?...?>
    PI(BytesText<'a>),
    /// Doctype <!DOCTYPE...>
    DocType(BytesText<'a>),
    /// Eof of file event
    Eof,
}

impl<'a> Deref for BytesStart<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.buf
    }
}

impl<'a> Deref for BytesDecl<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.element
    }
}

impl<'a> Deref for BytesEnd<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.name
    }
}

impl<'a> Deref for BytesText<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        &*self.content
    }
}

impl<'a> Deref for Event<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        match *self {
            Event::Start(ref e) => &*e,
            Event::End(ref e) => &*e,
            Event::Text(ref e) => &*e,
            Event::Empty(ref e) => &*e,
            Event::Decl(ref e) => &*e,
            Event::PI(ref e) => &*e,
            Event::CData(ref e) => &*e,
            Event::Comment(ref e) => &*e,
            Event::DocType(ref e) => &*e,
            Event::Eof => &[],
        }
    }
}

#[cfg(test)]
#[test]
fn local_name() {
    use std::str::from_utf8;
    let xml = r#"
        <foo:bus attr='bar'>foobusbar</foo:bus>
        <foo: attr='bar'>foobusbar</foo:>
        <:foo attr='bar'>foobusbar</:foo>
        <foo:bus:baz attr='bar'>foobusbar</foo:bus:baz>
        "#;
    let mut rdr = Reader::from_str(xml);
    let mut buf = Vec::new();
    let mut parsed_local_names = Vec::new();
    loop {
        match rdr.read_event(&mut buf).expect("unable to read xml event") {
            Event::Start(ref e) => {
                parsed_local_names.push(from_utf8(e.local_name())
                    .expect("unable to build str from local_name")
                    .to_string())
            }
            Event::End(ref e) => {
                parsed_local_names.push(from_utf8(e.local_name())
                    .expect("unable to build str from local_name")
                    .to_string())
            }
            Event::Eof => break,
            _ => {}
        }
    }
    assert_eq!(parsed_local_names[0], "bus".to_string());
    assert_eq!(parsed_local_names[1], "bus".to_string());
    assert_eq!(parsed_local_names[2], "".to_string());
    assert_eq!(parsed_local_names[3], "".to_string());
    assert_eq!(parsed_local_names[4], "foo".to_string());
    assert_eq!(parsed_local_names[5], "foo".to_string());
    assert_eq!(parsed_local_names[6], "bus:baz".to_string());
    assert_eq!(parsed_local_names[7], "bus:baz".to_string());
}
<|endoftext|>


<commit_before>// Enable all clippy lints except for many of the pedantic ones. It's a shame this needs to be copied and pasted across crates, but there doesn't appear to be a way to include inner attributes from a common source.
#![cfg_attr(
  feature = "cargo-clippy",
  deny(
    clippy, default_trait_access, expl_impl_clone_on_copy, if_not_else, needless_continue,
    single_match_else, unseparated_literal_suffix, used_underscore_binding
  )
)]
// It is often more clear to show that nothing is being moved.
#![cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))]
// Default isn't as big a deal as people seem to think it is.
#![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))]
// Arc<Mutex> can be more clear than needing to grok Orderings:
#![cfg_attr(feature = "cargo-clippy", allow(mutex_atomic))]

#[macro_use]
extern crate clap;
extern crate env_logger;
extern crate fs;
extern crate futures;
extern crate hashing;
extern crate process_execution;

use clap::{App, AppSettings, Arg};
use futures::future::Future;
use hashing::{Digest, Fingerprint};
use std::collections::{BTreeMap, BTreeSet};
use std::iter::Iterator;
use std::path::PathBuf;
use std::process::exit;
use std::sync::Arc;
use std::time::Duration;

/// A binary which takes args of format:
///  process_executor --env=FOO=bar --env=SOME=value --input-digest=abc123 --input-digest-length=80
///    -- /path/to/binary --flag --otherflag
/// and runs /path/to/binary --flag --otherflag with FOO and SOME set.
/// It outputs its output/err to stdout/err, and exits with its exit code.
///
/// It does not perform $PATH lookup or shell expansion.
fn main() {
  env_logger::init();

  let args = App::new("process_executor")
    .arg(
      Arg::with_name("work-dir")
        .long("work-dir")
        .takes_value(true)
        .help("Path to workdir"),
    )
    .arg(
      Arg::with_name("local-store-path")
        .long("local-store-path")
        .takes_value(true)
        .help("Path to lmdb directory used for local file storage"),
    )
    .arg(
      Arg::with_name("input-digest")
        .long("input-digest")
        .takes_value(true)
        .required(true)
        .help("Fingerprint (hex string) of the digest to use as the input file tree."),
    )
    .arg(
      Arg::with_name("input-digest-length")
        .long("input-digest-length")
        .takes_value(true)
        .required(true)
        .help("Length of the proto-bytes whose digest to use as the input file tree."),
    )
    .arg(
      Arg::with_name("server")
        .long("server")
        .takes_value(true)
        .help(
          "The host:port of the gRPC server to connect to. Forces remote execution. \
           If unspecified, local execution will be performed.",
        ),
    )
    .arg(
      Arg::with_name("cas-server")
        .long("cas-server")
        .takes_value(true)
        .help("The host:port of the gRPC CAS server to connect to."),
    )
      .arg(
        Arg::with_name("upload-chunk-bytes")
            .help("Number of bytes to include per-chunk when uploading bytes. grpc imposes a hard message-size limit of around 4MB.")
            .takes_value(true)
            .long("chunk-bytes")
            .required(false)
            .default_value("3145728") // 3MB
      )
    .arg(
      Arg::with_name("env")
        .long("env")
        .takes_value(true)
        .multiple(true)
        .help("Environment variables with which the process should be run."),
    )
    .setting(AppSettings::TrailingVarArg)
    .arg(
      Arg::with_name("argv")
        .multiple(true)
        .last(true)
        .required(true),
    )
    .get_matches();

  let argv: Vec<String> = args
    .values_of("argv")
    .unwrap()
    .map(|v| v.to_string())
    .collect();
  let env: BTreeMap<String, String> = match args.values_of("env") {
    Some(values) => values
      .map(|v| {
        let mut parts = v.splitn(2, '=');
        (
          parts.next().unwrap().to_string(),
          parts.next().unwrap_or_default().to_string(),
        )
      })
      .collect(),
    None => BTreeMap::new(),
  };
  let work_dir = args
    .value_of("work-dir")
    .map(PathBuf::from)
    .unwrap_or_else(std::env::temp_dir);
  let local_store_path = args
    .value_of("local-store-path")
    .map(PathBuf::from)
    .unwrap_or_else(fs::Store::default_path);
  let pool = Arc::new(fs::ResettablePool::new("process-executor-".to_owned()));
  let server_arg = args.value_of("server");
  let store = match (server_arg, args.value_of("cas-server")) {
    (Some(_server), Some(cas_server)) => {
      let chunk_size =
        value_t!(args.value_of("upload-chunk-bytes"), usize).expect("Bad upload-chunk-bytes flag");

      fs::Store::with_remote(
        local_store_path,
        pool.clone(),
        cas_server.to_owned(),
        1,
        chunk_size,
        Duration::from_secs(30),
      )
    }
    (None, None) => fs::Store::local_only(local_store_path, pool.clone()),
    _ => panic!("Must specify either both --server and --cas-server or neither."),
  }.expect("Error making store");

  let input_files = {
    let fingerprint = Fingerprint::from_hex_string(args.value_of("input-digest").unwrap())
      .expect("Bad input-digest");
    let length = args
      .value_of("input-digest-length")
      .unwrap()
      .parse::<usize>()
      .expect("input-digest-length must be a non-negative number");
    Digest(fingerprint, length)
  };

  let request = process_execution::ExecuteProcessRequest {
    argv,
    env,
    input_files,
    output_files: BTreeSet::new(),
    output_directories: BTreeSet::new(),
    timeout: Duration::new(15 * 60, 0),
    description: "process_executor".to_string(),
    jdk_home: None,
  };

  let runner: Box<process_execution::CommandRunner> = match server_arg {
    Some(address) => Box::new(process_execution::remote::CommandRunner::new(
      address.to_owned(),
      1,
      store,
    )),
    None => Box::new(process_execution::local::CommandRunner::new(
      store, pool, work_dir, true,
    )),
  };

  let result = runner.run(request).wait().expect("Error executing");

  print!("{}", String::from_utf8(result.stdout.to_vec()).unwrap());
  eprint!("{}", String::from_utf8(result.stderr.to_vec()).unwrap());
  exit(result.exit_code);
}
<commit_msg>Allow process_executor to make a JDK present (#6443)<commit_after>// Enable all clippy lints except for many of the pedantic ones. It's a shame this needs to be copied and pasted across crates, but there doesn't appear to be a way to include inner attributes from a common source.
#![cfg_attr(
  feature = "cargo-clippy",
  deny(
    clippy, default_trait_access, expl_impl_clone_on_copy, if_not_else, needless_continue,
    single_match_else, unseparated_literal_suffix, used_underscore_binding
  )
)]
// It is often more clear to show that nothing is being moved.
#![cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))]
// Default isn't as big a deal as people seem to think it is.
#![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))]
// Arc<Mutex> can be more clear than needing to grok Orderings:
#![cfg_attr(feature = "cargo-clippy", allow(mutex_atomic))]

#[macro_use]
extern crate clap;
extern crate env_logger;
extern crate fs;
extern crate futures;
extern crate hashing;
extern crate process_execution;

use clap::{App, AppSettings, Arg};
use futures::future::Future;
use hashing::{Digest, Fingerprint};
use std::collections::{BTreeMap, BTreeSet};
use std::iter::Iterator;
use std::path::PathBuf;
use std::process::exit;
use std::sync::Arc;
use std::time::Duration;

/// A binary which takes args of format:
///  process_executor --env=FOO=bar --env=SOME=value --input-digest=abc123 --input-digest-length=80
///    -- /path/to/binary --flag --otherflag
/// and runs /path/to/binary --flag --otherflag with FOO and SOME set.
/// It outputs its output/err to stdout/err, and exits with its exit code.
///
/// It does not perform $PATH lookup or shell expansion.
fn main() {
  env_logger::init();

  let args = App::new("process_executor")
    .arg(
      Arg::with_name("work-dir")
        .long("work-dir")
        .takes_value(true)
        .help("Path to workdir"),
    )
    .arg(
      Arg::with_name("local-store-path")
        .long("local-store-path")
        .takes_value(true)
        .help("Path to lmdb directory used for local file storage"),
    )
    .arg(
      Arg::with_name("input-digest")
        .long("input-digest")
        .takes_value(true)
        .required(true)
        .help("Fingerprint (hex string) of the digest to use as the input file tree."),
    )
    .arg(
      Arg::with_name("input-digest-length")
        .long("input-digest-length")
        .takes_value(true)
        .required(true)
        .help("Length of the proto-bytes whose digest to use as the input file tree."),
    )
    .arg(
      Arg::with_name("server")
        .long("server")
        .takes_value(true)
        .help(
          "The host:port of the gRPC server to connect to. Forces remote execution. \
           If unspecified, local execution will be performed.",
        ),
    )
    .arg(
      Arg::with_name("cas-server")
        .long("cas-server")
        .takes_value(true)
        .help("The host:port of the gRPC CAS server to connect to."),
    )
      .arg(
        Arg::with_name("upload-chunk-bytes")
            .help("Number of bytes to include per-chunk when uploading bytes. grpc imposes a hard message-size limit of around 4MB.")
            .takes_value(true)
            .long("chunk-bytes")
            .required(false)
            .default_value("3145728") // 3MB
      )
    .arg(
      Arg::with_name("env")
        .long("env")
        .takes_value(true)
        .multiple(true)
        .help("Environment variables with which the process should be run."),
    )
      .arg(
        Arg::with_name("jdk")
            .long("jdk")
            .takes_value(true)
            .required(false)
            .help("Symlink a JDK from .jdk in the working directory. For local execution, symlinks to the value of this flag. For remote execution, just requests that some JDK is symlinked if this flag has any value. https://github.com/pantsbuild/pants/issues/6416 will make this less weird in the future.")
      )
    .setting(AppSettings::TrailingVarArg)
    .arg(
      Arg::with_name("argv")
        .multiple(true)
        .last(true)
        .required(true),
    )
    .get_matches();

  let argv: Vec<String> = args
    .values_of("argv")
    .unwrap()
    .map(|v| v.to_string())
    .collect();
  let env: BTreeMap<String, String> = match args.values_of("env") {
    Some(values) => values
      .map(|v| {
        let mut parts = v.splitn(2, '=');
        (
          parts.next().unwrap().to_string(),
          parts.next().unwrap_or_default().to_string(),
        )
      })
      .collect(),
    None => BTreeMap::new(),
  };
  let work_dir = args
    .value_of("work-dir")
    .map(PathBuf::from)
    .unwrap_or_else(std::env::temp_dir);
  let local_store_path = args
    .value_of("local-store-path")
    .map(PathBuf::from)
    .unwrap_or_else(fs::Store::default_path);
  let pool = Arc::new(fs::ResettablePool::new("process-executor-".to_owned()));
  let server_arg = args.value_of("server");
  let store = match (server_arg, args.value_of("cas-server")) {
    (Some(_server), Some(cas_server)) => {
      let chunk_size =
        value_t!(args.value_of("upload-chunk-bytes"), usize).expect("Bad upload-chunk-bytes flag");

      fs::Store::with_remote(
        local_store_path,
        pool.clone(),
        cas_server.to_owned(),
        1,
        chunk_size,
        Duration::from_secs(30),
      )
    }
    (None, None) => fs::Store::local_only(local_store_path, pool.clone()),
    _ => panic!("Must specify either both --server and --cas-server or neither."),
  }.expect("Error making store");

  let input_files = {
    let fingerprint = Fingerprint::from_hex_string(args.value_of("input-digest").unwrap())
      .expect("Bad input-digest");
    let length = args
      .value_of("input-digest-length")
      .unwrap()
      .parse::<usize>()
      .expect("input-digest-length must be a non-negative number");
    Digest(fingerprint, length)
  };

  let request = process_execution::ExecuteProcessRequest {
    argv,
    env,
    input_files,
    output_files: BTreeSet::new(),
    output_directories: BTreeSet::new(),
    timeout: Duration::new(15 * 60, 0),
    description: "process_executor".to_string(),
    jdk_home: args.value_of("jdk").map(PathBuf::from),
  };

  let runner: Box<process_execution::CommandRunner> = match server_arg {
    Some(address) => Box::new(process_execution::remote::CommandRunner::new(
      address.to_owned(),
      1,
      store,
    )),
    None => Box::new(process_execution::local::CommandRunner::new(
      store, pool, work_dir, true,
    )),
  };

  let result = runner.run(request).wait().expect("Error executing");

  print!("{}", String::from_utf8(result.stdout.to_vec()).unwrap());
  eprint!("{}", String::from_utf8(result.stderr.to_vec()).unwrap());
  exit(result.exit_code);
}
<|endoftext|>