<commit_before>extern crate rand;
extern crate nalgebra;
extern crate abomonation;

use rand::random;
use abomonation::{Abomonation, encode, decode};
use nalgebra::{
    DMatrix, Matrix3x4, Point3, Translation3, Rotation3, Isometry3, Quaternion,
    IsometryMatrix3, Similarity3, SimilarityMatrix3
};

#[test]
fn abomonate_dmatrix() {
    assert_encode_and_decode(&DMatrix::<f32>::new_random(3, 5));
}

macro_rules! test_abomonation(
    ($($test: ident, $ty: ty);* $(;)*) => {$(
        #[test]
        fn $test() {
            assert_encode_and_decode(&random::<$ty>());
        }
    )*}
);

test_abomonation! {
    abomonate_matrix3x4, Matrix3x4<f32>;
    abomonate_point3, Point3<f32>;
    abomonate_translation3, Translation3<f64>;
    abomonate_rotation3, Rotation3<f64>;
    abomonate_isometry3, Isometry3<f32>;
    abomonate_isometry_matrix3, IsometryMatrix3<f64>;
    abomonate_similarity3, Similarity3<f32>;
    abomonate_similarity_matrix3, SimilarityMatrix3<f32>;
    abomonate_quaternion, Quaternion<f32>;
}

fn assert_encode_and_decode<T: Abomonation + PartialEq>(data: &T) {
    let mut bytes = Vec::new();
    unsafe { encode(data, &mut bytes); }

    if let Some((result, rest)) = unsafe { decode::<T>(&mut bytes) } {
        assert!(result == data);
        assert!(rest.len() == 0, "binary data was not decoded completely");
    }
}
<commit_msg>Test dropping of abomonated data<commit_after>extern crate rand;
extern crate nalgebra;
extern crate abomonation;

use rand::random;
use abomonation::{Abomonation, encode, decode};
use nalgebra::{
    DMatrix, Matrix3x4, Point3, Translation3, Rotation3, Isometry3, Quaternion,
    IsometryMatrix3, Similarity3, SimilarityMatrix3
};

#[test]
fn abomonate_dmatrix() {
    assert_encode_and_decode(DMatrix::<f32>::new_random(3, 5));
}

macro_rules! test_abomonation(
    ($($test: ident, $ty: ty);* $(;)*) => {$(
        #[test]
        fn $test() {
            assert_encode_and_decode(random::<$ty>());
        }
    )*}
);

test_abomonation! {
    abomonate_matrix3x4, Matrix3x4<f32>;
    abomonate_point3, Point3<f32>;
    abomonate_translation3, Translation3<f64>;
    abomonate_rotation3, Rotation3<f64>;
    abomonate_isometry3, Isometry3<f32>;
    abomonate_isometry_matrix3, IsometryMatrix3<f64>;
    abomonate_similarity3, Similarity3<f32>;
    abomonate_similarity_matrix3, SimilarityMatrix3<f32>;
    abomonate_quaternion, Quaternion<f32>;
}

fn assert_encode_and_decode<T: Abomonation + PartialEq + Clone>(original_data: T) {
    use std::mem::drop;

    // Hold on to a clone for later comparison
    let data = original_data.clone();

    // Encode
    let mut bytes = Vec::new();
    unsafe { encode(&original_data, &mut bytes); }

    // Drop the original, so that dangling pointers are revealed by the test
    drop(original_data);

    if let Some((result, rest)) = unsafe { decode::<T>(&mut bytes) } {
        assert!(result == &data);
        assert!(rest.len() == 0, "binary data was not decoded completely");
    }
}
<|endoftext|>


<commit_before>#![feature(slice_concat_ext)]

extern crate compiletest_rs as compiletest;
extern crate tempdir;

use std::slice::SliceConcatExt;
use std::path::{PathBuf, Path};
use std::io::Write;
use tempdir::TempDir;

macro_rules! eprintln {
    ($($arg:tt)*) => {
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), $($arg)*).unwrap();
    }
}

fn miri_path() -> PathBuf {
    if rustc_test_suite().is_some() {
        PathBuf::from(option_env!("MIRI_PATH").unwrap())
    } else {
        PathBuf::from(concat!("target/", env!("PROFILE"), "/miri"))
    }
}

fn rustc_test_suite() -> Option<PathBuf> {
    option_env!("RUSTC_TEST_SUITE").map(PathBuf::from)
}

fn rustc_lib_path() -> PathBuf {
    option_env!("RUSTC_LIB_PATH").unwrap().into()
}

fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, fullmir: bool) {
    eprintln!(
        "## Running compile-fail tests in {} against miri for target {}",
        path,
        target
    );
    let mut config = compiletest::Config::default();
    config.mode = "compile-fail".parse().expect("Invalid mode");
    let build_dir = TempDir::new("miri-tests").unwrap();
    config.build_base = build_dir.path().to_owned();
    config.rustc_path = miri_path();
    let mut flags = Vec::new();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    // if we are building as part of the rustc test suite, we already have fullmir for everything
    if fullmir && rustc_test_suite().is_none() {
        if host != target {
            // skip fullmir on nonhost
            return;
        }
        let sysroot = Path::new(&std::env::var("HOME").unwrap())
            .join(".xargo")
            .join("HOST");
        config.target_rustcflags = Some(format!("--sysroot {}", sysroot.to_str().unwrap()));
        config.src_base = PathBuf::from(path.to_string());
    } else {
        config.target_rustcflags = Some(format!("--sysroot {}", sysroot.to_str().unwrap()));
        config.src_base = PathBuf::from(path.to_string());
    }
    flags.push("-Zmir-emit-validate=1".to_owned());
    config.target_rustcflags = Some(flags.join(" "));
    config.target = target.to_owned();
    compiletest::run_tests(&config);
}

fn run_pass(path: &str) {
    eprintln!("## Running run-pass tests in {} against rustc", path);
    let mut config = compiletest::Config::default();
    config.mode = "run-pass".parse().expect("Invalid mode");
    let build_dir = TempDir::new("miri-tests").unwrap();
    config.build_base = build_dir.path().to_owned();
    config.src_base = PathBuf::from(path);
    if let Some(rustc_path) = rustc_test_suite() {
        config.rustc_path = rustc_path;
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
        config.target_rustcflags = Some(format!("-Dwarnings --sysroot {}", get_sysroot().display()));
    } else {
        config.target_rustcflags = Some("-Dwarnings".to_owned());
    }
    config.host_rustcflags = Some("-Dwarnings".to_string());
    compiletest::run_tests(&config);
}

fn miri_pass(path: &str, target: &str, host: &str, fullmir: bool, opt: bool) {
    let opt_str = if opt { " with optimizations" } else { "" };
    eprintln!(
        "## Running run-pass tests in {} against miri for target {}{}",
        path,
        target,
        opt_str
    );
    let mut config = compiletest::Config::default();
    config.mode = "mir-opt".parse().expect("Invalid mode");
    let build_dir = TempDir::new("miri-tests").unwrap();
    config.build_base = build_dir.path().to_owned();
    config.src_base = PathBuf::from(path);
    config.target = target.to_owned();
    config.host = host.to_owned();
    config.rustc_path = miri_path();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    let mut flags = Vec::new();
    // if we are building as part of the rustc test suite, we already have fullmir for everything
    if fullmir && rustc_test_suite().is_none() {
        if host != target {
            // skip fullmir on nonhost
            return;
        }
        let sysroot = Path::new(&std::env::var("HOME").unwrap())
            .join(".xargo")
            .join("HOST");
        flags.push(format!("--sysroot {}", sysroot.to_str().unwrap()));
    }
    if opt {
        flags.push("-Zmir-opt-level=3".to_owned());
    } else {
        flags.push("-Zmir-opt-level=0".to_owned());
        // For now, only validate without optimizations.  Inlining breaks validation.
        flags.push("-Zmir-emit-validate=1".to_owned());
    }
    config.target_rustcflags = Some(flags.join(" "));
    // don't actually execute the final binary, it might be for other targets and we only care
    // about running miri, not the binary.
    config.runtool = Some("echo \"\" || ".to_owned());
    if target == host {
        std::env::set_var("MIRI_HOST_TARGET", "yes");
    }
    compiletest::run_tests(&config);
    std::env::set_var("MIRI_HOST_TARGET", "");
}

fn is_target_dir<P: Into<PathBuf>>(path: P) -> bool {
    let mut path = path.into();
    path.push("lib");
    path.metadata().map(|m| m.is_dir()).unwrap_or(false)
}

fn for_all_targets<F: FnMut(String)>(sysroot: &Path, mut f: F) {
    let target_dir = sysroot.join("lib").join("rustlib");
    for entry in std::fs::read_dir(target_dir).expect("invalid sysroot") {
        let entry = entry.unwrap();
        if !is_target_dir(entry.path()) {
            continue;
        }
        let target = entry.file_name().into_string().unwrap();
        f(target);
    }
}

fn get_sysroot() -> PathBuf {
    let sysroot = std::env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
        let sysroot = std::process::Command::new("rustc")
            .arg("--print")
            .arg("sysroot")
            .output()
            .expect("rustc not found")
            .stdout;
        String::from_utf8(sysroot).expect("sysroot is not utf8")
    });
    PathBuf::from(sysroot.trim())
}

fn get_host() -> String {
    let host = std::process::Command::new("rustc")
        .arg("-vV")
        .output()
        .expect("rustc not found for -vV")
        .stdout;
    let host = std::str::from_utf8(&host).expect("sysroot is not utf8");
    let host = host.split("\nhost: ").nth(1).expect(
        "no host: part in rustc -vV",
    );
    let host = host.split('\n').next().expect("no \n after host");
    String::from(host)
}

#[test]
fn run_pass_miri() {
    let sysroot = get_sysroot();
    let host = get_host();

    for &opt in [false, true].iter() {
        for_all_targets(&sysroot, |target| {
            miri_pass("tests/run-pass", &target, &host, false, opt);
        });
        miri_pass("tests/run-pass-fullmir", &host, &host, true, opt);
    }
}

#[test]
fn run_pass_rustc() {
    run_pass("tests/run-pass");
    run_pass("tests/run-pass-fullmir");
}

#[test]
fn compile_fail_miri() {
    let sysroot = get_sysroot();
    let host = get_host();

    for_all_targets(&sysroot, |target| {
        compile_fail(&sysroot, "tests/compile-fail", &target, &host, false);
    });
    compile_fail(&sysroot, "tests/compile-fail-fullmir", &host, &host, true);
}
<commit_msg>reorder lets so that build_dir is deleted last<commit_after>#![feature(slice_concat_ext)]

extern crate compiletest_rs as compiletest;
extern crate tempdir;

use std::slice::SliceConcatExt;
use std::path::{PathBuf, Path};
use std::io::Write;
use tempdir::TempDir;

macro_rules! eprintln {
    ($($arg:tt)*) => {
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), $($arg)*).unwrap();
    }
}

fn miri_path() -> PathBuf {
    if rustc_test_suite().is_some() {
        PathBuf::from(option_env!("MIRI_PATH").unwrap())
    } else {
        PathBuf::from(concat!("target/", env!("PROFILE"), "/miri"))
    }
}

fn rustc_test_suite() -> Option<PathBuf> {
    option_env!("RUSTC_TEST_SUITE").map(PathBuf::from)
}

fn rustc_lib_path() -> PathBuf {
    option_env!("RUSTC_LIB_PATH").unwrap().into()
}

fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, fullmir: bool) {
    eprintln!(
        "## Running compile-fail tests in {} against miri for target {}",
        path,
        target
    );
    let build_dir = TempDir::new("miri-tests").unwrap();
    let mut config = compiletest::Config::default();
    config.mode = "compile-fail".parse().expect("Invalid mode");
    config.build_base = build_dir.path().to_owned();
    config.rustc_path = miri_path();
    let mut flags = Vec::new();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    // if we are building as part of the rustc test suite, we already have fullmir for everything
    if fullmir && rustc_test_suite().is_none() {
        if host != target {
            // skip fullmir on nonhost
            return;
        }
        let sysroot = Path::new(&std::env::var("HOME").unwrap())
            .join(".xargo")
            .join("HOST");
        config.target_rustcflags = Some(format!("--sysroot {}", sysroot.to_str().unwrap()));
        config.src_base = PathBuf::from(path.to_string());
    } else {
        config.target_rustcflags = Some(format!("--sysroot {}", sysroot.to_str().unwrap()));
        config.src_base = PathBuf::from(path.to_string());
    }
    flags.push("-Zmir-emit-validate=1".to_owned());
    config.target_rustcflags = Some(flags.join(" "));
    config.target = target.to_owned();
    compiletest::run_tests(&config);
}

fn run_pass(path: &str) {
    eprintln!("## Running run-pass tests in {} against rustc", path);
    let build_dir = TempDir::new("miri-tests").unwrap();
    let mut config = compiletest::Config::default();
    config.mode = "run-pass".parse().expect("Invalid mode");
    config.build_base = build_dir.path().to_owned();
    config.src_base = PathBuf::from(path);
    if let Some(rustc_path) = rustc_test_suite() {
        config.rustc_path = rustc_path;
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
        config.target_rustcflags = Some(format!("-Dwarnings --sysroot {}", get_sysroot().display()));
    } else {
        config.target_rustcflags = Some("-Dwarnings".to_owned());
    }
    config.host_rustcflags = Some("-Dwarnings".to_string());
    compiletest::run_tests(&config);
}

fn miri_pass(path: &str, target: &str, host: &str, fullmir: bool, opt: bool) {
    let opt_str = if opt { " with optimizations" } else { "" };
    eprintln!(
        "## Running run-pass tests in {} against miri for target {}{}",
        path,
        target,
        opt_str
    );
    let build_dir = TempDir::new("miri-tests").unwrap();
    let mut config = compiletest::Config::default();
    config.mode = "mir-opt".parse().expect("Invalid mode");
    config.build_base = build_dir.path().to_owned();
    config.src_base = PathBuf::from(path);
    config.target = target.to_owned();
    config.host = host.to_owned();
    config.rustc_path = miri_path();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    let mut flags = Vec::new();
    // if we are building as part of the rustc test suite, we already have fullmir for everything
    if fullmir && rustc_test_suite().is_none() {
        if host != target {
            // skip fullmir on nonhost
            return;
        }
        let sysroot = Path::new(&std::env::var("HOME").unwrap())
            .join(".xargo")
            .join("HOST");
        flags.push(format!("--sysroot {}", sysroot.to_str().unwrap()));
    }
    if opt {
        flags.push("-Zmir-opt-level=3".to_owned());
    } else {
        flags.push("-Zmir-opt-level=0".to_owned());
        // For now, only validate without optimizations.  Inlining breaks validation.
        flags.push("-Zmir-emit-validate=1".to_owned());
    }
    config.target_rustcflags = Some(flags.join(" "));
    // don't actually execute the final binary, it might be for other targets and we only care
    // about running miri, not the binary.
    config.runtool = Some("echo \"\" || ".to_owned());
    if target == host {
        std::env::set_var("MIRI_HOST_TARGET", "yes");
    }
    compiletest::run_tests(&config);
    std::env::set_var("MIRI_HOST_TARGET", "");
}

fn is_target_dir<P: Into<PathBuf>>(path: P) -> bool {
    let mut path = path.into();
    path.push("lib");
    path.metadata().map(|m| m.is_dir()).unwrap_or(false)
}

fn for_all_targets<F: FnMut(String)>(sysroot: &Path, mut f: F) {
    let target_dir = sysroot.join("lib").join("rustlib");
    for entry in std::fs::read_dir(target_dir).expect("invalid sysroot") {
        let entry = entry.unwrap();
        if !is_target_dir(entry.path()) {
            continue;
        }
        let target = entry.file_name().into_string().unwrap();
        f(target);
    }
}

fn get_sysroot() -> PathBuf {
    let sysroot = std::env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
        let sysroot = std::process::Command::new("rustc")
            .arg("--print")
            .arg("sysroot")
            .output()
            .expect("rustc not found")
            .stdout;
        String::from_utf8(sysroot).expect("sysroot is not utf8")
    });
    PathBuf::from(sysroot.trim())
}

fn get_host() -> String {
    let host = std::process::Command::new("rustc")
        .arg("-vV")
        .output()
        .expect("rustc not found for -vV")
        .stdout;
    let host = std::str::from_utf8(&host).expect("sysroot is not utf8");
    let host = host.split("\nhost: ").nth(1).expect(
        "no host: part in rustc -vV",
    );
    let host = host.split('\n').next().expect("no \n after host");
    String::from(host)
}

#[test]
fn run_pass_miri() {
    let sysroot = get_sysroot();
    let host = get_host();

    for &opt in [false, true].iter() {
        for_all_targets(&sysroot, |target| {
            miri_pass("tests/run-pass", &target, &host, false, opt);
        });
        miri_pass("tests/run-pass-fullmir", &host, &host, true, opt);
    }
}

#[test]
fn run_pass_rustc() {
    run_pass("tests/run-pass");
    run_pass("tests/run-pass-fullmir");
}

#[test]
fn compile_fail_miri() {
    let sysroot = get_sysroot();
    let host = get_host();

    for_all_targets(&sysroot, |target| {
        compile_fail(&sysroot, "tests/compile-fail", &target, &host, false);
    });
    compile_fail(&sysroot, "tests/compile-fail-fullmir", &host, &host, true);
}
<|endoftext|>


<commit_before>extern crate clap;

use clap::{App, Arg, ErrorKind};

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

#[test]
fn positional() {
    let m = App::new("positional")
        .args(&[
            Arg::from_usage("-f, --flag 'some flag'"),
            Arg::with_name("positional")
                .index(1)
            ])
        .get_matches_from(vec!["", "-f", "test"]);
    assert!(m.is_present("positional"));
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("positional").unwrap(), "test");

    let m = App::new("positional")
        .args(&[
            Arg::from_usage("-f, --flag 'some flag'"),
            Arg::with_name("positional")
                .index(1)
            ])
        .get_matches_from(vec!["", "test", "--flag"]);
    assert!(m.is_present("positional"));
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("positional").unwrap(), "test");
}

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

#[test]
fn positional_multiple() {
    let m = App::new("positional_multiple")
        .args(&[
            Arg::from_usage("-f, --flag 'some flag'"),
            Arg::with_name("positional")
                .index(1)
                .multiple(true)
            ])
        .get_matches_from(vec!["", "-f", "test1", "test2", "test3"]);
    assert!(m.is_present("positional"));
    assert!(m.is_present("flag"));
    assert_eq!(&*m.values_of("positional").unwrap().collect::<Vec<_>>(), &["test1", "test2", "test3"]);

    let m = App::new("positional_multiple")
        .args(&[
            Arg::from_usage("-f, --flag 'some flag'"),
            Arg::with_name("positional")
                .index(1)
                .multiple(true)
            ])
        .get_matches_from(vec!["", "test1", "test2", "test3", "--flag"]);
    assert!(m.is_present("positional"));
    assert!(m.is_present("flag"));
    assert_eq!(&*m.values_of("positional").unwrap().collect::<Vec<_>>(), &["test1", "test2", "test3"]);
}

#[test]
fn positional_multiple_2() {
    let result = App::new("positional_multiple")
        .args(&[
            Arg::from_usage("-f, --flag 'some flag'"),
            Arg::with_name("positional")
                .index(1)
            ])
        .get_matches_from_safe(vec!["", "-f", "test1", "test2", "test3"]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::UnknownArgument);
}

#[test]
fn positional_possible_values() {
    let m = App::new("positional_possible_values")
        .args(&[
            Arg::from_usage("-f, --flag 'some flag'"),
            Arg::with_name("positional")
                .index(1)
                .possible_value("test123")
            ])
        .get_matches_from(vec!["", "-f", "test123"]);
    assert!(m.is_present("positional"));
    assert!(m.is_present("flag"));
    assert_eq!(&*m.values_of("positional").unwrap().collect::<Vec<_>>(), &["test123"]);
}

#[test]
fn create_positional() {
    let _ = App::new("test")
                .arg(Arg::with_name("test")
                            .index(1)
                            .help("testing testing"))
                .get_matches_from(vec![""]);
}

#[test]
fn positional_hyphen_does_not_panic() {
    let _ = App::new("test")
        .arg(Arg::with_name("dummy"))
        .get_matches_from(vec!["test", "-"]);
}

#[test]
fn single_positional_usage_string() {
    let m = App::new("test").arg_from_usage("[FILE] 'some file'").get_matches_from(vec!["test"]);
    assert_eq!(m.usage(), "USAGE:\n    test [FILE]");
}

#[test]
fn single_positional_multiple_usage_string() {
    let m = App::new("test").arg_from_usage("[FILE]... 'some file'").get_matches_from(vec!["test"]);
    assert_eq!(m.usage(), "USAGE:\n    test [FILE]...");
}

#[test]
fn multiple_positional_usage_string() {
    let m = App::new("test")
        .arg_from_usage("[FILE] 'some file'")
        .arg_from_usage("[FILES]... 'some file'")
        .get_matches_from(vec!["test"]);
    assert_eq!(m.usage(), "USAGE:\n    test [ARGS]");
}

#[test]
fn multiple_positional_one_required_usage_string() {
    let m = App::new("test")
        .arg_from_usage("<FILE> 'some file'")
        .arg_from_usage("[FILES]... 'some file'")
        .get_matches_from(vec!["test", "file"]);
    assert_eq!(m.usage(), "USAGE:\n    test <FILE> [FILES]...");
}

#[test]
fn single_positional_required_usage_string() {
    let m = App::new("test")
        .arg_from_usage("<FILE> 'some file'")
        .get_matches_from(vec!["test", "file"]);
    assert_eq!(m.usage(), "USAGE:\n    test <FILE>");
}

#[test]
#[should_panic]
fn missing_required() {
    let r = App::new("test")
        .arg_from_usage("[FILE1] 'some file'")
        .arg_from_usage("<FILE2> 'some file'")
        .get_matches_from_safe(vec!["test", "file"]);
    assert!(r.is_err());
    assert_eq!(r.unwrap_err().kind, ErrorKind::MissingRequiredArgument);
}

#[test]
fn missing_required_2() {
    let r = App::new("test")
        .arg_from_usage("<FILE1> 'some file'")
        .arg_from_usage("<FILE2> 'some file'")
        .get_matches_from_safe(vec!["test", "file"]);
    assert!(r.is_err());
    assert_eq!(r.unwrap_err().kind, ErrorKind::MissingRequiredArgument);
}

#[test]
fn last_positional() {
    let r = App::new("test")
        .arg_from_usage("<TARGET> 'some target'")
        .arg_from_usage("[CORPUS] 'some corpus'")
        .arg(Arg::from_usage("[ARGS]... 'some file'").last(true))
        .get_matches_from_safe(vec!["test", "tgt", "--", "arg"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert_eq!(m.values_of("ARGS").unwrap().collect::<Vec<_>>(), &["arg"]);
}

#[test]
fn last_positional_no_double_dash() {
    let r = App::new("test")
        .arg_from_usage("<TARGET> 'some target'")
        .arg_from_usage("[CORPUS] 'some corpus'")
        .arg(Arg::from_usage("[ARGS]... 'some file'").last(true))
        .get_matches_from_safe(vec!["test", "tgt", "crp", "arg"]);
    assert!(r.is_err());
    assert_eq!(r.unwrap_err().kind, ErrorKind::UnknownArgument);
}<commit_msg>tests: adds tests for passing assertions with new Arg::last setting<commit_after>extern crate clap;

use clap::{App, Arg, ErrorKind};

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

#[test]
fn positional() {
    let m = App::new("positional")
        .args(&[
            Arg::from_usage("-f, --flag 'some flag'"),
            Arg::with_name("positional")
                .index(1)
            ])
        .get_matches_from(vec!["", "-f", "test"]);
    assert!(m.is_present("positional"));
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("positional").unwrap(), "test");

    let m = App::new("positional")
        .args(&[
            Arg::from_usage("-f, --flag 'some flag'"),
            Arg::with_name("positional")
                .index(1)
            ])
        .get_matches_from(vec!["", "test", "--flag"]);
    assert!(m.is_present("positional"));
    assert!(m.is_present("flag"));
    assert_eq!(m.value_of("positional").unwrap(), "test");
}

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

#[test]
fn positional_multiple() {
    let m = App::new("positional_multiple")
        .args(&[
            Arg::from_usage("-f, --flag 'some flag'"),
            Arg::with_name("positional")
                .index(1)
                .multiple(true)
            ])
        .get_matches_from(vec!["", "-f", "test1", "test2", "test3"]);
    assert!(m.is_present("positional"));
    assert!(m.is_present("flag"));
    assert_eq!(&*m.values_of("positional").unwrap().collect::<Vec<_>>(), &["test1", "test2", "test3"]);

    let m = App::new("positional_multiple")
        .args(&[
            Arg::from_usage("-f, --flag 'some flag'"),
            Arg::with_name("positional")
                .index(1)
                .multiple(true)
            ])
        .get_matches_from(vec!["", "test1", "test2", "test3", "--flag"]);
    assert!(m.is_present("positional"));
    assert!(m.is_present("flag"));
    assert_eq!(&*m.values_of("positional").unwrap().collect::<Vec<_>>(), &["test1", "test2", "test3"]);
}

#[test]
fn positional_multiple_2() {
    let result = App::new("positional_multiple")
        .args(&[
            Arg::from_usage("-f, --flag 'some flag'"),
            Arg::with_name("positional")
                .index(1)
            ])
        .get_matches_from_safe(vec!["", "-f", "test1", "test2", "test3"]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::UnknownArgument);
}

#[test]
fn positional_possible_values() {
    let m = App::new("positional_possible_values")
        .args(&[
            Arg::from_usage("-f, --flag 'some flag'"),
            Arg::with_name("positional")
                .index(1)
                .possible_value("test123")
            ])
        .get_matches_from(vec!["", "-f", "test123"]);
    assert!(m.is_present("positional"));
    assert!(m.is_present("flag"));
    assert_eq!(&*m.values_of("positional").unwrap().collect::<Vec<_>>(), &["test123"]);
}

#[test]
fn create_positional() {
    let _ = App::new("test")
                .arg(Arg::with_name("test")
                            .index(1)
                            .help("testing testing"))
                .get_matches_from(vec![""]);
}

#[test]
fn positional_hyphen_does_not_panic() {
    let _ = App::new("test")
        .arg(Arg::with_name("dummy"))
        .get_matches_from(vec!["test", "-"]);
}

#[test]
fn single_positional_usage_string() {
    let m = App::new("test").arg_from_usage("[FILE] 'some file'").get_matches_from(vec!["test"]);
    assert_eq!(m.usage(), "USAGE:\n    test [FILE]");
}

#[test]
fn single_positional_multiple_usage_string() {
    let m = App::new("test").arg_from_usage("[FILE]... 'some file'").get_matches_from(vec!["test"]);
    assert_eq!(m.usage(), "USAGE:\n    test [FILE]...");
}

#[test]
fn multiple_positional_usage_string() {
    let m = App::new("test")
        .arg_from_usage("[FILE] 'some file'")
        .arg_from_usage("[FILES]... 'some file'")
        .get_matches_from(vec!["test"]);
    assert_eq!(m.usage(), "USAGE:\n    test [ARGS]");
}

#[test]
fn multiple_positional_one_required_usage_string() {
    let m = App::new("test")
        .arg_from_usage("<FILE> 'some file'")
        .arg_from_usage("[FILES]... 'some file'")
        .get_matches_from(vec!["test", "file"]);
    assert_eq!(m.usage(), "USAGE:\n    test <FILE> [FILES]...");
}

#[test]
fn single_positional_required_usage_string() {
    let m = App::new("test")
        .arg_from_usage("<FILE> 'some file'")
        .get_matches_from(vec!["test", "file"]);
    assert_eq!(m.usage(), "USAGE:\n    test <FILE>");
}

#[test]
#[should_panic]
fn missing_required() {
    let r = App::new("test")
        .arg_from_usage("[FILE1] 'some file'")
        .arg_from_usage("<FILE2> 'some file'")
        .get_matches_from_safe(vec!["test", "file"]);
    assert!(r.is_err());
    assert_eq!(r.unwrap_err().kind, ErrorKind::MissingRequiredArgument);
}

#[test]
fn missing_required_2() {
    let r = App::new("test")
        .arg_from_usage("<FILE1> 'some file'")
        .arg_from_usage("<FILE2> 'some file'")
        .get_matches_from_safe(vec!["test", "file"]);
    assert!(r.is_err());
    assert_eq!(r.unwrap_err().kind, ErrorKind::MissingRequiredArgument);
}

#[test]
fn last_positional() {
    let r = App::new("test")
        .arg_from_usage("<TARGET> 'some target'")
        .arg_from_usage("[CORPUS] 'some corpus'")
        .arg(Arg::from_usage("[ARGS]... 'some file'").last(true))
        .get_matches_from_safe(vec!["test", "tgt", "--", "arg"]);
    assert!(r.is_ok());
    let m = r.unwrap();
    assert_eq!(m.values_of("ARGS").unwrap().collect::<Vec<_>>(), &["arg"]);
}

#[test]
fn last_positional_no_double_dash() {
    let r = App::new("test")
        .arg_from_usage("<TARGET> 'some target'")
        .arg_from_usage("[CORPUS] 'some corpus'")
        .arg(Arg::from_usage("[ARGS]... 'some file'").last(true))
        .get_matches_from_safe(vec!["test", "tgt", "crp", "arg"]);
    assert!(r.is_err());
    assert_eq!(r.unwrap_err().kind, ErrorKind::UnknownArgument);
}

#[test]
fn last_positional_second_to_last_mult() {
    let r = App::new("test")
        .arg_from_usage("<TARGET> 'some target'")
        .arg_from_usage("[CORPUS]... 'some corpus'")
        .arg(Arg::from_usage("[ARGS]... 'some file'").last(true))
        .get_matches_from_safe(vec!["test", "tgt", "crp1", "crp2", "--", "arg"]);
    assert!(r.is_ok(), "{:?}", r.unwrap_err().kind);
}<|endoftext|>


<commit_before>/* 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/. */

use platform::font_list::get_available_families;
use platform::font_list::get_variations_for_family;
use platform::font_list::get_last_resort_font_families;
use platform::font_context::FontContextHandle;

use std::collections::HashMap;
use sync::Arc;
use font_template::{FontTemplate, FontTemplateDescriptor};
use platform::font_template::FontTemplateData;
use servo_net::resource_task::{ResourceTask, load_whole_resource};
use url::Url;

/// A list of font templates that make up a given font family.
struct FontFamily {
    templates: Vec<FontTemplate>,
}

impl FontFamily {
    fn new() -> FontFamily {
        FontFamily {
            templates: vec!(),
        }
    }

    /// Find a font in this family that matches a given desriptor.
    fn find_font_for_style<'a>(&'a mut self, desc: &FontTemplateDescriptor, fctx: &FontContextHandle)
                               -> Option<Arc<FontTemplateData>> {
        // TODO(Issue #189): optimize lookup for
        // regular/bold/italic/bolditalic with fixed offsets and a
        // static decision table for fallback between these values.

        // TODO(Issue #190): if not in the fast path above, do
        // expensive matching of weights, etc.
        for template in self.templates.mut_iter() {
            let maybe_template = template.get_if_matches(fctx, desc);
            if maybe_template.is_some() {
                return maybe_template;
            }
        }

        // If a request is made for a font family that exists,
        // pick the first valid font in the family if we failed
        // to find an exact match for the descriptor.
        for template in self.templates.mut_iter() {
            let maybe_template = template.get();
            if maybe_template.is_some() {
                return maybe_template;
            }
        }

        None
    }

    fn add_template(&mut self, identifier: &str, maybe_data: Option<Vec<u8>>) {
        for template in self.templates.iter() {
            if template.identifier() == identifier {
                return;
            }
        }

        let template = FontTemplate::new(identifier, maybe_data);
        self.templates.push(template);
    }
}

/// Commands that the FontContext sends to the font cache task.
pub enum Command {
    GetFontTemplate(String, FontTemplateDescriptor, Sender<Reply>),
    AddWebFont(Vec<Url>, String, Sender<()>),
    Exit(Sender<()>),
}

/// Reply messages sent from the font cache task to the FontContext caller.
pub enum Reply {
    GetFontTemplateReply(Arc<FontTemplateData>),
}

/// The font cache task itself. It maintains a list of reference counted
/// font templates that are currently in use.
struct FontCache {
    port: Receiver<Command>,
    generic_fonts: HashMap<String, String>,
    local_families: HashMap<String, FontFamily>,
    web_families: HashMap<String, FontFamily>,
    font_context: FontContextHandle,
    resource_task: ResourceTask,
}

impl FontCache {
    fn run(&mut self) {
        loop {
            let msg = self.port.recv();

            match msg {
                GetFontTemplate(family, descriptor, result) => {
                    let maybe_font_template = self.get_font_template(&family, &descriptor);
                    let font_template = match maybe_font_template {
                        Some(font_template) => font_template,
                        None => self.get_last_resort_template(&descriptor),
                    };

                    result.send(GetFontTemplateReply(font_template));
                }
                AddWebFont(urls, family_name, result) => {
                    for url in urls.iter() {
                        let maybe_resource = load_whole_resource(&self.resource_task, url.clone());
                        match maybe_resource {
                            Ok((_, bytes)) => {
                                if !self.web_families.contains_key(&family_name) {
                                    let family = FontFamily::new();
                                    self.web_families.insert(family_name.clone(), family);
                                }
                                let family = self.web_families.get_mut(&family_name);
                                family.add_template(format!("{}", url).as_slice(), Some(bytes));
                            },
                            Err(msg) => {
                                fail!(msg);
                            }
                        }
                    }
                    result.send(());
                }
                Exit(result) => {
                    result.send(());
                    break;
                }
            }
        }
    }

    fn refresh_local_families(&mut self) {
        self.local_families.clear();
        get_available_families(|family_name| {
            if !self.local_families.contains_key(&family_name) {
                let family = FontFamily::new();
                self.local_families.insert(family_name, family);
            }
        });
    }

    fn transform_family(&self, family: &String) -> String {
        match self.generic_fonts.find(family) {
            None => family.to_string(),
            Some(mapped_family) => (*mapped_family).clone()
        }
    }

    fn find_font_in_local_family<'a>(&'a mut self, family_name: &String, desc: &FontTemplateDescriptor)
                                -> Option<Arc<FontTemplateData>> {
        // TODO(Issue #188): look up localized font family names if canonical name not found
        // look up canonical name
        if self.local_families.contains_key(family_name) {
            debug!("FontList: Found font family with name={:s}", family_name.to_string());
            let s = self.local_families.get_mut(family_name);

            if s.templates.len() == 0 {
                get_variations_for_family(family_name.as_slice(), |path| {
                    s.add_template(path.as_slice(), None);
                });
            }

            // TODO(Issue #192: handle generic font families, like 'serif' and 'sans-serif'.
            // if such family exists, try to match style to a font
            let result = s.find_font_for_style(desc, &self.font_context);
            if result.is_some() {
                return result;
            }

            None
        } else {
            debug!("FontList: Couldn't find font family with name={:s}", family_name.to_string());
            None
        }
    }

    fn find_font_in_web_family<'a>(&'a mut self, family_name: &String, desc: &FontTemplateDescriptor)
                                -> Option<Arc<FontTemplateData>> {
        if self.web_families.contains_key(family_name) {
            let family = self.web_families.get_mut(family_name);
            let maybe_font = family.find_font_for_style(desc, &self.font_context);
            maybe_font
        } else {
            None
        }
    }

    fn get_font_template(&mut self, family: &String, desc: &FontTemplateDescriptor) -> Option<Arc<FontTemplateData>> {
        let transformed_family_name = self.transform_family(family);
        let mut maybe_template = self.find_font_in_web_family(&transformed_family_name, desc);
        if maybe_template.is_none() {
            maybe_template = self.find_font_in_local_family(&transformed_family_name, desc);
        }
        maybe_template
    }

    fn get_last_resort_template(&mut self, desc: &FontTemplateDescriptor) -> Arc<FontTemplateData> {
        let last_resort = get_last_resort_font_families();

        for family in last_resort.iter() {
            let maybe_font_in_family = self.find_font_in_local_family(family, desc);
            if maybe_font_in_family.is_some() {
                return maybe_font_in_family.unwrap();
            }
        }

        fail!("Unable to find any fonts that match (do you have fallback fonts installed?)");
    }
}

/// The public interface to the font cache task, used exclusively by
/// the per-thread/task FontContext structures.
#[deriving(Clone)]
pub struct FontCacheTask {
    chan: Sender<Command>,
}

impl FontCacheTask {
    pub fn new(resource_task: ResourceTask) -> FontCacheTask {
        let (chan, port) = channel();

        spawn(proc() {
            // TODO: Allow users to specify these.
            let mut generic_fonts = HashMap::with_capacity(5);
            generic_fonts.insert("serif".to_string(), "Times New Roman".to_string());
            generic_fonts.insert("sans-serif".to_string(), "Arial".to_string());
            generic_fonts.insert("cursive".to_string(), "Apple Chancery".to_string());
            generic_fonts.insert("fantasy".to_string(), "Papyrus".to_string());
            generic_fonts.insert("monospace".to_string(), "Menlo".to_string());

            let mut cache = FontCache {
                port: port,
                generic_fonts: generic_fonts,
                local_families: HashMap::new(),
                web_families: HashMap::new(),
                font_context: FontContextHandle::new(),
                resource_task: resource_task,
            };

            cache.refresh_local_families();
            cache.run();
        });

        FontCacheTask {
            chan: chan,
        }
    }

    pub fn get_font_template(&mut self, family: String, desc: FontTemplateDescriptor)
                                                -> Arc<FontTemplateData> {

        let (response_chan, response_port) = channel();
        self.chan.send(GetFontTemplate(family, desc, response_chan));

        let reply = response_port.recv();

        match reply {
            GetFontTemplateReply(data) => {
                data
            }
        }
    }

    pub fn add_web_font(&mut self, urls: Vec<Url>, family: &str) {
        let (response_chan, response_port) = channel();
        self.chan.send(AddWebFont(urls, family.to_string(), response_chan));
        response_port.recv();
    }

    pub fn exit(&self) {
        let (response_chan, response_port) = channel();
        self.chan.send(Exit(response_chan));
        response_port.recv();
    }
}
<commit_msg>Improve error message for when web fonts fail to load.<commit_after>/* 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/. */

use platform::font_list::get_available_families;
use platform::font_list::get_variations_for_family;
use platform::font_list::get_last_resort_font_families;
use platform::font_context::FontContextHandle;

use std::collections::HashMap;
use sync::Arc;
use font_template::{FontTemplate, FontTemplateDescriptor};
use platform::font_template::FontTemplateData;
use servo_net::resource_task::{ResourceTask, load_whole_resource};
use url::Url;

/// A list of font templates that make up a given font family.
struct FontFamily {
    templates: Vec<FontTemplate>,
}

impl FontFamily {
    fn new() -> FontFamily {
        FontFamily {
            templates: vec!(),
        }
    }

    /// Find a font in this family that matches a given desriptor.
    fn find_font_for_style<'a>(&'a mut self, desc: &FontTemplateDescriptor, fctx: &FontContextHandle)
                               -> Option<Arc<FontTemplateData>> {
        // TODO(Issue #189): optimize lookup for
        // regular/bold/italic/bolditalic with fixed offsets and a
        // static decision table for fallback between these values.

        // TODO(Issue #190): if not in the fast path above, do
        // expensive matching of weights, etc.
        for template in self.templates.mut_iter() {
            let maybe_template = template.get_if_matches(fctx, desc);
            if maybe_template.is_some() {
                return maybe_template;
            }
        }

        // If a request is made for a font family that exists,
        // pick the first valid font in the family if we failed
        // to find an exact match for the descriptor.
        for template in self.templates.mut_iter() {
            let maybe_template = template.get();
            if maybe_template.is_some() {
                return maybe_template;
            }
        }

        None
    }

    fn add_template(&mut self, identifier: &str, maybe_data: Option<Vec<u8>>) {
        for template in self.templates.iter() {
            if template.identifier() == identifier {
                return;
            }
        }

        let template = FontTemplate::new(identifier, maybe_data);
        self.templates.push(template);
    }
}

/// Commands that the FontContext sends to the font cache task.
pub enum Command {
    GetFontTemplate(String, FontTemplateDescriptor, Sender<Reply>),
    AddWebFont(Vec<Url>, String, Sender<()>),
    Exit(Sender<()>),
}

/// Reply messages sent from the font cache task to the FontContext caller.
pub enum Reply {
    GetFontTemplateReply(Arc<FontTemplateData>),
}

/// The font cache task itself. It maintains a list of reference counted
/// font templates that are currently in use.
struct FontCache {
    port: Receiver<Command>,
    generic_fonts: HashMap<String, String>,
    local_families: HashMap<String, FontFamily>,
    web_families: HashMap<String, FontFamily>,
    font_context: FontContextHandle,
    resource_task: ResourceTask,
}

impl FontCache {
    fn run(&mut self) {
        loop {
            let msg = self.port.recv();

            match msg {
                GetFontTemplate(family, descriptor, result) => {
                    let maybe_font_template = self.get_font_template(&family, &descriptor);
                    let font_template = match maybe_font_template {
                        Some(font_template) => font_template,
                        None => self.get_last_resort_template(&descriptor),
                    };

                    result.send(GetFontTemplateReply(font_template));
                }
                AddWebFont(urls, family_name, result) => {
                    for url in urls.iter() {
                        let maybe_resource = load_whole_resource(&self.resource_task, url.clone());
                        match maybe_resource {
                            Ok((_, bytes)) => {
                                if !self.web_families.contains_key(&family_name) {
                                    let family = FontFamily::new();
                                    self.web_families.insert(family_name.clone(), family);
                                }
                                let family = self.web_families.get_mut(&family_name);
                                family.add_template(format!("{}", url).as_slice(), Some(bytes));
                            },
                            Err(msg) => {
                                fail!("{}: url={}", msg, url);
                            }
                        }
                    }
                    result.send(());
                }
                Exit(result) => {
                    result.send(());
                    break;
                }
            }
        }
    }

    fn refresh_local_families(&mut self) {
        self.local_families.clear();
        get_available_families(|family_name| {
            if !self.local_families.contains_key(&family_name) {
                let family = FontFamily::new();
                self.local_families.insert(family_name, family);
            }
        });
    }

    fn transform_family(&self, family: &String) -> String {
        match self.generic_fonts.find(family) {
            None => family.to_string(),
            Some(mapped_family) => (*mapped_family).clone()
        }
    }

    fn find_font_in_local_family<'a>(&'a mut self, family_name: &String, desc: &FontTemplateDescriptor)
                                -> Option<Arc<FontTemplateData>> {
        // TODO(Issue #188): look up localized font family names if canonical name not found
        // look up canonical name
        if self.local_families.contains_key(family_name) {
            debug!("FontList: Found font family with name={:s}", family_name.to_string());
            let s = self.local_families.get_mut(family_name);

            if s.templates.len() == 0 {
                get_variations_for_family(family_name.as_slice(), |path| {
                    s.add_template(path.as_slice(), None);
                });
            }

            // TODO(Issue #192: handle generic font families, like 'serif' and 'sans-serif'.
            // if such family exists, try to match style to a font
            let result = s.find_font_for_style(desc, &self.font_context);
            if result.is_some() {
                return result;
            }

            None
        } else {
            debug!("FontList: Couldn't find font family with name={:s}", family_name.to_string());
            None
        }
    }

    fn find_font_in_web_family<'a>(&'a mut self, family_name: &String, desc: &FontTemplateDescriptor)
                                -> Option<Arc<FontTemplateData>> {
        if self.web_families.contains_key(family_name) {
            let family = self.web_families.get_mut(family_name);
            let maybe_font = family.find_font_for_style(desc, &self.font_context);
            maybe_font
        } else {
            None
        }
    }

    fn get_font_template(&mut self, family: &String, desc: &FontTemplateDescriptor) -> Option<Arc<FontTemplateData>> {
        let transformed_family_name = self.transform_family(family);
        let mut maybe_template = self.find_font_in_web_family(&transformed_family_name, desc);
        if maybe_template.is_none() {
            maybe_template = self.find_font_in_local_family(&transformed_family_name, desc);
        }
        maybe_template
    }

    fn get_last_resort_template(&mut self, desc: &FontTemplateDescriptor) -> Arc<FontTemplateData> {
        let last_resort = get_last_resort_font_families();

        for family in last_resort.iter() {
            let maybe_font_in_family = self.find_font_in_local_family(family, desc);
            if maybe_font_in_family.is_some() {
                return maybe_font_in_family.unwrap();
            }
        }

        fail!("Unable to find any fonts that match (do you have fallback fonts installed?)");
    }
}

/// The public interface to the font cache task, used exclusively by
/// the per-thread/task FontContext structures.
#[deriving(Clone)]
pub struct FontCacheTask {
    chan: Sender<Command>,
}

impl FontCacheTask {
    pub fn new(resource_task: ResourceTask) -> FontCacheTask {
        let (chan, port) = channel();

        spawn(proc() {
            // TODO: Allow users to specify these.
            let mut generic_fonts = HashMap::with_capacity(5);
            generic_fonts.insert("serif".to_string(), "Times New Roman".to_string());
            generic_fonts.insert("sans-serif".to_string(), "Arial".to_string());
            generic_fonts.insert("cursive".to_string(), "Apple Chancery".to_string());
            generic_fonts.insert("fantasy".to_string(), "Papyrus".to_string());
            generic_fonts.insert("monospace".to_string(), "Menlo".to_string());

            let mut cache = FontCache {
                port: port,
                generic_fonts: generic_fonts,
                local_families: HashMap::new(),
                web_families: HashMap::new(),
                font_context: FontContextHandle::new(),
                resource_task: resource_task,
            };

            cache.refresh_local_families();
            cache.run();
        });

        FontCacheTask {
            chan: chan,
        }
    }

    pub fn get_font_template(&mut self, family: String, desc: FontTemplateDescriptor)
                                                -> Arc<FontTemplateData> {

        let (response_chan, response_port) = channel();
        self.chan.send(GetFontTemplate(family, desc, response_chan));

        let reply = response_port.recv();

        match reply {
            GetFontTemplateReply(data) => {
                data
            }
        }
    }

    pub fn add_web_font(&mut self, urls: Vec<Url>, family: &str) {
        let (response_chan, response_port) = channel();
        self.chan.send(AddWebFont(urls, family.to_string(), response_chan));
        response_port.recv();
    }

    pub fn exit(&self) {
        let (response_chan, response_port) = channel();
        self.chan.send(Exit(response_chan));
        response_port.recv();
    }
}
<|endoftext|>


<commit_before>// allow(const_err) to work around a bug in warnings
#[allow(const_err)]
static FOO: fn() = || { assert_ne!(42, 43) };
#[allow(const_err)]
static BAR: fn(i32, i32) = |a, b| { assert_ne!(a, b) };

// use to first make the closure FnOnce() before making it fn()
fn force_once0<R, F: FnOnce() -> R>(f: F) -> F { f }
fn force_once1<T, R, F: FnOnce(T) -> R>(f: F) -> F { f }
fn force_mut0<R, F: FnMut() -> R>(f: F) -> F { f }
fn force_mut1<T, R, F: FnMut(T) -> R>(f: F) -> F { f }

fn main() {
    FOO();
    BAR(44, 45);
    let bar: unsafe fn(i32, i32) = BAR;
    unsafe { bar(46, 47) };
    let boo: &dyn Fn(i32, i32) = &BAR;
    boo(48, 49);

    let f: fn() = ||{};
    f();
    let f = force_once0(||{}) as fn();
    f();
    let f = force_mut0(||{}) as fn();
    f();

    let g: fn(i32) = |i| assert_eq!(i, 2);
    g(2);
    let g = force_once1(|i| assert_eq!(i, 2)) as fn(i32);
    g(2);
    let g = force_mut1(|i| assert_eq!(i, 2)) as fn(i32);
    g(2);
}
<commit_msg>remove some no-longer-needed allow(const_err)<commit_after>static FOO: fn() = || { assert_ne!(42, 43) };
static BAR: fn(i32, i32) = |a, b| { assert_ne!(a, b) };

// use to first make the closure FnOnce() before making it fn()
fn force_once0<R, F: FnOnce() -> R>(f: F) -> F { f }
fn force_once1<T, R, F: FnOnce(T) -> R>(f: F) -> F { f }
fn force_mut0<R, F: FnMut() -> R>(f: F) -> F { f }
fn force_mut1<T, R, F: FnMut(T) -> R>(f: F) -> F { f }

fn main() {
    FOO();
    BAR(44, 45);
    let bar: unsafe fn(i32, i32) = BAR;
    unsafe { bar(46, 47) };
    let boo: &dyn Fn(i32, i32) = &BAR;
    boo(48, 49);

    let f: fn() = ||{};
    f();
    let f = force_once0(||{}) as fn();
    f();
    let f = force_mut0(||{}) as fn();
    f();

    let g: fn(i32) = |i| assert_eq!(i, 2);
    g(2);
    let g = force_once1(|i| assert_eq!(i, 2)) as fn(i32);
    g(2);
    let g = force_mut1(|i| assert_eq!(i, 2)) as fn(i32);
    g(2);
}
<|endoftext|>


<commit_before>// 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/.

use std::path::PathBuf;

use mnt::get_submounts;
use nix::mount::{MntFlags, umount2};

use devicemapper::{DevId, DmOptions};

use super::super::dm::{get_dm, get_dm_init};

mod cleanup_errors {
    use mnt;
    use nix;

    error_chain!{
        foreign_links {
            Mnt(mnt::ParseError);
            Nix(nix::Error);
        }
    }
}

use self::cleanup_errors::{Error, Result};

/// Attempt to remove all device mapper devices which match the stratis naming convention.
/// FIXME: Current implementation complicated by https://bugzilla.redhat.com/show_bug.cgi?id=1506287
fn dm_stratis_devices_remove() -> Result<()> {
    /// One iteration of removing devicemapper devices
    fn one_iteration() -> Result<(bool, Vec<String>)> {
        let mut progress_made = false;
        let mut remain = Vec::new();

        for n in get_dm()
            .list_devices()
            .map_err(|e| {
                let err_msg = "failed while listing DM devices, giving up";
                Error::with_chain(e, err_msg)
            })?
            .iter()
            .map(|d| &d.0)
            .filter(|n| n.to_string().starts_with("stratis-1"))
        {
            match get_dm().device_remove(&DevId::Name(n), &DmOptions::new()) {
                Ok(_) => progress_made = true,
                Err(_) => remain.push(n.to_string()),
            }
        }
        Ok((progress_made, remain))
    }

    /// Do one iteration of removals until progress stops. Return remaining
    /// dm devices.
    fn do_while_progress() -> Result<Vec<String>> {
        let mut result = one_iteration()?;
        while result.0 {
            result = one_iteration()?;
        }
        Ok(result.1)
    }

    || -> Result<()> {
        get_dm_init().map_err(|err| Error::with_chain(err, "Unable to initialize DM"))?;
        do_while_progress().and_then(|remain| {
            if !remain.is_empty() {
                Err(format!("Some Stratis DM devices remaining: {:?}", remain).into())
            } else {
                Ok(())
            }
        })
    }().map_err(|e| e.chain_err(|| "Failed to ensure removal of all Stratis DM devices"))
}

/// Try and un-mount any filesystems that have the name stratis in the mount point, returning
/// immediately on the first one we are unable to unmount.
fn stratis_filesystems_unmount() -> Result<()> {
    || -> Result<()> {
        let mounts = get_submounts(&PathBuf::from("/"))?;
        for m in mounts
            .iter()
            .filter(|m| m.file.to_str().map_or(false, |s| s.contains("stratis")))
        {
            umount2(&m.file, MntFlags::MNT_DETACH)?;
        }
        Ok(())
    }().map_err(|e| e.chain_err(|| "Failed to ensure all Stratis filesystems were unmounted"))
}

/// When a unit test panics we can leave the system in an inconsistent state.  This function
/// tries to clean up by un-mounting any mounted file systems which contain the string
/// "stratis_testing" and then it tries to remove any device mapper tables which are also stratis
/// created.
pub fn clean_up() -> Result<()> {
    stratis_filesystems_unmount()?;
    dm_stratis_devices_remove()?;
    Ok(())
}
<commit_msg>Allow renamed and removed lints, as we do in devicemapper-rs<commit_after>// 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/.

use std::path::PathBuf;

use mnt::get_submounts;
use nix::mount::{MntFlags, umount2};

use devicemapper::{DevId, DmOptions};

use super::super::dm::{get_dm, get_dm_init};

#[allow(renamed_and_removed_lints)]
mod cleanup_errors {
    use mnt;
    use nix;

    error_chain!{
        foreign_links {
            Mnt(mnt::ParseError);
            Nix(nix::Error);
        }
    }
}

use self::cleanup_errors::{Error, Result};

/// Attempt to remove all device mapper devices which match the stratis naming convention.
/// FIXME: Current implementation complicated by https://bugzilla.redhat.com/show_bug.cgi?id=1506287
fn dm_stratis_devices_remove() -> Result<()> {
    /// One iteration of removing devicemapper devices
    fn one_iteration() -> Result<(bool, Vec<String>)> {
        let mut progress_made = false;
        let mut remain = Vec::new();

        for n in get_dm()
            .list_devices()
            .map_err(|e| {
                let err_msg = "failed while listing DM devices, giving up";
                Error::with_chain(e, err_msg)
            })?
            .iter()
            .map(|d| &d.0)
            .filter(|n| n.to_string().starts_with("stratis-1"))
        {
            match get_dm().device_remove(&DevId::Name(n), &DmOptions::new()) {
                Ok(_) => progress_made = true,
                Err(_) => remain.push(n.to_string()),
            }
        }
        Ok((progress_made, remain))
    }

    /// Do one iteration of removals until progress stops. Return remaining
    /// dm devices.
    fn do_while_progress() -> Result<Vec<String>> {
        let mut result = one_iteration()?;
        while result.0 {
            result = one_iteration()?;
        }
        Ok(result.1)
    }

    || -> Result<()> {
        get_dm_init().map_err(|err| Error::with_chain(err, "Unable to initialize DM"))?;
        do_while_progress().and_then(|remain| {
            if !remain.is_empty() {
                Err(format!("Some Stratis DM devices remaining: {:?}", remain).into())
            } else {
                Ok(())
            }
        })
    }().map_err(|e| e.chain_err(|| "Failed to ensure removal of all Stratis DM devices"))
}

/// Try and un-mount any filesystems that have the name stratis in the mount point, returning
/// immediately on the first one we are unable to unmount.
fn stratis_filesystems_unmount() -> Result<()> {
    || -> Result<()> {
        let mounts = get_submounts(&PathBuf::from("/"))?;
        for m in mounts
            .iter()
            .filter(|m| m.file.to_str().map_or(false, |s| s.contains("stratis")))
        {
            umount2(&m.file, MntFlags::MNT_DETACH)?;
        }
        Ok(())
    }().map_err(|e| e.chain_err(|| "Failed to ensure all Stratis filesystems were unmounted"))
}

/// When a unit test panics we can leave the system in an inconsistent state.  This function
/// tries to clean up by un-mounting any mounted file systems which contain the string
/// "stratis_testing" and then it tries to remove any device mapper tables which are also stratis
/// created.
pub fn clean_up() -> Result<()> {
    stratis_filesystems_unmount()?;
    dm_stratis_devices_remove()?;
    Ok(())
}
<|endoftext|>


<commit_before>#[macro_use] extern crate log;
#[macro_use] extern crate matches;
extern crate env_logger;
extern crate argparse;
extern crate quire;
extern crate lithos;
extern crate time;
extern crate scan_dir;
extern crate serde_json;

use std::env;
use std::rc::Rc;
use std::io::{BufReader, BufRead};
use std::fs::{File, remove_dir_all};
use std::path::{PathBuf, Path};
use std::process::exit;
use std::collections::HashSet;
use std::collections::BTreeMap;
use std::collections::VecDeque;

use time::{Tm, Duration, now_utc};
use quire::{parse_config, Options};
use argparse::{ArgumentParser, Parse, ParseOption, StoreTrue, StoreConst};
use argparse::{Print};

use lithos::child_config::ChildConfig;
use lithos::master_config::MasterConfig;
use lithos::MAX_CONFIG_LOGS;
use lithos::sandbox_config::SandboxConfig;

#[derive(Clone, Copy, Debug)]
enum Action {
    Used,
    Unused,
    DeleteUnused,
}

enum Candidate {
    Config(Tm, BTreeMap<String, ChildConfig>),
    BrokenLine(Rc<PathBuf>, usize),
}

struct LogFiles<'a> {
    base: &'a Path,
    name: String,
    idx: u32,
}

fn main() {

    if env::var("RUST_LOG").is_err() {
        env::set_var("RUST_LOG", "warn");
    }
    env_logger::init().unwrap();
    let mut config_file = PathBuf::from("/etc/lithos/master.yaml");
    let mut verbose = false;
    let mut ver_min = 0;
    let mut ver_max = 1000;
    let mut action = Action::Used;
    let mut days = None::<u32>;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Show used/unused images and clean if needed");
        ap.refer(&mut config_file)
          .add_option(&["-C", "--config"], Parse,
            "Name of the global configuration file \
             (default /etc/lithos/master.yaml)")
          .metavar("FILE");
        ap.refer(&mut days)
          .add_option(&["-D", "--history-days"], ParseOption,
            r"Keep images that used no more than DAYS ago.
              There is no reasonable default, so you should specify this
              argument or --versions-min to get sane behavior.")
          .metavar("DAYS");
        ap.refer(&mut ver_min)
          .add_option(&["--vmin", "--versions-min"], Parse,
            r"Keep minimum NUM versions (even if they are older than DAYS).
              Default is 0 which means keep only current version.")
          .metavar("NUM");
        ap.refer(&mut ver_max)
          .add_option(&["--vmax", "--versions-max"], Parse,
            r"Keep maximum NUM versions
              (even if need to delete images more recent than DAYS).
              Default is 1000.")
          .metavar("NUM");
        ap.refer(&mut verbose)
          .add_option(&["-v", "--verbose"], StoreTrue,
            "Verbose output");
        ap.refer(&mut action)
          .add_option(&["--used"], StoreConst(Action::Used),
            "Show used images")
          .add_option(&["--unused"], StoreConst(Action::Unused),
            "Show unused images")
          .add_option(&["--delete-unused"], StoreConst(Action::DeleteUnused),
            "Delete unused images");
        ap.add_option(&["--version"],
            Print(env!("CARGO_PKG_VERSION").to_string()),
            "Show version of the lithos");
        ap.parse_args_or_exit();
    }
    let master: MasterConfig = match parse_config(&config_file,
        &MasterConfig::validator(), &Options::default()) {
        Ok(cfg) => cfg,
        Err(e) => {
            error!("Can't parse config: {}", e);
            exit(1);
        }
    };
    let tm = days.map(|days| now_utc() - Duration::days(days as i64));
    let (used, dirs) = match find_used_images(&master, &config_file,
        tm, ver_min, ver_max)
    {
        Ok((used, dirs)) => (used, dirs),
        Err(e) => {
            error!("Error finding out used images: {}", e);
            exit(1);
        }
    };
    match action {
        Action::Used => {
            for i in used {
                println!("{:?}", i);
            }
        }
        Action::Unused => {
            let unused = find_unused(&used, &dirs)
                .map_err(|e| {
                    error!("Error finding unused images: {:?}", e);
                    exit(2);
                })
                .unwrap();
            for i in unused {
                println!("{:?}", i);
            }
        }
        Action::DeleteUnused => {
            let unused = find_unused(&used, &dirs)
                .map_err(|e| {
                    error!("Error finding unused images: {:?}", e);
                    exit(2);
                })
                .unwrap();
            for i in unused {
                if verbose {
                    println!("Deleting {:?}", i);
                }
                remove_dir_all(&i)
                    .map_err(|e| error!("Error removing {:?}: {}", i, e)).ok();
            }
        }
    }
}

impl<'a> LogFiles<'a> {
    fn new(path: &'a Path, name: &'a str) -> LogFiles<'a> {
        LogFiles {
            base: path,
            name: format!("{}.log", name),
            idx: 0,
        }
    }
}

impl<'a> Iterator for LogFiles<'a> {
    type Item = PathBuf;
    fn next(&mut self) -> Option<PathBuf> {
        if self.idx == 0 {
            let result = self.base.join(&self.name);
            self.idx += 1;
            debug!("Trying {:?}: {}", result, result.exists());
            if result.exists() {
                return Some(result);
            }
        }
        while self.idx < MAX_CONFIG_LOGS {
            let result = self.base.join(format!("{}.{}", self.name, self.idx));
            self.idx += 1;
            debug!("Trying {:?}: {}", result, result.exists());
            if result.exists() {
                return Some(result);
            }
        }
        return None;
    }
}

fn find_unused(used: &HashSet<PathBuf>, dirs: &HashSet<PathBuf>)
    -> Result<Vec<PathBuf>, scan_dir::Error>
{
    let mut unused = Vec::new();
    for dir in dirs.iter() {
        if !dir.exists() {
            warn!("Directory {:?} does not exists", dir);
            continue;
        }
        try!(scan_dir::ScanDir::dirs().skip_symlinks(true).read(dir, |iter| {
            for (entry, _) in iter {
                let path = entry.path().to_path_buf();
                if !used.contains(&path) {
                    unused.push(path);
                }
            }
        }));
    }
    Ok(unused)
}

impl PartialEq for Candidate {
    fn eq(&self, other: &Candidate) -> bool {
        use Candidate::*;
        match (self, other) {
            (&BrokenLine(..), &BrokenLine(..)) => true,
            (&Config(_, ref a), &Config(_, ref b)) => a == b,
            _ => false,
        }
    }
}

fn parse_line(line: &str) -> Result<(Tm, BTreeMap<String, ChildConfig>), ()> {
    let mut iter = line.splitn(2, " ");
    let date = iter.next().ok_or(())?;
    let config = iter.next().ok_or(())?;
    Ok((
        time::strptime(date, "%Y-%m-%dT%H:%M:%SZ").map_err(|_| ())?,
        serde_json::from_str(config).map_err(|_| ())?,
    ))
}

fn find_used_images(master: &MasterConfig, master_file: &Path,
    min_time: Option<Tm>, ver_min: u32, ver_max: u32)
    -> Result<(HashSet<PathBuf>, HashSet<PathBuf>), String>
{
    let config_dir = master_file.parent().unwrap().join(&master.sandboxes_dir);
    let mut images = HashSet::new();
    let mut image_dirs = HashSet::new();
    let childval = ChildConfig::mapping_validator();
    scan_dir::ScanDir::files().read(&config_dir, |iter| {
        let yamls = iter.filter(|&(_, ref name)| name.ends_with(".yaml"));
        for (entry, sandbox_fname) in yamls {
            let sandbox_name = &sandbox_fname[..sandbox_fname.len()-5];  // strip .yaml
            let sandbox_config: SandboxConfig = parse_config(&entry.path(),
                &SandboxConfig::validator(), &Options::default())
                .map_err(|e| e.to_string())?;
            image_dirs.insert(sandbox_config.image_dir.clone());

            let cfg = master_file.parent().unwrap()
                .join(&master.processes_dir)
                .join(sandbox_config.config_file.as_ref().unwrap_or(
                    &PathBuf::from(&(sandbox_name.to_string() + ".yaml"))));
            if cfg.exists() {
                let all_children: BTreeMap<String, ChildConfig>;
                all_children =
                    parse_config(&cfg, &childval, &Options::default())
                    .map_err(|e| format!("Can't read child config {:?}: {}",
                                         sandbox_config.config_file, e))?;
                for child in all_children.values() {
                    // Current are always added
                    images.insert(sandbox_config.image_dir.join(&child.image));
                }
            } else {
                info!("No current processes for {}", sandbox_name);
            }

            let mut configs = VecDeque::<Candidate>::new();
            for logname in LogFiles::new(&master.config_log_dir, &sandbox_name) {
                let logname = Rc::new(logname);
                let log = try!(File::open(&*logname)
                    .map_err(|e| format!("Can't read log file {:?}: {}", logname, e)));
                for (line_no, line) in BufReader::new(log).lines().enumerate() {
                    let line = try!(line
                        .map_err(|e| format!("Readline error: {}", e)));
                    if line.trim() == "" {
                        continue; // Probably last line
                    }
                    let cand = match parse_line(&line) {
                        Ok((tm, cfg)) => Candidate::Config(tm, cfg),
                        Err(()) => {
                            warn!("Broken line {}: {}",
                                logname.display(), line_no);
                            Candidate::BrokenLine(logname.clone(), line_no)
                        }
                    };
                    if configs.back() == Some(&cand) {
                        continue;
                    }
                    configs.push_back(cand);
                    if configs.len() as u32 >= ver_max {
                        configs.pop_front();
                    }
                }
                if configs.len() as u32 > ver_max ||
                    matches!((configs.front(), min_time),
                        (Some(&Candidate::Config(tm, _)), Some(min_time))
                                              if tm > min_time)
                {
                    break;
                }
            }
            min_time.map(|min_time| {
                while configs.len() > ver_min as usize {
                    match configs.front() {
                        Some(&Candidate::Config(tm, _)) if tm > min_time
                        => {
                            break;
                        }
                        _ => {}
                    }
                    configs.pop_front();
                }
            });
            for cand in configs.iter() {
                match *cand {
                    Candidate::Config(_, ref cfg) => {
                        for child in cfg.values() {
                            // Current are always added
                            images.insert(
                                sandbox_config.image_dir
                                .join(&child.image));
                        }
                    }
                    Candidate::BrokenLine(..) => {
                        return Err(format!("Can't reliably find out \
                            used images for sandbox {}", sandbox_name));
                    }
                }
            }
        }
        Ok(())
    }).map_err(|e| format!("Read dir error: {}", e))??;
    Ok((images, image_dirs))
}
<commit_msg>lithos_clean: Limit effect of errors in config file.<commit_after>#[macro_use] extern crate log;
#[macro_use] extern crate matches;
extern crate env_logger;
extern crate argparse;
extern crate quire;
extern crate lithos;
extern crate time;
extern crate scan_dir;
extern crate serde_json;

use std::env;
use std::rc::Rc;
use std::io::{BufReader, BufRead};
use std::fs::{File, remove_dir_all};
use std::path::{PathBuf, Path};
use std::process::exit;
use std::collections::HashSet;
use std::collections::BTreeMap;
use std::collections::VecDeque;

use time::{Tm, Duration, now_utc};
use quire::{parse_config, Options};
use argparse::{ArgumentParser, Parse, ParseOption, StoreTrue, StoreConst};
use argparse::{Print};

use lithos::child_config::ChildConfig;
use lithos::master_config::MasterConfig;
use lithos::MAX_CONFIG_LOGS;
use lithos::sandbox_config::SandboxConfig;

#[derive(Clone, Copy, Debug)]
enum Action {
    Used,
    Unused,
    DeleteUnused,
}

enum Candidate {
    Config(Tm, BTreeMap<String, ChildConfig>),
    BrokenLine(Rc<PathBuf>, usize),
}

struct LogFiles<'a> {
    base: &'a Path,
    name: String,
    idx: u32,
}

fn main() {

    if env::var("RUST_LOG").is_err() {
        env::set_var("RUST_LOG", "warn");
    }
    env_logger::init().unwrap();
    let mut config_file = PathBuf::from("/etc/lithos/master.yaml");
    let mut verbose = false;
    let mut ver_min = 0;
    let mut ver_max = 1000;
    let mut action = Action::Used;
    let mut days = None::<u32>;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Show used/unused images and clean if needed");
        ap.refer(&mut config_file)
          .add_option(&["-C", "--config"], Parse,
            "Name of the global configuration file \
             (default /etc/lithos/master.yaml)")
          .metavar("FILE");
        ap.refer(&mut days)
          .add_option(&["-D", "--history-days"], ParseOption,
            r"Keep images that used no more than DAYS ago.
              There is no reasonable default, so you should specify this
              argument or --versions-min to get sane behavior.")
          .metavar("DAYS");
        ap.refer(&mut ver_min)
          .add_option(&["--vmin", "--versions-min"], Parse,
            r"Keep minimum NUM versions (even if they are older than DAYS).
              Default is 0 which means keep only current version.")
          .metavar("NUM");
        ap.refer(&mut ver_max)
          .add_option(&["--vmax", "--versions-max"], Parse,
            r"Keep maximum NUM versions
              (even if need to delete images more recent than DAYS).
              Default is 1000.")
          .metavar("NUM");
        ap.refer(&mut verbose)
          .add_option(&["-v", "--verbose"], StoreTrue,
            "Verbose output");
        ap.refer(&mut action)
          .add_option(&["--used"], StoreConst(Action::Used),
            "Show used images")
          .add_option(&["--unused"], StoreConst(Action::Unused),
            "Show unused images")
          .add_option(&["--delete-unused"], StoreConst(Action::DeleteUnused),
            "Delete unused images");
        ap.add_option(&["--version"],
            Print(env!("CARGO_PKG_VERSION").to_string()),
            "Show version of the lithos");
        ap.parse_args_or_exit();
    }
    let master: MasterConfig = match parse_config(&config_file,
        &MasterConfig::validator(), &Options::default()) {
        Ok(cfg) => cfg,
        Err(e) => {
            error!("Can't parse config: {}", e);
            exit(1);
        }
    };
    let tm = days.map(|days| now_utc() - Duration::days(days as i64));
    let (used, dirs) = match find_used_images(&master, &config_file,
        tm, ver_min, ver_max)
    {
        Ok((used, dirs)) => (used, dirs),
        Err(e) => {
            error!("Error finding out used images: {}", e);
            exit(1);
        }
    };
    match action {
        Action::Used => {
            for i in used {
                println!("{:?}", i);
            }
        }
        Action::Unused => {
            let unused = find_unused(&used, &dirs)
                .map_err(|e| {
                    error!("Error finding unused images: {:?}", e);
                    exit(2);
                })
                .unwrap();
            for i in unused {
                println!("{:?}", i);
            }
        }
        Action::DeleteUnused => {
            let unused = find_unused(&used, &dirs)
                .map_err(|e| {
                    error!("Error finding unused images: {:?}", e);
                    exit(2);
                })
                .unwrap();
            for i in unused {
                if verbose {
                    println!("Deleting {:?}", i);
                }
                remove_dir_all(&i)
                    .map_err(|e| error!("Error removing {:?}: {}", i, e)).ok();
            }
        }
    }
}

impl<'a> LogFiles<'a> {
    fn new(path: &'a Path, name: &'a str) -> LogFiles<'a> {
        LogFiles {
            base: path,
            name: format!("{}.log", name),
            idx: 0,
        }
    }
}

impl<'a> Iterator for LogFiles<'a> {
    type Item = PathBuf;
    fn next(&mut self) -> Option<PathBuf> {
        if self.idx == 0 {
            let result = self.base.join(&self.name);
            self.idx += 1;
            debug!("Trying {:?}: {}", result, result.exists());
            if result.exists() {
                return Some(result);
            }
        }
        while self.idx < MAX_CONFIG_LOGS {
            let result = self.base.join(format!("{}.{}", self.name, self.idx));
            self.idx += 1;
            debug!("Trying {:?}: {}", result, result.exists());
            if result.exists() {
                return Some(result);
            }
        }
        return None;
    }
}

fn find_unused(used: &HashSet<PathBuf>, dirs: &HashSet<PathBuf>)
    -> Result<Vec<PathBuf>, scan_dir::Error>
{
    let mut unused = Vec::new();
    for dir in dirs.iter() {
        if !dir.exists() {
            warn!("Directory {:?} does not exists", dir);
            continue;
        }
        try!(scan_dir::ScanDir::dirs().skip_symlinks(true).read(dir, |iter| {
            for (entry, _) in iter {
                let path = entry.path().to_path_buf();
                if !used.contains(&path) {
                    unused.push(path);
                }
            }
        }));
    }
    Ok(unused)
}

impl PartialEq for Candidate {
    fn eq(&self, other: &Candidate) -> bool {
        use Candidate::*;
        match (self, other) {
            (&BrokenLine(..), &BrokenLine(..)) => true,
            (&Config(_, ref a), &Config(_, ref b)) => a == b,
            _ => false,
        }
    }
}

fn parse_line(line: &str) -> Result<(Tm, BTreeMap<String, ChildConfig>), ()> {
    let mut iter = line.splitn(2, " ");
    let date = iter.next().ok_or(())?;
    let config = iter.next().ok_or(())?;
    Ok((
        time::strptime(date, "%Y-%m-%dT%H:%M:%SZ").map_err(|_| ())?,
        serde_json::from_str(config).map_err(|_| ())?,
    ))
}

fn find_used_images(master: &MasterConfig, master_file: &Path,
    min_time: Option<Tm>, ver_min: u32, ver_max: u32)
    -> Result<(HashSet<PathBuf>, HashSet<PathBuf>), String>
{
    let config_dir = master_file.parent().unwrap().join(&master.sandboxes_dir);
    let mut bad_dirs = HashSet::new();
    let mut images = HashSet::new();
    let mut image_dirs = HashSet::new();
    let childval = ChildConfig::mapping_validator();
    scan_dir::ScanDir::files().read(&config_dir, |iter| -> Result<(), String> {
        let yamls = iter.filter(|&(_, ref name)| name.ends_with(".yaml"));
        for (entry, sandbox_fname) in yamls {
            let sandbox_name = &sandbox_fname[..sandbox_fname.len()-5];  // strip .yaml
            let sandbox_config: SandboxConfig = parse_config(&entry.path(),
                &SandboxConfig::validator(), &Options::default())
                .map_err(|e| e.to_string())?;
            image_dirs.insert(sandbox_config.image_dir.clone());

            let cfg = master_file.parent().unwrap()
                .join(&master.processes_dir)
                .join(sandbox_config.config_file.as_ref().unwrap_or(
                    &PathBuf::from(&(sandbox_name.to_string() + ".yaml"))));
            if cfg.exists() {
                let all_children: BTreeMap<String, ChildConfig>;
                all_children =
                    parse_config(&cfg, &childval, &Options::default())
                    .map_err(|e| format!("Can't read child config {:?}: {}",
                                         sandbox_config.config_file, e))?;
                for child in all_children.values() {
                    // Current are always added
                    images.insert(sandbox_config.image_dir.join(&child.image));
                }
            } else {
                info!("No current processes for {}", sandbox_name);
            }

            let mut configs = VecDeque::<Candidate>::new();
            for logname in LogFiles::new(&master.config_log_dir, &sandbox_name) {
                let logname = Rc::new(logname);
                let log = try!(File::open(&*logname)
                    .map_err(|e| format!("Can't read log file {:?}: {}", logname, e)));
                for (line_no, line) in BufReader::new(log).lines().enumerate() {
                    let line = try!(line
                        .map_err(|e| format!("Readline error: {}", e)));
                    if line.trim() == "" {
                        continue; // Probably last line
                    }
                    let cand = match parse_line(&line) {
                        Ok((tm, cfg)) => Candidate::Config(tm, cfg),
                        Err(()) => {
                            warn!("Broken line {}: {}",
                                logname.display(), line_no);
                            Candidate::BrokenLine(logname.clone(), line_no)
                        }
                    };
                    if configs.back() == Some(&cand) {
                        continue;
                    }
                    configs.push_back(cand);
                    if configs.len() as u32 >= ver_max {
                        configs.pop_front();
                    }
                }
                if configs.len() as u32 > ver_max ||
                    matches!((configs.front(), min_time),
                        (Some(&Candidate::Config(tm, _)), Some(min_time))
                                              if tm > min_time)
                {
                    break;
                }
            }
            min_time.map(|min_time| {
                while configs.len() > ver_min as usize {
                    match configs.front() {
                        Some(&Candidate::Config(tm, _)) if tm > min_time
                        => {
                            break;
                        }
                        _ => {}
                    }
                    configs.pop_front();
                }
            });
            for cand in configs.iter() {
                match *cand {
                    Candidate::Config(_, ref cfg) => {
                        for child in cfg.values() {
                            // Current are always added
                            images.insert(
                                sandbox_config.image_dir
                                .join(&child.image));
                        }
                    }
                    Candidate::BrokenLine(..) => {
                        bad_dirs.insert(sandbox_config.image_dir.clone());
                    }
                }
            }
        }
        Ok(())
    }).map_err(|e| format!("Read dir error: {}", e))??;

    for dir in &bad_dirs {
        error!("Can't reliably find out used images in the directory {:?}",
            dir);
        image_dirs.remove(dir);
    }
    Ok((images, image_dirs))
}
<|endoftext|>


<commit_before>use std::io;
use std::str::FromStr;

struct VM {
	stack: Vec<Operator>,
}

impl VM {
	fn pop <'a> (&'a mut self, ret: &mut Operator) {
		*ret = self.stack.pop().expect("gg reinstall");
	}

	fn push <'a> (&'a mut self, op: Operator) {
		self.stack.push(op);
	}
}

enum Operator {
	//stack
	Dup,
	Drop,
	Swap,
	Over,
	Rot,

	//debug
	Print,

	//math
	Add,
	Subtract,
	Multiply,
	Divide,
	Mod,
	And,
	Or,
	Xor,
	Lshift,
	Rshift,
	Abs,
	Max,
	Min,
	Invert,
	Negate,

	//custom
	Define,

	//values and custom calls
	Value(i32),
	Other,
}

fn parse(s: &str) -> Operator {
	match s {
		//stack operations:
		"dup" => Operator::Dup,
		"drop" => Operator::Drop,
		"swap" => Operator::Swap,
		"over" => Operator::Over,
		"rot" => Operator::Rot,

		//debug operations:
		"." => Operator::Print,

		//math operations:
		"+" => Operator::Add,
		"-" => Operator::Subtract,
		"*" => Operator::Multiply,
		"/" => Operator::Divide,
		"mod" => Operator::Mod,
		"and" => Operator::And,
		"or" => Operator::Or,
		"xor" => Operator::Xor,
		"lshift" => Operator::Lshift,
		"<<" => Operator::Lshift,
		"rshift" => Operator::Rshift,
		">>" => Operator::Rshift,
		"abs" => Operator::Abs,
		"max" => Operator::Max,
		"min" => Operator::Min,
		"invert" => Operator::Invert,
		"negate" => Operator::Negate,

		//custom definitions
		":" => Operator::Define,

		//value or custom operator
		x => {
			let y: Option<i32> = FromStr::from_str(x).ok();
			match y{
				Some(i) => Operator::Value(i),
				None => Operator::Other,
			}
		},
	}
}

fn process(s: String) {
	for o in s.split(" "){
		let b = parse(o.trim());
		println!("{}",match b {
			Operator::Add => "addition!",
			Operator::Subtract => "subtraction!",
			Operator::Value(_) => "value!",
			_ => "other!",
		});
	}
}

fn main() {

	let mut x = io::stdin();
	let mut vm = VM {stack: vec![Operator::Print]};

	loop{
		let mut y: String = "".to_string();
		let _ = x.read_line(&mut y);

	    process(y);
	}
}
<commit_msg>eval loop for vm<commit_after>use std::io;
use std::str::FromStr;

struct VM {
	stack: Vec<Operator>,
}

impl VM {
	fn pop <'a> (&'a mut self, ret: &mut Operator) {
		*ret = self.stack.pop().expect("gg reinstall");
	}

	fn push <'a> (&'a mut self, op: Operator) {
		self.stack.push(op);
	}

	fn eval <'a> (&'a mut self, op: Operator) {

	}
}

enum Operator {
	//stack
	Dup,
	Drop,
	Swap,
	Over,
	Rot,

	//debug
	Print,

	//math
	Add,
	Subtract,
	Multiply,
	Divide,
	Mod,
	And,
	Or,
	Xor,
	Lshift,
	Rshift,
	Abs,
	Max,
	Min,
	Invert,
	Negate,

	//custom
	Define,

	//values and custom calls
	Value(i32),
	Other,
}

fn parse(s: &str) -> Operator {
	match s {
		//stack operations:
		"dup" => Operator::Dup,
		"drop" => Operator::Drop,
		"swap" => Operator::Swap,
		"over" => Operator::Over,
		"rot" => Operator::Rot,

		//debug operations:
		"." => Operator::Print,

		//math operations:
		"+" => Operator::Add,
		"-" => Operator::Subtract,
		"*" => Operator::Multiply,
		"/" => Operator::Divide,
		"mod" => Operator::Mod,
		"and" => Operator::And,
		"or" => Operator::Or,
		"xor" => Operator::Xor,
		"lshift" => Operator::Lshift,
		"<<" => Operator::Lshift,
		"rshift" => Operator::Rshift,
		">>" => Operator::Rshift,
		"abs" => Operator::Abs,
		"max" => Operator::Max,
		"min" => Operator::Min,
		"invert" => Operator::Invert,
		"negate" => Operator::Negate,

		//custom definitions
		":" => Operator::Define,

		//value or custom operator
		x => {
			let y: Option<i32> = FromStr::from_str(x).ok();
			match y{
				Some(i) => Operator::Value(i),
				None => Operator::Other,
			}
		},
	}
}

fn main() {
	let mut x = io::stdin();
	let mut vm = VM {stack: vec![Operator::Print]};

	loop{
		let mut line: String = "".to_string();
		let _ = x.read_line(&mut line);

		for o in line.split(" "){
			println!("evaling: {}", o.trim());
			let b = parse(o.trim());
			vm.eval(b);
		}
	}
}
<|endoftext|>